security: Export/Import Identity from/into KeyChain

Change-Id: I757f51f1408cf08b9fb1b1927834889fd29c0231
diff --git a/src/security/sec-tpm.cpp b/src/security/sec-tpm.cpp
new file mode 100644
index 0000000..13b04a8
--- /dev/null
+++ b/src/security/sec-tpm.cpp
@@ -0,0 +1,287 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Yingdi Yu <yingdi@cs.ucla.edu>
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "sec-tpm.hpp"
+
+#include <cryptopp/rsa.h>
+#include <cryptopp/files.h>
+#include <cryptopp/base64.h>
+#include <cryptopp/hex.h>
+#include <cryptopp/osrng.h>
+#include <cryptopp/sha.h>
+#include <cryptopp/pssr.h>
+#include <cryptopp/modes.h>
+#include <cryptopp/pwdbased.h>
+#include <cryptopp/sha.h>
+#include <cryptopp/des.h>
+
+using namespace std;
+
+namespace ndn {
+
+ConstBufferPtr
+SecTpm::exportPrivateKeyPkcs8FromTpm(const Name& keyName, bool inTerminal, const string& passwordStr)
+{
+  uint8_t salt[8] = {0};
+  uint8_t iv[8] = {0};
+    
+  try{
+    using namespace CryptoPP;
+
+    // check password
+    string password;
+    if(passwordStr.empty())
+      if(!inTerminal)
+        return shared_ptr<Buffer>();
+      else
+        {
+          int count = 0;
+          while(!getPassWord(password, keyName.toUri()))
+            {
+              cerr << "Password mismatch!" << endl;
+              count++;
+              if(count > 3)
+                return shared_ptr<Buffer>();
+            }
+        }
+    else
+      password = passwordStr;
+
+    // derive key
+    if(!generateRandomBlock(salt, 8))
+      return shared_ptr<Buffer>();
+    
+    if(!generateRandomBlock(iv, 8))
+      return shared_ptr<Buffer>();
+    
+    uint32_t iterationCount = 2048;
+
+    PKCS5_PBKDF2_HMAC<SHA1> keyGenerator;
+    size_t derivedLen = 24; //For DES-EDE3-CBC-PAD
+    byte derived[24] = {0};
+    byte purpose = 0;
+
+    keyGenerator.DeriveKey(derived, derivedLen, 
+                           purpose, 
+                           reinterpret_cast<const byte*>(password.c_str()), password.size(), 
+                           salt, 8, 
+                           iterationCount); 
+    
+    memset(const_cast<char*>(password.c_str()), 0, password.size());
+
+    //encrypt
+    CBC_Mode< DES_EDE3 >::Encryption e;
+    e.SetKeyWithIV(derived, derivedLen, iv);
+    
+    string encrypted;
+    OBufferStream encryptedOs;
+    ConstBufferPtr pkcs1PrivateKey = exportPrivateKeyPkcs1FromTpm(keyName);
+    StringSource stringSource(pkcs1PrivateKey->buf(), pkcs1PrivateKey->size(), true, 
+			      new StreamTransformationFilter(e, new FileSink(encryptedOs)));
+
+    //encode
+    OID pbes2Id("1.2.840.113549.1.5.13");
+    OID pbkdf2Id("1.2.840.113549.1.5.12");
+    OID pbes2encsId("1.2.840.113549.3.7");
+
+    OBufferStream pkcs8Os;
+    FileSink sink(pkcs8Os);
+  
+    // EncryptedPrivateKeyInfo ::= SEQUENCE {
+    //   encryptionAlgorithm  EncryptionAlgorithmIdentifier,
+    //   encryptedData        OCTET STRING }
+    DERSequenceEncoder encryptedPrivateKeyInfo(sink);
+    {
+      // EncryptionAlgorithmIdentifier ::= SEQUENCE {
+      //   algorithm      OBJECT IDENTIFIER {{PBES2-id}},
+      //   parameters     SEQUENCE {{PBES2-params}} }
+      DERSequenceEncoder encryptionAlgorithm(encryptedPrivateKeyInfo);
+      {
+        pbes2Id.encode(encryptionAlgorithm);
+        // PBES2-params ::= SEQUENCE {
+        //   keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+        //   encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
+        DERSequenceEncoder pbes2Params(encryptionAlgorithm);
+        {
+          // AlgorithmIdentifier ::= SEQUENCE {
+          //   algorithm      OBJECT IDENTIFIER {{PBKDF2-id}},
+          //   parameters     SEQUENCE {{PBKDF2-params}} }
+          DERSequenceEncoder pbes2KDFs(pbes2Params);
+          {
+             pbkdf2Id.encode(pbes2KDFs);
+            // AlgorithmIdentifier ::= SEQUENCE {
+            //   salt           OCTET STRING,
+            //   iterationCount INTEGER (1..MAX),
+            //   keyLength      INTEGER (1..MAX) OPTIONAL,
+            //   prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 }
+            DERSequenceEncoder pbkdf2Params(pbes2KDFs);
+            {
+              DEREncodeOctetString(pbkdf2Params, salt, 8);
+              DEREncodeUnsigned<uint32_t>(pbkdf2Params, iterationCount, INTEGER);
+            }
+            pbkdf2Params.MessageEnd();
+          }
+          pbes2KDFs.MessageEnd();
+        
+          // AlgorithmIdentifier ::= SEQUENCE {
+          //   algorithm   OBJECT IDENTIFIER {{DES-EDE3-CBC-PAD}},
+          //   parameters  OCTET STRING} {{iv}} }
+          DERSequenceEncoder pbes2Encs(pbes2Params);
+          {
+            pbes2encsId.encode(pbes2Encs);
+            DEREncodeOctetString(pbes2Encs, iv, 8);
+          }
+          pbes2Encs.MessageEnd();
+        }
+        pbes2Params.MessageEnd();
+      }
+      encryptionAlgorithm.MessageEnd();
+
+      DEREncodeOctetString(encryptedPrivateKeyInfo, encryptedOs.buf()->buf(), encryptedOs.buf()->size());
+    }
+    encryptedPrivateKeyInfo.MessageEnd();
+
+    return pkcs8Os.buf();
+  }catch(...){
+    return shared_ptr<Buffer>();
+  }
+}
+
+bool
+SecTpm::importPrivateKeyPkcs8IntoTpm(const Name& keyName, const uint8_t* buf, size_t size, bool inTerminal, const string& passwordStr)
+{
+  try{
+    using namespace CryptoPP;
+    
+    OID pbes2Id;
+    OID pbkdf2Id;
+    SecByteBlock saltBlock;
+    uint32_t iterationCount;
+    OID pbes2encsId;
+    SecByteBlock ivBlock;
+    SecByteBlock encryptedDataBlock;
+    
+    //decode some decoding processes are not necessary for now, because we assume only one encryption scheme.
+    StringSource source(buf, size, true);
+    
+    // EncryptedPrivateKeyInfo ::= SEQUENCE {
+    //   encryptionAlgorithm  EncryptionAlgorithmIdentifier,
+    //   encryptedData        OCTET STRING }
+    BERSequenceDecoder encryptedPrivateKeyInfo(source);
+    {
+      // EncryptionAlgorithmIdentifier ::= SEQUENCE {
+      //   algorithm      OBJECT IDENTIFIER {{PBES2-id}},
+      //   parameters     SEQUENCE {{PBES2-params}} }
+      BERSequenceDecoder encryptionAlgorithm(encryptedPrivateKeyInfo);
+      {
+        pbes2Id.decode(encryptionAlgorithm);
+        // PBES2-params ::= SEQUENCE {
+        //   keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+        //   encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
+        BERSequenceDecoder pbes2Params(encryptionAlgorithm);
+        {
+          // AlgorithmIdentifier ::= SEQUENCE {
+          //   algorithm      OBJECT IDENTIFIER {{PBKDF2-id}},
+          //   parameters     SEQUENCE {{PBKDF2-params}} }
+          BERSequenceDecoder pbes2KDFs(pbes2Params);
+          {
+            pbkdf2Id.decode(pbes2KDFs);
+            // AlgorithmIdentifier ::= SEQUENCE {
+            //   salt           OCTET STRING,
+            //   iterationCount INTEGER (1..MAX),
+            //   keyLength      INTEGER (1..MAX) OPTIONAL,
+            //   prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 }
+            BERSequenceDecoder pbkdf2Params(pbes2KDFs);
+            {
+              BERDecodeOctetString(pbkdf2Params, saltBlock);
+              BERDecodeUnsigned<uint32_t>(pbkdf2Params, iterationCount, INTEGER);
+            }
+            pbkdf2Params.MessageEnd();
+          }
+          pbes2KDFs.MessageEnd();
+        
+          // AlgorithmIdentifier ::= SEQUENCE {
+          //   algorithm   OBJECT IDENTIFIER {{DES-EDE3-CBC-PAD}},
+          //   parameters  OCTET STRING} {{iv}} }
+          BERSequenceDecoder pbes2Encs(pbes2Params);
+          {
+            pbes2encsId.decode(pbes2Encs);
+            BERDecodeOctetString(pbes2Encs, ivBlock);
+          }
+          pbes2Encs.MessageEnd();
+        }
+        pbes2Params.MessageEnd();
+      }
+      encryptionAlgorithm.MessageEnd();
+
+      BERDecodeOctetString(encryptedPrivateKeyInfo, encryptedDataBlock);
+    }
+    encryptedPrivateKeyInfo.MessageEnd();
+
+  
+    PKCS5_PBKDF2_HMAC<SHA1> keyGenerator;
+    size_t derivedLen = 24; //For DES-EDE3-CBC-PAD
+    byte derived[24] = {0};
+    byte purpose = 0;
+
+    string password;
+    if(passwordStr.empty())
+      if(inTerminal)
+        {
+          char* pw = getpass("Password for the private key: ");
+          if (!pw)
+            return false;
+          password = pw;
+          memset(pw, 0, strlen(pw));
+        }
+      else
+        return false;
+    else
+      password = passwordStr;
+      
+    keyGenerator.DeriveKey(derived, derivedLen, 
+                           purpose, 
+                           reinterpret_cast<const byte*>(password.c_str()), password.size(), 
+                           saltBlock.BytePtr(), saltBlock.size(), 
+                           iterationCount);
+
+    memset(const_cast<char*>(password.c_str()), 0, password.size());
+        
+    //decrypt
+    CBC_Mode< DES_EDE3 >::Decryption d;
+    d.SetKeyWithIV(derived, derivedLen, ivBlock.BytePtr());
+    
+    OBufferStream privateKeyOs;
+    StringSource encryptedSource(encryptedDataBlock.BytePtr(), encryptedDataBlock.size(), true, 
+                                 new StreamTransformationFilter(d,  new FileSink(privateKeyOs)));
+
+    if(!importPrivateKeyPkcs1IntoTpm(keyName, privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()))
+      return false;
+    
+    //derive public key
+    RSA::PrivateKey privateKey;
+    privateKey.Load(StringStore(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()).Ref());
+
+    RSAFunction publicKey(privateKey);
+
+    OBufferStream publicKeyOs;
+    FileSink publicKeySink(publicKeyOs);
+    publicKey.DEREncode(publicKeySink);
+    publicKeySink.MessageEnd();
+
+    if(!importPublicKeyPkcs1IntoTpm(keyName, publicKeyOs.buf()->buf(), publicKeyOs.buf()->size()))
+      return false;
+
+    return true;
+  }catch(std::runtime_error& e){
+    cerr << e.what() << endl;
+    return false;
+  }
+}
+
+
+}//ndn