security: CryptoPP functions are used directly to encode/decode DER/BER

This change eliminates the need for custom der decoder/encoder.

Change-Id: I5be2e55cec2b63157927a4ad87fffe8e8651ed3c
diff --git a/src/security/certificate/certificate-extension.cpp b/src/security/certificate/certificate-extension.cpp
index d9297a9..51c29ed 100644
--- a/src/security/certificate/certificate-extension.cpp
+++ b/src/security/certificate/certificate-extension.cpp
@@ -6,42 +6,51 @@
  * See COPYING for copyright and distribution information.
  */
 
-#include "../../encoding/der/der.hpp"
-#include "../../util/blob-stream.hpp"
 #include <ndn-cpp/security/certificate/certificate-extension.hpp>
+#include <cryptopp/asn.h>
 
 using namespace std;
+using namespace CryptoPP;
 
 namespace ndn {
 
-ptr_lib::shared_ptr<der::DerNode> 
-CertificateExtension::toDer() const
+void
+CertificateExtension::encode(CryptoPP::BufferedTransformation &out) const
 {
-  ptr_lib::shared_ptr<der::DerSequence> root(new der::DerSequence);
-    
-  ptr_lib::shared_ptr<der::DerOid> extensionId(new der::DerOid(extensionId_));
-  ptr_lib::shared_ptr<der::DerBool> isCritical(new der::DerBool(isCritical_));
-  ptr_lib::shared_ptr<der::DerOctetString> extensionValue(new der::DerOctetString(*extensionValue_));
+  // Extension ::= SEQUENCE {
+  //        extnID      OBJECT IDENTIFIER,
+  //        critical    BOOLEAN DEFAULT FALSE,
+  //        extnValue   OCTET STRING  }
 
-  root->addChild(extensionId);
-  root->addChild(isCritical);
-  root->addChild(extensionValue);
-
-  root->getSize();
-
-  return root;
+  DERSequenceEncoder extension(out);
+  {
+    extensionId_.encode(extension);
+    DEREncodeUnsigned(extension, isCritical_, BOOLEAN);
+    DEREncodeOctetString(extension, extensionValue_.buf(), extensionValue_.size());
+  }
+  extension.MessageEnd();
 }
 
-Blob
-CertificateExtension::toDerBlob() const
+void
+CertificateExtension::decode(CryptoPP::BufferedTransformation &in)
 {
-  blob_stream blobStream;
-  der::OutputIterator& start = reinterpret_cast<der::OutputIterator&>(blobStream);
+  // Extension ::= SEQUENCE {
+  //        extnID      OBJECT IDENTIFIER,
+  //        critical    BOOLEAN DEFAULT FALSE,
+  //        extnValue   OCTET STRING  }
 
-  toDer()->encode(start);
+  BERSequenceDecoder extension(in);
+  {
+    extensionId_.decode(extension);
+    BERDecodeUnsigned(extension, isCritical_, BOOLEAN);
 
-  return blobStream.buf();
+    // the extra copy operation can be optimized, but not trivial,
+    // since the length is not known in advance
+    SecByteBlock tmpBlock;
+    BERDecodeOctetString(extension, tmpBlock);
+    extensionValue_.assign(tmpBlock.begin(), tmpBlock.end());
+  }
+  extension.MessageEnd();
 }
-
-
+ 
 }
diff --git a/src/security/certificate/certificate-subject-description.cpp b/src/security/certificate/certificate-subject-description.cpp
index 3571f7f..472b30e 100644
--- a/src/security/certificate/certificate-subject-description.cpp
+++ b/src/security/certificate/certificate-subject-description.cpp
@@ -6,25 +6,58 @@
  * See COPYING for copyright and distribution information.
  */
 
-#include "../../encoding/der/der.hpp"
 #include <ndn-cpp/security/certificate/certificate-subject-description.hpp>
+#include <cryptopp/asn.h>
 
 using namespace std;
+using namespace CryptoPP;
 
 namespace ndn {
 
-ptr_lib::shared_ptr<der::DerNode> 
-CertificateSubjectDescription::toDer() const
+void
+CertificateSubjectDescription::encode(CryptoPP::BufferedTransformation &out) const
 {
-  ptr_lib::shared_ptr<der::DerSequence> root(new der::DerSequence());
+  // RelativeDistinguishedName ::=
+  //     SET OF AttributeTypeAndValue
+  // 
+  // AttributeTypeAndValue ::= SEQUENCE {
+  //     type     AttributeType,
+  //     value    AttributeValue   }
+  // 
+  // AttributeType ::= OBJECT IDENTIFIER
+  // 
+  // AttributeValue ::= ANY DEFINED BY AttributeType
+  DERSequenceEncoder attributeTypeAndValue(out);
+  {
+    oid_.encode(attributeTypeAndValue);
+    DEREncodeTextString(attributeTypeAndValue, value_, PRINTABLE_STRING);
+  }
+  attributeTypeAndValue.MessageEnd();
+}
 
-  ptr_lib::shared_ptr<der::DerOid> oid(new der::DerOid(oid_));
-  ptr_lib::shared_ptr<der::DerPrintableString> value(new der::DerPrintableString(value_));
+void
+CertificateSubjectDescription::decode(CryptoPP::BufferedTransformation &in)
+{
+  // RelativeDistinguishedName ::=
+  //     SET OF AttributeTypeAndValue
+  // 
+  // AttributeTypeAndValue ::= SEQUENCE {
+  //     type     AttributeType,
+  //     value    AttributeValue   }
+  // 
+  // AttributeType ::= OBJECT IDENTIFIER
+  // 
+  // AttributeValue ::= ANY DEFINED BY AttributeType
 
-  root->addChild(oid);
-  root->addChild(value);
+  BERSequenceDecoder attributeTypeAndValue(in);
+  {
+    oid_.decode(attributeTypeAndValue);
 
-  return root;
+    /// @todo May be add more intelligent processing, since the following
+    ///       may fail if somebody encoded attribute that uses non PRINTABLE_STRING as value
+    BERDecodeTextString(attributeTypeAndValue, value_, PRINTABLE_STRING);
+  }
+  attributeTypeAndValue.MessageEnd();
 }
 
 }
diff --git a/src/security/certificate/certificate.cpp b/src/security/certificate/certificate.cpp
index 3275ac5..da88444 100644
--- a/src/security/certificate/certificate.cpp
+++ b/src/security/certificate/certificate.cpp
@@ -8,7 +8,7 @@
 
 #include <ndn-cpp/common.hpp>
 
-#include <float.h>
+#include "certificate.hpp"
 
 #if NDN_CPP_USE_SYSTEM_BOOST
 #include <boost/iostreams/stream.hpp>
@@ -20,14 +20,16 @@
 #include <ndnboost/iostreams/device/array.hpp>
 #endif
 
-#include <ndn-cpp/sha256-with-rsa-signature.hpp>
-#include "../../encoding/der/der.hpp"
-#include "../../encoding/der/visitor/certificate-data-visitor.hpp"
-#include "../../encoding/der/visitor/print-visitor.hpp"
 #include "../../util/logging.hpp"
-#include "../../util/blob-stream.hpp"
-#include "../../c/util/time.h"
-#include <ndn-cpp/security/certificate/certificate.hpp>
+// #include "../../util/blob-stream.hpp"
+// #include <ndn-cpp/security/certificate/certificate.hpp>
+#include "../../util/time.hpp"
+
+#include <cryptopp/asn.h>
+#include <cryptopp/base64.h>
+#include <cryptopp/files.h>
+
+#include "../../encoding/cryptopp/asn_ext.hpp"
 
 INIT_LOGGER("ndn.security.Certificate");
 
@@ -36,8 +38,8 @@
 namespace ndn {
 
 Certificate::Certificate()
-  : notBefore_(DBL_MAX)
-  , notAfter_(-DBL_MAX)
+  : notBefore_(std::numeric_limits<MillisecondsSince1970>::max())
+  , notAfter_(std::numeric_limits<MillisecondsSince1970>::min())
 {}
 
 Certificate::Certificate(const Data& data)
@@ -77,80 +79,202 @@
 void
 Certificate::encode()
 {
-  ptr_lib::shared_ptr<der::DerSequence> root(new der::DerSequence());
+  // Name
+  //    <key_name>/ID-CERT/<id#>
+  // Content
+  // DER encoded idCert:
+  //
+  // 	idCert ::= SEQUENCE {
+  //        validity            Validity,
+  // 	    subject             Name,
+  // 	    subjectPubKeyInfo   SubjectPublicKeyInfo,
+  // 	    extension           Extensions OPTIONAL   }
+  //
+  // 	Validity ::= SEQUENCE {
+  //        notBefore           Time,
+  //        notAfter            Time   }
+  //
+  //    Name ::= CHOICE {
+  //        RDNSequence   }
+  //
+  //    RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+  //
+  //    RelativeDistinguishedName ::=
+  //        SET OF AttributeTypeAndValue
+  //
+  // 	SubjectPublicKeyInfo ::= SEQUENCE {
+  //        algorithm           AlgorithmIdentifier
+  //        keybits             BIT STRING   }
+  //
+  //    Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+  //
+  // (see http://www.ietf.org/rfc/rfc3280.txt for more detail)
+  //
+  // KeyLocator
+  //    issuer’s certificate name
+  // Signature
+
+  using namespace CryptoPP;
+
+  OBufferStream os;
+  CryptoPP::FileSink sink(os);
   
-  ptr_lib::shared_ptr<der::DerSequence> validity(new der::DerSequence());
-  ptr_lib::shared_ptr<der::DerGtime> notBefore(new der::DerGtime(notBefore_));
-  ptr_lib::shared_ptr<der::DerGtime> notAfter(new der::DerGtime(notAfter_));
-  validity->addChild(notBefore);
-  validity->addChild(notAfter);
-  root->addChild(validity);
-
-  ptr_lib::shared_ptr<der::DerSequence> subjectList(new der::DerSequence());
-  SubjectDescriptionList::iterator it = subjectDescriptionList_.begin();
-  for(; it != subjectDescriptionList_.end(); it++)
+  // idCert ::= SEQUENCE {
+  //     validity            Validity,
+  //     subject             Name,
+  //     subjectPubKeyInfo   SubjectPublicKeyInfo,
+  //     extension           Extensions OPTIONAL   }
+  DERSequenceEncoder idCert(sink);
+  {
+    // Validity ::= SEQUENCE {
+    //       notBefore           Time,
+    //       notAfter            Time   }
+    DERSequenceEncoder validity(idCert);
     {
-      ptr_lib::shared_ptr<der::DerNode> child = it->toDer();
-      subjectList->addChild(child);
+      DEREncodeGeneralTime(validity, notBefore_);
+      DEREncodeGeneralTime(validity, notAfter_);
     }
-  root->addChild(subjectList);
+    validity.MessageEnd();
 
-  root->addChild(key_.toDer());
-
-  if(!extensionList_.empty())
+    // Name ::= CHOICE {
+    //     RDNSequence   }
+    // 
+    // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+    DERSequenceEncoder name(idCert);
     {
-      ptr_lib::shared_ptr<der::DerSequence> extnList(new der::DerSequence());
-      ExtensionList::iterator it = extensionList_.begin();
-      for(; it != extensionList_.end(); it++)
-        extnList->addChild(it->toDer());
-      root->addChild(extnList);
+      for(SubjectDescriptionList::iterator it = subjectDescriptionList_.begin();
+          it != subjectDescriptionList_.end(); ++it)
+        {
+          it->encode(name);
+        }
     }
+    name.MessageEnd();
+  
+    // SubjectPublicKeyInfo
+    key_.encode(idCert);
 
-  blob_stream blobStream;
-  der::OutputIterator& start = reinterpret_cast<der::OutputIterator&>(blobStream);
+    // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+    //
+    // Extension ::= SEQUENCE {
+    //        extnID      OBJECT IDENTIFIER,
+    //        critical    BOOLEAN DEFAULT FALSE,
+    //        extnValue   OCTET STRING  }
+    if(!extensionList_.empty())
+      {
+        DERSequenceEncoder extensions(idCert);
+        {
+          
+          for(ExtensionList::iterator it = extensionList_.begin();
+              it != extensionList_.end(); ++it)
+            {
+              it->encode(extensions);
+            }
+        }
+        extensions.MessageEnd();
+      }
+  }
 
-  root->encode(start);
+  idCert.MessageEnd();
 
-  ptr_lib::shared_ptr<vector<uint8_t> > blob = blobStream.buf();
-  setContent(blob);
-  getMetaInfo().setType(ndn_ContentType_KEY);
+  setContent(os.buf());
+  setContentType(MetaInfo::TYPE_KEY);
 }
 
 void 
 Certificate::decode()
 {
-  Blob blob = getContent();
+  using namespace CryptoPP;
 
-  ndnboost::iostreams::stream<ndnboost::iostreams::array_source> is((const char*)blob.buf(), blob.size());
+  OBufferStream os;
+  CryptoPP::StringSource source(getContent().value(), getContent().value_size(), true);
+  
+  // idCert ::= SEQUENCE {
+  //     validity            Validity,
+  //     subject             Name,
+  //     subjectPubKeyInfo   SubjectPublicKeyInfo,
+  //     extension           Extensions OPTIONAL   }
+  BERSequenceDecoder idCert(source);
+  {
+    // Validity ::= SEQUENCE {
+    //       notBefore           Time,
+    //       notAfter            Time   }
+    BERSequenceDecoder validity(idCert);
+    {
+      BERDecodeTime(validity, notBefore_);
+      BERDecodeTime(validity, notAfter_);
+    }
+    validity.MessageEnd();
 
-  ptr_lib::shared_ptr<der::DerNode> node = der::DerNode::parse(reinterpret_cast<der::InputIterator&>(is));
+    // Name ::= CHOICE {
+    //     RDNSequence   }
+    // 
+    // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+    subjectDescriptionList_.clear();
+    BERSequenceDecoder name(idCert);
+    {
+      while(!name.EndReached())
+        {
+          subjectDescriptionList_.push_back(CertificateSubjectDescription(name));
+        }
+    }
+    name.MessageEnd();
+  
+    // SubjectPublicKeyInfo ::= SEQUENCE {
+    //     algorithm           AlgorithmIdentifier
+    //     keybits             BIT STRING   }
+    key_.decode(idCert);
 
-  // der::PrintVisitor printVisitor;
-  // node->accept(printVisitor, string(""));
+    // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+    //
+    // Extension ::= SEQUENCE {
+    //        extnID      OBJECT IDENTIFIER,
+    //        critical    BOOLEAN DEFAULT FALSE,
+    //        extnValue   OCTET STRING  }
+    extensionList_.clear();
+    if(!idCert.EndReached())
+      {
+        BERSequenceDecoder extensions(idCert);
+        {
+          while(!extensions.EndReached())
+            {
+              extensionList_.push_back(CertificateExtension(extensions));
+            }
+        }
+        extensions.MessageEnd();
+      }
+  }
 
-  der::CertificateDataVisitor certDataVisitor;
-  node->accept(certDataVisitor, this);
+  idCert.MessageEnd();
 }
 
 void 
-Certificate::printCertificate()
+Certificate::printCertificate(std::ostream &os) const
 {
-  cout << "Validity:" << endl;
-  cout << der::DerGtime::toIsoString(notBefore_) << endl;
-  cout << der::DerGtime::toIsoString(notAfter_) << endl;
-
-  cout << "Subject Info:" << endl;  
-  vector<CertificateSubjectDescription>::iterator it = subjectDescriptionList_.begin();
-  for(; it < subjectDescriptionList_.end(); it++){
-    cout << it->getOidString() << "\t" << it->getValue() << endl;
+  os << "Certificate name: " << endl;
+  os << "  " << getName() << endl;
+  os << "Validity:" << endl;
+  {
+    os << "  NotBefore: " << toIsoString(notBefore_) << endl;
+    os << "  NotAfter: "  << toIsoString(notAfter_)  << endl;
   }
 
-  ndnboost::iostreams::stream<ndnboost::iostreams::array_source> is((const char*)key_.getKeyDer().buf(), key_.getKeyDer().size());
+  os << "Subject Description:" << endl;  
+  for(SubjectDescriptionList::const_iterator it = subjectDescriptionList_.begin();
+      it != subjectDescriptionList_.end(); ++it)
+    {
+      os << "  " << it->getOidString() << ": " << it->getValue() << endl;
+    }
 
-  ptr_lib::shared_ptr<der::DerNode> keyRoot = der::DerNode::parse(reinterpret_cast<der::InputIterator&> (is));
+  os << "Public key bits: " << endl;
+  CryptoPP::Base64Encoder encoder(new CryptoPP::FileSink(os), true, 64);
+  key_.encode(encoder);
+  
+  // ndnboost::iostreams::stream<ndnboost::iostreams::array_source> is((const char*)key_.getKeyDer().buf(), key_.getKeyDer().size());
 
-  der::PrintVisitor printVisitor;
-  keyRoot->accept(printVisitor, string(""));
+  // ptr_lib::shared_ptr<der::DerNode> keyRoot = der::DerNode::parse(reinterpret_cast<der::InputIterator&> (is));
+
+  // der::PrintVisitor printVisitor;
+  // keyRoot->accept(printVisitor, string(""));
 }
 
 }
diff --git a/src/security/certificate/public-key.cpp b/src/security/certificate/public-key.cpp
index fc41a94..131e3c1 100644
--- a/src/security/certificate/public-key.cpp
+++ b/src/security/certificate/public-key.cpp
@@ -8,6 +8,8 @@
 
 #include <ndn-cpp/common.hpp>
 
+#include "public-key.hpp"
+
 #if NDN_CPP_USE_SYSTEM_BOOST
 #include <boost/iostreams/stream.hpp>
 #include <boost/iostreams/device/array.hpp>
@@ -18,49 +20,109 @@
 #include <ndnboost/iostreams/device/array.hpp>
 #endif
 
-#include <ndn-cpp/security/security-exception.hpp>
-#include "../../c/util/crypto.h"
-#include "../../encoding/der/der.hpp"
-#include <ndn-cpp/security/certificate/public-key.hpp>
+#include <cryptopp/rsa.h>
+#include <cryptopp/base64.h>
+#include <cryptopp/files.h>
 
 using namespace std;
+using namespace CryptoPP;
 
 namespace ndn {
 
-ptr_lib::shared_ptr<der::DerNode>
-PublicKey::toDer()
-{
-  ndnboost::iostreams::stream<ndnboost::iostreams::array_source> is((const char*)keyDer_.buf (), keyDer_.size ());
+static OID RSA_OID("1.2.840.113549.1.1.1");
 
-  return der::DerNode::parse(reinterpret_cast<der::InputIterator&> (is));
+PublicKey::PublicKey()
+{
 }
 
-static int RSA_OID[] = { 1, 2, 840, 113549, 1, 1, 1 };
-
-ptr_lib::shared_ptr<PublicKey>
-PublicKey::fromDer(const Blob& keyDer)
+/**
+ * Create a new PublicKey with the given values.
+ * @param algorithm The algorithm of the public key.
+ * @param keyDer The blob of the PublicKeyInfo in terms of DER.
+ */
+PublicKey::PublicKey(const uint8_t *keyDerBuf, size_t keyDerSize)
 {
-  // Use a temporary pointer since d2i updates it.
-  const uint8_t *derPointer = keyDer.buf();
-  RSA *publicKey = d2i_RSA_PUBKEY(NULL, &derPointer, keyDer.size());
-  if (!publicKey)
-    throw UnrecognizedKeyFormatException("Error decoding public key DER");  
-  RSA_free(publicKey);
-  
-  return ptr_lib::shared_ptr<PublicKey>(new PublicKey(OID(vector<int>(RSA_OID, RSA_OID + sizeof(RSA_OID))), keyDer));
+  StringSource src(keyDerBuf, keyDerSize, true);
+  decode(src);
 }
 
-Blob
-PublicKey::getDigest(DigestAlgorithm digestAlgorithm) const
+void
+PublicKey::encode(CryptoPP::BufferedTransformation &out) const
 {
-  if (digestAlgorithm == DIGEST_ALGORITHM_SHA256) {
-    uint8_t digest[SHA256_DIGEST_LENGTH];
-    ndn_digestSha256(keyDer_.buf(), keyDer_.size(), digest);
-    
-    return Blob(digest, sizeof(digest));
+  // SubjectPublicKeyInfo ::= SEQUENCE {
+  //     algorithm           AlgorithmIdentifier
+  //     keybits             BIT STRING   }
+
+  out.Put(key_.buf(), key_.size());
+}
+
+void
+PublicKey::decode(CryptoPP::BufferedTransformation &in)
+{
+  // SubjectPublicKeyInfo ::= SEQUENCE {
+  //     algorithm           AlgorithmIdentifier
+  //     keybits             BIT STRING   }
+
+  try {
+    std::string out;
+    StringSink sink(out);
+
+    ////////////////////////
+    // part 1: copy as is //
+    ////////////////////////
+    BERSequenceDecoder decoder(in);
+    {
+      assert (decoder.IsDefiniteLength());
+
+      DERSequenceEncoder encoder(sink);
+      decoder.TransferTo(encoder, decoder.RemainingLength());
+      encoder.MessageEnd();
+    }
+    decoder.MessageEnd();
+
+    ////////////////////////
+    // part 2: check if the key is RSA (since it is the only supported for now)
+    ////////////////////////
+    StringSource checkedSource(out, true);
+    BERSequenceDecoder subjectPublicKeyInfo(checkedSource);
+    {
+      BERSequenceDecoder algorithmInfo(subjectPublicKeyInfo);
+      {
+        OID algorithm;
+        algorithm.decode(algorithmInfo);
+
+        if (algorithm != RSA_OID)
+          throw Error("Only RSA public keys are supported for now (" + algorithm.toString() + " requested");
+      }
+    }
+
+    key_.assign(out.begin(), out.end());
   }
-  else
-    throw UnrecognizedDigestAlgorithmException("Wrong format!");
+  catch (CryptoPP::BERDecodeErr &err) {
+    throw Error("PublicKey decoding error");
+  }
+}
+
+// Blob
+// PublicKey::getDigest(DigestAlgorithm digestAlgorithm) const
+// {
+//   if (digestAlgorithm == DIGEST_ALGORITHM_SHA256) {
+//     uint8_t digest[SHA256_DIGEST_LENGTH];
+//     ndn_digestSha256(keyDer_.buf(), keyDer_.size(), digest);
+    
+//     return Blob(digest, sizeof(digest));
+//   }
+//   else
+//     throw UnrecognizedDigestAlgorithmException("Wrong format!");
+// }
+
+std::ostream &
+operator <<(std::ostream &os, const PublicKey &key)
+{
+  CryptoPP::StringSource(key.get().buf(), key.get().size(), true,
+                         new CryptoPP::Base64Encoder(new CryptoPP::FileSink(os), true, 64));
+
+  return os;
 }
 
 }