>From 82e97c1f83dbd145073b14b56399a195a6d1e87b Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Thu, 20 Feb 2014 16:13:45 -0500
Subject: [PATCH 199/203] Add methods to create nss certdb and import cert

Also changed arguments so that all args and returns from
CryptoUtil are unencoded.
---
 base/common/python/pki/__init__.py   |  3 +-
 base/common/python/pki/cryptoutil.py | 53 +++++++++++++++++++++++++++++++-----
 base/common/python/pki/kraclient.py  | 20 ++++++++++----
 base/kra/functional/drmtest.py       | 18 +++++++++---
 4 files changed, 77 insertions(+), 17 deletions(-)

diff --git a/base/common/python/pki/__init__.py b/base/common/python/pki/__init__.py
index 190d1f10fb9f7a3bc16ac8768ecc6a9cd8cbb8ff..7e4e2771c72ccd5a329ed44ea237ac0fe592dadd 100644
--- a/base/common/python/pki/__init__.py
+++ b/base/common/python/pki/__init__.py
@@ -32,7 +32,8 @@ BASE_DIR = '/var/lib'
 LOG_DIR = '/var/log/pki'
 
 PACKAGE_VERSION = SHARE_DIR + '/VERSION'
-
+CERT_HEADER = "-----BEGIN NEW CERTIFICATE REQUEST-----"
+CERT_FOOTER = "-----END NEW CERTIFICATE REQUEST-----"
 
 def read_text(message,
     options=None, default=None, delimiter=':',
diff --git a/base/common/python/pki/cryptoutil.py b/base/common/python/pki/cryptoutil.py
index e4a01e323a4d75036baf3d521b84c7582c43f1b4..c48c6ca0f818a62d328809de7557cef5992fa94c 100644
--- a/base/common/python/pki/cryptoutil.py
+++ b/base/common/python/pki/cryptoutil.py
@@ -22,8 +22,12 @@
 Module containing crypto classes.
 '''
 import abc
-import base64
+import exceptions
 import nss.nss as nss
+import os
+import shutil
+import subprocess
+import tempfile
 
 
 class CryptoUtil(object):
@@ -74,8 +78,29 @@ class NSSCryptoUtil(CryptoUtil):
     '''
     Class that defines NSS implementation of CryptoUtil.
     Requires an NSS database to have been set up and initialized.
+
+    Note that all inputs and outputs are unencoded.
     '''
 
+    @staticmethod
+    def setup_database(db_dir, password, over_write=False):
+        ''' Create an NSS database '''
+        if os.path.exists(db_dir):
+            if not over_write:
+                raise exceptions.ValueError(
+                        "Directory already exists and over_write is false")
+            if os.path.isdir(db_dir):
+                shutil.rmtree(db_dir)
+            else:
+                os.remove(db_dir)
+        os.makedirs(db_dir)
+
+        with tempfile.NamedTemporaryFile() as pwd_file:
+            pwd_file.write(password)
+            pwd_file.flush()
+            command = ['certutil', '-N', '-d', db_dir, '-f', pwd_file.name]
+            subprocess.check_call(command)
+
     def __init__(self, certdb_dir, certdb_password):
         ''' Initialize nss and nss related parameters
 
@@ -85,9 +110,23 @@ class NSSCryptoUtil(CryptoUtil):
         CryptoUtil.__init__(self)
         self.certdb_dir = certdb_dir
         self.certdb_password = certdb_password
-        nss.nss_init(certdb_dir)
         self.nonce_iv = "e4:bb:3b:d3:c3:71:2e:58"
 
+    def initialize_db(self):
+        nss.nss_init(self.certdb_dir)
+
+    def import_cert(self, cert_nick, cert, trust):
+        ''' Import a certificate into the nss database
+        '''
+        # certutil -A -d db_dir -n cert_nick -t trust -i cert_file -a
+        with tempfile.NamedTemporaryFile() as cert_file:
+            cert_file.write(cert)
+            cert_file.flush()
+            command = ['certutil', '-A', '-d', self.certdb_dir,
+                       '-n', cert_nick, '-t', trust,
+                       '-i', cert_file.name]
+            subprocess.check_call(command)
+
     @staticmethod
     def setup_contexts(mechanism, sym_key, nonce_iv):
         ''' Set up contexts to do wrapping/unwrapping by symmetric keys. '''
@@ -139,19 +178,19 @@ class NSSCryptoUtil(CryptoUtil):
 
     def symmetric_unwrap(self, data, wrapping_key, mechanism=nss.CKM_DES3_CBC_PAD, nonce_iv=None):
         '''
-        :param data:           Data to be unwrapped (base 64 encoded)
+        :param data:           Data to be unwrapped
         :param wrapping_key    Symmetric key to unwrap data
-        :param nonce_iv              Base 64 encoded iv data
+        :param nonce_iv        iv data
 
         Unwrap (decrypt) data using the supplied symmetric key
         '''
         if nonce_iv == None:
             nonce_iv = self.nonce_iv
         else:
-            nonce_iv = nss.data_to_hex(base64.decodestring(nonce_iv))
+            nonce_iv = nss.data_to_hex(nonce_iv)
 
         _encoding_ctx, decoding_ctx = self.setup_contexts(mechanism, wrapping_key, nonce_iv)
-        unwrapped_data = decoding_ctx.cipher_op(base64.decodestring(data)) \
+        unwrapped_data = decoding_ctx.cipher_op(data) \
             + decoding_ctx.digest_final()
         return unwrapped_data
 
@@ -164,7 +203,7 @@ class NSSCryptoUtil(CryptoUtil):
         Wrap (encrypt) data using the supplied asymmetric key
         '''
         public_key = wrapping_cert.subject_public_key_info.public_key
-        return base64.b64encode(nss.pub_wrap_sym_key(mechanism, public_key, data))
+        return nss.pub_wrap_sym_key(mechanism, public_key, data)
 
     def get_cert(self, cert_nick):
         '''
diff --git a/base/common/python/pki/kraclient.py b/base/common/python/pki/kraclient.py
index 18707b744f0838f02ed19195543591baccf14f42..227298c850c759020c23b87d556c64bd6ce65a8e 100644
--- a/base/common/python/pki/kraclient.py
+++ b/base/common/python/pki/kraclient.py
@@ -25,6 +25,7 @@ to interact with the DRM to expose the functionality of the KeyClient and
 KeyRequestResouce REST APIs.
 '''
 
+import base64
 import pki.key as key
 
 from pki.systemcert import SystemCertClient
@@ -34,7 +35,7 @@ class KRAClient(object):
     Client class that models interactions with a KRA using the Key and KeyRequest REST APIs.
     '''
 
-    def __init__(self, connection, crypto, transport_cert_nick):
+    def __init__(self, connection, crypto, transport_cert_nick=None):
         ''' Constructor
 
         :param connection - PKIConnection object with DRM connection info.
@@ -49,7 +50,14 @@ class KRAClient(object):
         self.keys = key.KeyClient(connection)
         self.system_certs = SystemCertClient(connection)
         self.crypto = crypto
-        self.transport_cert = crypto.get_cert(transport_cert_nick)
+        if transport_cert_nick != None:
+            self.transport_cert = crypto.get_cert(transport_cert_nick)
+        else:
+            self.transport_cert = None
+
+    def set_transport_cert(self, transport_cert_nick):
+        ''' Set the transport certificate for crypto operations '''
+        self.transport_cert = self.crypto.get_cert(transport_cert_nick)
 
     def retrieve_key(self, key_id, trans_wrapped_session_key=None):
         ''' Retrieve a secret (passphrase or symmetric key) from the DRM.
@@ -90,12 +98,14 @@ class KRAClient(object):
         self.keys.approve_request(request_id)
 
         key_data = self.keys.request_key_retrieval(key_id, request_id,
-                        trans_wrapped_session_key=trans_wrapped_session_key)
+                        trans_wrapped_session_key=base64.encodestring(trans_wrapped_session_key))
         if key_provided:
             return key_data, None
 
-        unwrapped_key = self.crypto.symmetric_unwrap(key_data.wrappedPrivateData, session_key,
-                                                     nonce_iv=key_data.nonceData)
+        unwrapped_key = self.crypto.symmetric_unwrap(
+                                base64.decodestring(key_data.wrappedPrivateData),
+                                session_key,
+                                nonce_iv=base64.decodestring(key_data.nonceData))
         return key_data, unwrapped_key
 
     def retrieve_key_by_passphrase(self, key_id, passphrase=None,
diff --git a/base/kra/functional/drmtest.py b/base/kra/functional/drmtest.py
index 43c297d4a0bd1c09c0cb85cec578b26a3b4d2b1d..f3352b687e20dffd0466879c108002a583ab4330 100644
--- a/base/kra/functional/drmtest.py
+++ b/base/kra/functional/drmtest.py
@@ -65,12 +65,20 @@ def main():
     ''' test code execution '''
     connection = PKIConnection('https', 'localhost', '8443', 'kra')
     connection.set_authentication_cert('/tmp/temp4.pem')
-    crypto = cryptoutil.NSSCryptoUtil("/tmp/drmtest/certdb", "redhat123")
-    kraclient = KRAClient(connection, crypto, "kra transport cert")
+    certdb_dir = "/tmp/drmtest-certdb"
+    certdb_password = "redhat123"
+    transport_nick = "kra transport cert"
+    cryptoutil.NSSCryptoUtil.setup_database(certdb_dir, certdb_password, over_write=True)
+    crypto = cryptoutil.NSSCryptoUtil(certdb_dir, certdb_password)
+    kraclient = KRAClient(connection, crypto)
 
-    # Test 1: Get Transport Cert
+    # Test 1: Get transport certificate and import it into the NSS database
     transport_cert = kraclient.system_certs.get_transport_cert()
     print transport_cert
+    tcert = transport_cert[len(pki.CERT_HEADER):len(transport_cert) -len(pki.CERT_FOOTER)]
+    crypto.import_cert(transport_nick, base64.decodestring(tcert), "u,u,u")
+    crypto.initialize_db()
+    kraclient.set_transport_cert(transport_nick)
 
     # Test 2: Get key request info
     print "Now getting key request"
@@ -111,7 +119,9 @@ def main():
     wrapped_session_key = crypto.asymmetric_wrap(session_key, kraclient.transport_cert)
     key_data, _unwrapped_key = kraclient.retrieve_key(key_id, trans_wrapped_session_key=wrapped_session_key)
     print_key_data(key_data)
-    unwrapped_key = crypto.symmetric_unwrap(key_data.wrappedPrivateData, session_key, nonce_iv=key_data.nonceData)
+    unwrapped_key = crypto.symmetric_unwrap(base64.decodestring(key_data.wrappedPrivateData),
+                                            session_key,
+                                            nonce_iv=base64.decodestring(key_data.nonceData))
     key1 = base64.encodestring(unwrapped_key)
 
     # Test 7: Recover key without providing trans_wrapped_session_key
-- 
1.8.4.2

