security: Enable exporting/importing ECDSA key

Change-Id: I58f722337f26cad4eb6a3c83d883814efc4ed8df
Refs: #1660
diff --git a/src/encoding/oid.cpp b/src/encoding/oid.cpp
index 511ff6c..1e623a5 100644
--- a/src/encoding/oid.cpp
+++ b/src/encoding/oid.cpp
@@ -28,11 +28,13 @@
 
 #include <sstream>
 
-using namespace std;
-using namespace CryptoPP;
-
 namespace ndn {
 
+using std::string;
+using std::vector;
+
+static const int OID_MAGIC_NUMBER = 40;
+
 OID::OID(const char* oid)
 {
   construct(oid);
@@ -64,9 +66,10 @@
   }
 }
 
-string OID::toString() const
+string
+OID::toString() const
 {
-  ostringstream convert;
+  std::ostringstream convert;
 
   for (vector<int>::const_iterator it = m_oid.begin(); it != m_oid.end(); ++it) {
     if (it != m_oid.begin())
@@ -83,28 +86,29 @@
   vector<int>::const_iterator i = m_oid.begin();
   vector<int>::const_iterator j = oid.m_oid.begin();
 
-  for (; i != m_oid.end () && j != oid.m_oid.end (); i++, j++) {
+  for (; i != m_oid.end() && j != oid.m_oid.end(); i++, j++) {
     if (*i != *j)
       return false;
   }
 
-  if (i == m_oid.end () && j == oid.m_oid.end ())
-    return true;
-  else
-    return false;
+  return (i == m_oid.end() && j == oid.m_oid.end()); // keep parenthesis for readability.
 }
 
 inline void
-EncodeValue(BufferedTransformation& bt, word32 v)
+encodeValue(CryptoPP::BufferedTransformation& bt, CryptoPP::word32 v)
 {
-  for (unsigned int i = RoundUpToMultipleOf(STDMAX(7U,BitPrecision(v)), 7U) - 7; i != 0; i -= 7)
-    bt.Put((byte)(0x80 | ((v >> i) & 0x7f)));
-  bt.Put((byte)(v & 0x7f));
+  using namespace CryptoPP;
+
+  for (unsigned int i = RoundUpToMultipleOf(STDMAX(7U, BitPrecision(v)), 7U) - 7; i != 0; i -= 7)
+    bt.Put(static_cast<byte>(0x80 | ((v >> i) & 0x7f)));
+  bt.Put(static_cast<byte>(v & 0x7f));
 }
 
 inline size_t
-DecodeValue(BufferedTransformation& bt, word32& v)
+decodeValue(CryptoPP::BufferedTransformation& bt, CryptoPP::word32& v)
 {
+  using namespace CryptoPP;
+
   v = 0;
   size_t i = 0;
   while (true)
@@ -113,11 +117,11 @@
       if (!bt.Get(b))
         BERDecodeError();
       i++;
-      if (v >> (8*sizeof(v) - 7)) // v about to overflow
+      if (v >> (8 * sizeof(v) - 7)) // v about to overflow
         BERDecodeError();
       v <<= 7;
       v += b & 0x7f;
-      if (!(b & 0x80))
+      if ((b & 0x80) == 0)
         return i;
     }
 }
@@ -125,12 +129,14 @@
 void
 OID::encode(CryptoPP::BufferedTransformation& out) const
 {
+  using namespace CryptoPP;
+
   BOOST_ASSERT(m_oid.size() >= 2);
 
   ByteQueue temp;
-  temp.Put(byte(m_oid[0] * 40 + m_oid[1]));
+  temp.Put(byte(m_oid[0] * OID_MAGIC_NUMBER + m_oid[1]));
   for (size_t i = 2; i < m_oid.size(); i++)
-    EncodeValue(temp, m_oid[i]);
+    encodeValue(temp, m_oid[i]);
 
   out.Put(OBJECT_IDENTIFIER);
   DERLengthEncode(out, temp.CurrentSize());
@@ -140,6 +146,8 @@
 void
 OID::decode(CryptoPP::BufferedTransformation& in)
 {
+  using namespace CryptoPP;
+
   byte b;
   if (!in.Get(b) || b != OBJECT_IDENTIFIER)
     BERDecodeError();
@@ -153,13 +161,13 @@
 
   length--;
   m_oid.resize(2);
-  m_oid[0] = b / 40;
-  m_oid[1] = b % 40;
+  m_oid[0] = b / OID_MAGIC_NUMBER;
+  m_oid[1] = b % OID_MAGIC_NUMBER;
 
   while (length > 0)
     {
       word32 v;
-      size_t valueLen = DecodeValue(in, v);
+      size_t valueLen = decodeValue(in, v);
       if (valueLen > length)
         BERDecodeError();
       m_oid.push_back(v);
@@ -167,4 +175,11 @@
     }
 }
 
+namespace oid {
+const OID RSA("1.2.840.113549.1.1.1");
+const OID ECDSA("1.2.840.10045.2.1");
+
+const OID ATTRIBUTE_NAME("2.5.4.41");
+}
+
 } // namespace ndn
diff --git a/src/encoding/oid.hpp b/src/encoding/oid.hpp
index 7da47a6..d5f99ca 100644
--- a/src/encoding/oid.hpp
+++ b/src/encoding/oid.hpp
@@ -35,14 +35,17 @@
 class OID
 {
 public:
-  OID ()
+  OID()
   {
   }
 
+  explicit
   OID(const char* oid);
 
+  explicit
   OID(const std::string& oid);
 
+  explicit
   OID(const std::vector<int>& oid)
     : m_oid(oid)
   {
@@ -55,19 +58,22 @@
   }
 
   void
-  setIntegerList(const std::vector<int>& value){
+  setIntegerList(const std::vector<int>& value)
+  {
     m_oid = value;
   }
 
   std::string
   toString() const;
 
-  bool operator == (const OID& oid) const
+  bool
+  operator==(const OID& oid) const
   {
     return equal(oid);
   }
 
-  bool operator != (const OID& oid) const
+  bool
+  operator!=(const OID& oid) const
   {
     return !equal(oid);
   }
@@ -90,6 +96,15 @@
   std::vector<int> m_oid;
 };
 
+namespace oid {
+//crypto algorithm
+extern const OID RSA;
+extern const OID ECDSA;
+
+//certificate entries
+extern const OID ATTRIBUTE_NAME;
+}
+
 }
 
 #endif // NDN_ENCODING_OID_HPP
diff --git a/src/security/key-chain.cpp b/src/security/key-chain.cpp
index 9c5e000..5118468 100644
--- a/src/security/key-chain.cpp
+++ b/src/security/key-chain.cpp
@@ -274,8 +274,7 @@
 
   if (subjectDescription.empty())
     {
-      // OID 2.5.4.41 is the oid of subject name.
-      CertificateSubjectDescription subjectName("2.5.4.41", keyName.getPrefix(-1).toUri());
+      CertificateSubjectDescription subjectName(oid::ATTRIBUTE_NAME, keyName.getPrefix(-1).toUri());
       certificate->addSubjectDescription(subjectName);
     }
   else
@@ -333,9 +332,10 @@
 
   certificate->setName(certificateName);
   certificate->setNotBefore(time::system_clock::now());
-  certificate->setNotAfter(time::system_clock::now() + time::days(7300)/* ~20 years*/);
+  certificate->setNotAfter(time::system_clock::now() + time::days(7300)); // ~20 years
   certificate->setPublicKeyInfo(*pubKey);
-  certificate->addSubjectDescription(CertificateSubjectDescription("2.5.4.41", keyName.toUri()));
+  certificate->addSubjectDescription(CertificateSubjectDescription(oid::ATTRIBUTE_NAME,
+                                                                   keyName.toUri()));
   certificate->encode();
 
   selfSign(*certificate);
diff --git a/src/security/public-key.cpp b/src/security/public-key.cpp
index bed8f81..801fe6c 100644
--- a/src/security/public-key.cpp
+++ b/src/security/public-key.cpp
@@ -29,9 +29,6 @@
 
 namespace ndn {
 
-static OID RSA_OID("1.2.840.113549.1.1.1");
-static OID ECDSA_OID("1.2.840.10045.2.1");
-
 PublicKey::PublicKey()
   : m_type(KEY_TYPE_NULL)
 {
@@ -91,9 +88,9 @@
           OID algorithm;
           algorithm.decode(algorithmInfo);
 
-          if (algorithm == RSA_OID)
+          if (algorithm == oid::RSA)
             m_type = KEY_TYPE_RSA;
-          else if (algorithm == ECDSA_OID)
+          else if (algorithm == oid::ECDSA)
             m_type = KEY_TYPE_ECDSA;
           else
             throw Error("Only RSA/ECDSA public keys are supported for now (" +
diff --git a/src/security/sec-tpm-osx.cpp b/src/security/sec-tpm-osx.cpp
index f3a782bc..b805d4e 100644
--- a/src/security/sec-tpm-osx.cpp
+++ b/src/security/sec-tpm-osx.cpp
@@ -535,6 +535,8 @@
       throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
     }
 
+  shared_ptr<PublicKey> publicKey = getPublicKeyFromTpm(keyName);
+
   CFReleaser<CFDataRef> exportedKey;
   OSStatus res = SecItemExport(privateKey.get(),
                                kSecFormatOpenSSL,
@@ -555,11 +557,40 @@
         return shared_ptr<Buffer>();
     }
 
-  OBufferStream pkcs1Os;
-  FileSink sink(pkcs1Os);
-
   uint32_t version = 0;
-  OID algorithm("1.2.840.113549.1.1.1"); // "RSA encryption"
+  OID algorithm;
+  bool hasParameters = false;
+  OID algorithmParameter;
+  switch (publicKey->getKeyType()) {
+  case KEY_TYPE_RSA:
+    {
+      algorithm = oid::RSA; // "RSA encryption"
+      hasParameters = false;
+      break;
+    }
+  case KEY_TYPE_ECDSA:
+    {
+      // "ECDSA encryption"
+      StringSource src(publicKey->get().buf(), publicKey->get().size(), true);
+      BERSequenceDecoder subjectPublicKeyInfo(src);
+      {
+        BERSequenceDecoder algorithmInfo(subjectPublicKeyInfo);
+        {
+          algorithm.decode(algorithmInfo);
+          algorithmParameter.decode(algorithmInfo);
+        }
+      }
+      hasParameters = true;
+      break;
+    }
+  default:
+    throw Error("Unsupported key type" +
+                boost::lexical_cast<std::string>(publicKey->getKeyType()));
+  }
+
+  OBufferStream pkcs8Os;
+  FileSink sink(pkcs8Os);
+
   SecByteBlock rawKeyBits;
   // PrivateKeyInfo ::= SEQUENCE {
   //   version              INTEGER,
@@ -571,7 +602,10 @@
     DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
     {
       algorithm.encode(privateKeyAlgorithm);
-      DEREncodeNull(privateKeyAlgorithm);
+      if (hasParameters)
+        algorithmParameter.encode(privateKeyAlgorithm);
+      else
+        DEREncodeNull(privateKeyAlgorithm);
     }
     privateKeyAlgorithm.MessageEnd();
     DEREncodeOctetString(privateKeyInfo,
@@ -580,7 +614,7 @@
   }
   privateKeyInfo.MessageEnd();
 
-  return pkcs1Os.buf();
+  return pkcs8Os.buf();
 }
 
 #ifdef __GNUC__
@@ -598,8 +632,6 @@
   using namespace CryptoPP;
 
   StringSource privateKeySource(buf, size, true);
-  uint32_t tmpNum;
-  OID tmpOID;
   SecByteBlock rawKeyBits;
   // PrivateKeyInfo ::= SEQUENCE {
   //   INTEGER,
@@ -607,11 +639,24 @@
   //   OCTECT STRING}
   BERSequenceDecoder privateKeyInfo(privateKeySource);
   {
-    BERDecodeUnsigned<uint32_t>(privateKeyInfo, tmpNum, INTEGER);
+    uint32_t versionNum;
+    BERDecodeUnsigned<uint32_t>(privateKeyInfo, versionNum, INTEGER);
     BERSequenceDecoder sequenceDecoder(privateKeyInfo);
     {
-      tmpOID.decode(sequenceDecoder);
-      BERDecodeNull(sequenceDecoder);
+      OID keyTypeOID;
+      keyTypeOID.decode(sequenceDecoder);
+
+      if (keyTypeOID == oid::RSA)
+        BERDecodeNull(sequenceDecoder);
+      else if (keyTypeOID == oid::ECDSA)
+        {
+          OID parameterOID;
+          parameterOID.decode(sequenceDecoder);
+        }
+      else
+        return false; // Unsupported key type;
+
+
     }
     BERDecodeOctetString(privateKeyInfo, rawKeyBits);
   }
diff --git a/src/security/sec-tpm.cpp b/src/security/sec-tpm.cpp
index 3191095..97adf7a 100644
--- a/src/security/sec-tpm.cpp
+++ b/src/security/sec-tpm.cpp
@@ -66,6 +66,7 @@
   e.SetKeyWithIV(derived, derivedLen, iv);
 
   ConstBufferPtr pkcs8PrivateKey = exportPrivateKeyPkcs8FromTpm(keyName);
+
   if (!static_cast<bool>(pkcs8PrivateKey))
     throw Error("Cannot export the private key, #1");
 
@@ -233,7 +234,6 @@
       return false;
     }
 
-
   PKCS5_PBKDF2_HMAC<SHA1> keyGenerator;
   size_t derivedLen = 24; //For DES-EDE3-CBC-PAD
   byte derived[24] = {0};
@@ -271,23 +271,70 @@
                                     privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()))
     return false;
 
+  //determine key type
+  StringSource privateKeySource(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size(), true);
+
+  KeyType publicKeyType = KEY_TYPE_NULL;
+  SecByteBlock rawKeyBits;
+  // PrivateKeyInfo ::= SEQUENCE {
+  //   INTEGER,
+  //   SEQUENCE,
+  //   OCTECT STRING}
+  BERSequenceDecoder privateKeyInfo(privateKeySource);
+  {
+    uint32_t versionNum;
+    BERDecodeUnsigned<uint32_t>(privateKeyInfo, versionNum, INTEGER);
+    BERSequenceDecoder sequenceDecoder(privateKeyInfo);
+    {
+      OID keyTypeOID;
+      keyTypeOID.decode(sequenceDecoder);
+      if (keyTypeOID == oid::RSA)
+        publicKeyType = KEY_TYPE_RSA;
+      else if (keyTypeOID == oid::ECDSA)
+        publicKeyType = KEY_TYPE_ECDSA;
+      else
+        return false; // Unsupported key type;
+    }
+  }
+
+
   //derive public key
   OBufferStream publicKeyOs;
 
-  try
-    {
-      RSA::PrivateKey privateKey;
-      privateKey.Load(StringStore(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()).Ref());
-      RSAFunction publicKey(privateKey);
+  try {
+    switch (publicKeyType) {
+    case KEY_TYPE_RSA:
+      {
+        RSA::PrivateKey privateKey;
+        privateKey.Load(StringStore(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()).Ref());
+        RSAFunction publicKey(privateKey);
 
-      FileSink publicKeySink(publicKeyOs);
-      publicKey.DEREncode(publicKeySink);
-      publicKeySink.MessageEnd();
-    }
-  catch (CryptoPP::Exception& e)
-    {
+        FileSink publicKeySink(publicKeyOs);
+        publicKey.DEREncode(publicKeySink);
+        publicKeySink.MessageEnd();
+        break;
+      }
+    case KEY_TYPE_ECDSA:
+      {
+        ECDSA<ECP, SHA256>::PrivateKey privateKey;
+        privateKey.Load(StringStore(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()).Ref());
+
+        ECDSA<ECP, SHA256>::PublicKey publicKey;
+        privateKey.MakePublicKey(publicKey);
+        publicKey.AccessGroupParameters().SetEncodeAsOID(true);
+
+        FileSink publicKeySink(publicKeyOs);
+        publicKey.DEREncode(publicKeySink);
+        publicKeySink.MessageEnd();
+        break;
+      }
+    default:
       return false;
     }
+  }
+  catch (CryptoPP::Exception& e) {
+      return false;
+  }
 
   if (!importPublicKeyPkcs1IntoTpm(keyName, publicKeyOs.buf()->buf(), publicKeyOs.buf()->size()))
     return false;