security: Add NDN Certificate 2.0

Change-Id: I7d779554f53a613e67f283ca44718e57f2f1c771
Refs: #3103
diff --git a/docs/specs/certificate-format.rst b/docs/specs/certificate-format.rst
index 5873a10..9db67b3 100644
--- a/docs/specs/certificate-format.rst
+++ b/docs/specs/certificate-format.rst
@@ -11,7 +11,7 @@
 a common certificate format, as it requires additional components.  For example,
 a certificate may follow a specific naming convention and may need to include
 validity period, revocation information, etc.  This specification defines
-naming and components of the NDN certificates and is complementary to NDN packet
+naming and structure of the NDN certificates and is complementary to NDN packet
 specification.
 
 ::
@@ -24,6 +24,9 @@
                                  |+------------------------+|
                                  || ContentType:  KEY(2)   ||
                                  |+------------------------+|
+                                 |+------------------------+|
+                                 || FreshnessPeriod: >~ 1h ||
+                                 |+------------------------+|
                                  +--------------------------+
                                  |          Content         |
                                  |+------------------------+|
@@ -42,6 +45,24 @@
                                  +--------------------------+
 
 
+     CertificateV2 ::= DATA-TLV TLV-LENGTH
+                         Name      (= /<NameSpace>/KEY/[KeyId]/[IssuerId]/[Version])
+                         MetaInfo  (.ContentType = KEY,
+                                    .FreshnessPeriod >~ 1h))
+                         Content   (= X509PublicKeyContent)
+                         SignatureInfo (= CertificateV2SignatureInfo)
+                         SignatureValue
+
+     X509PublicKeyContent ::= CONTENT-TLV TLV-LENGTH
+                                BYTE+ (= public key bits in PKCS#8 format)
+
+     CertificateV2SignatureInfo ::= SIGNATURE-INFO-TYPE TLV-LENGTH
+                                      SignatureType
+                                      KeyLocator
+                                      ValidityPeriod
+                                      ... optional critical or non-critical extension blocks ...
+
+
 Name
 ----
 
@@ -49,24 +70,29 @@
 
 ::
 
-    /<SubjectName>/[KeyId]/KEY/[IssuerId]/[Version]
+    /<SubjectName>/KEY/[KeyId]/[IssuerId]/[Version]
 
-A certificate name starts with the subject to which a public key is bound.  The
-second part is a single name component, called KeyId, which should uniquely
-identify the key under the subject namespace.  The value of KeyId is up to
-the owner of the subject namespace (e.g., 8-byte random number, SHA-256 digest
-of the public key, timestamp, or numerical identifier).  A special name
-component ``KEY`` is appended after KeyId, which indicates that the data is a
-certificate.  After ``KEY``, there is an IssuerId name component that
-distinguishes different issuers for the same key.  How to specify the IssuerId
-is up to the issuer and key owner.  The last component is version number.
+A certificate name starts with the subject to which a public key is bound.  The following parts
+include the keyword ``KEY`` component, KeyId, IssuerId, and version components.
+
+``KeyId`` is an opaque name component to identify an instance of the public key for the
+certificate namespace.  The value of `Key ID` is controlled by the namespace owner and can be
+an 8-byte random number, SHA-256 digest of the public key, timestamp, or a simple numerical
+identifier.
+
+``Issuer Id`` is an opaque name component to identify issuer of the certificate.  The value is
+controlled by the certificate issuer and, similar to KeyId, can be an 8-byte random number,
+SHA-256 digest of the issuer's public key, or a simple numerical identifier.
+
+
 For example,
 
 ::
 
-    /edu/ucla/cs/yingdi/%03%CD...%F1/KEY/%9F%D3...%B7/%FD%d2...%8E
-    \_________________/\___________/    \___________/\___________/
-       Subject Name       Key ID          Issuer Id     Version
+      /edu/ucla/cs/yingdi/KEY/%03%CD...%F1/%9F%D3...%B7/%FD%d2...%8E
+      \_________________/    \___________/ \___________/\___________/
+     Certificate Namespace      Key Id       Issuer Id     Version
+          (Identity)
 
 
 MetaInfo
@@ -86,27 +112,15 @@
 SignatureInfo
 -------------
 
-Besides, ``SignatureType`` and ``KeyLocator``, the ``SignatureInfo`` field of a
-certificate include more optional fields.
-
-::
-
-    SignatureInfo ::= SIGNATURE-INFO-TYPE TLV-LENGTH
-                        SignatureType
-                        KeyLocator
-                        ValidityPeriod?
-                        ... (SignatureInfo Extension TLVs)
-
-One optional field is ``ValidityPeriod``, which contains two sub TLV fields:
-``NotBefore`` and ``NotAfter``, which are two UTC timestamps in ISO 8601 compact
-format (``yyyymmddTHHMMSS``, e.g., "20020131T235959").  NotBefore indicates
-when the certificate takes effect while NotAfter indicates when the certificate
-expires.
+The SignatureInfo block of a certificate is required to include the ``ValidityPeriod`` field.
+``ValidityPeriod`` includes two sub TLV fields: ``NotBefore`` and ``NotAfter``, which carry two
+UTC timestamps in ISO 8601 compact format (``yyyymmddTHHMMSS``, e.g., "20020131T235959").
+``NotBefore`` indicates when the certificate takes effect while ``NotAfter`` indicates when the
+certificate expires.
 
 .. note::
-    Using ISO style string is the convention of specifying validity period of
-    certificate, which has been adopted by many certificate systems, such as
-    X.509, PGP, and DNSSEC.
+    Using ISO style string is the convention of specifying the validity period of certificate,
+    which has been adopted by many certificate systems, such as X.509, PGP, and DNSSEC.
 
 ::
 
@@ -133,17 +147,13 @@
 | NotAfter                                    | 255               | 0xFF           |
 +---------------------------------------------+-------------------+----------------+
 
-.. note::
-    TLV-TYPE code that falls into [253, 65536) is encoded in
-    `3-byte <http://named-data.net/doc/ndn-tlv/tlv.html#variable-size-encoding-for-type-t-and-length-l>`__
-
 Extensions
 ~~~~~~~~~~
 
 A certificate may optionally carry some extensions in SignatureInfo.  An extension
-could be either critical or non-critical depends on the TLV-TYPE code convention.  An
-critical extension implies that if a validator cannot recognize or cannot parse the
-extension, the validator must reject the certificate.  An non-critical extension
+could be either critical or non-critical depends on the TLV-TYPE code convention.  A
+critical extension implies that if a validator cannot recognize or parse the
+extension, the validator must reject the certificate.  A non-critical extension
 implies that if a validator cannot recognize or cannot parse the extension, the
 validator may ignore the extension.
 
@@ -151,42 +161,19 @@
 TLV-TYPE code indicates whether the extension is critical or not: ``1`` for critical
 while ``0`` for non-critical.  If an extension could be either critical or
 non-critical, the extension should be allocated with two TLV-TYPE codes which only
-differ at the last bit.  For example, TLV-TYPE codes 256 and 257 are allocated to the
-``StatusChecking`` extension, 256 for critical StatusChecking while 257 for
-non-critical StatusChecking.
+differ at the last bit.
 
+Extensions
+----------
 
-Proposed Extensions
--------------------
-
-We list the proposed extensions here:
+We list currently defined extensions:
 
 +---------------------------------------------+-------------------+----------------+
 | TLV-TYPE                                    | Assigned code     | Assigned code  |
 |                                             | (decimal)         | (hexadecimal)  |
 +=============================================+===================+================+
-| StatusChecking (Non-critical)               | 256               | 0x0100         |
+| AdditionalDescription (non-critical)        | 258               | 0x0102         |
 +---------------------------------------------+-------------------+----------------+
-| StatusChecking (Critical)                   | 257               | 0x0101         |
-+---------------------------------------------+-------------------+----------------+
-| AdditionalDescription (Non-critical)        | 258               | 0x0102         |
-+---------------------------------------------+-------------------+----------------+
-| MultipleSignature (Critical)                | 259               | 0x0103         |
-+---------------------------------------------+-------------------+----------------+
-
-.. note::
-    TLV-TYPE code that falls into [253, 65536) is encoded in
-    `3-byte <http://named-data.net/doc/ndn-tlv/tlv.html#variable-size-encoding-for-type-t-and-length-l>`__
-
-Status Checking
-~~~~~~~~~~~~~~~
-
-TBD
-
-Multiple Signature
-~~~~~~~~~~~~~~~~~~
-
-TBD
 
 AdditionalDescription
 ~~~~~~~~~~~~~~~~~~~~~
diff --git a/src/security/v2/certificate.cpp b/src/security/v2/certificate.cpp
new file mode 100644
index 0000000..75ee53c
--- /dev/null
+++ b/src/security/v2/certificate.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ */
+
+#include "certificate.hpp"
+#include "../../encoding/block-helpers.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+BOOST_CONCEPT_ASSERT((WireEncodable<Certificate>));
+BOOST_CONCEPT_ASSERT((WireDecodable<Certificate>));
+
+// /<NameSpace>/KEY/[KeyId]/[IssuerId]/[Version]
+
+const ssize_t Certificate::VERSION_OFFSET = -1;
+const ssize_t Certificate::ISSUER_ID_OFFSET = -2;
+const ssize_t Certificate::KEY_ID_OFFSET = -3;
+const ssize_t Certificate::KEY_COMPONENT_OFFSET = -4;
+const size_t Certificate::MIN_CERT_NAME_LENGTH = 4;
+const size_t Certificate::MIN_KEY_NAME_LENGTH = 2;
+const name::Component Certificate::KEY_COMPONENT("KEY");
+
+Certificate::Certificate()
+{
+  setContentType(tlv::ContentTypeValue::ContentType_Key);
+}
+
+Certificate::Certificate(Data&& data)
+  : Data(data)
+{
+  if (!isValidName(getName())) {
+    BOOST_THROW_EXCEPTION(Data::Error("Name does not follow the naming convention for certificate"));
+  }
+  if (getContentType() != tlv::ContentTypeValue::ContentType_Key) {
+    BOOST_THROW_EXCEPTION(Data::Error("ContentType is not KEY"));
+  }
+  if (getFreshnessPeriod() < time::seconds::zero()) {
+    BOOST_THROW_EXCEPTION(Data::Error("FreshnessPeriod is not set"));
+  }
+  if (getContent().value_size() == 0) {
+    BOOST_THROW_EXCEPTION(Data::Error("Content is empty"));
+  }
+}
+
+Certificate::Certificate(const Data& data)
+  : Certificate(Data(data))
+{
+}
+
+Certificate::Certificate(const Block& block)
+  : Certificate(Data(block))
+{
+}
+
+Name
+Certificate::getKeyName() const
+{
+  return getName().getPrefix(KEY_ID_OFFSET + 1);
+}
+
+Name
+Certificate::getIdentity() const
+{
+  return getName().getPrefix(KEY_COMPONENT_OFFSET);
+}
+
+name::Component
+Certificate::getKeyId() const
+{
+  return getName().at(KEY_ID_OFFSET);
+}
+
+name::Component
+Certificate::getIssuerId() const
+{
+  return getName().at(ISSUER_ID_OFFSET);
+}
+
+const Buffer
+Certificate::getPublicKey() const
+{
+  if (getContent().value_size() == 0)
+    BOOST_THROW_EXCEPTION(Data::Error("Content is empty"));
+  return Buffer(getContent().value(), getContent().value_size());
+}
+
+ValidityPeriod
+Certificate::getValidityPeriod() const
+{
+  return getSignature().getSignatureInfo().getValidityPeriod();
+}
+
+bool
+Certificate::isValid(const time::system_clock::TimePoint& ts) const
+{
+  return getSignature().getSignatureInfo().getValidityPeriod().isValid(ts);
+}
+
+const Block&
+Certificate::getExtension(uint32_t type) const
+{
+  return getSignature().getSignatureInfo().getTypeSpecificTlv(type);
+}
+
+bool
+Certificate::isValidName(const Name& certName)
+{
+  // /<NameSpace>/KEY/[KeyId]/[IssuerId]/[Version]
+  return (certName.size() >= Certificate::MIN_CERT_NAME_LENGTH &&
+          certName.get(Certificate::KEY_COMPONENT_OFFSET) == Certificate::KEY_COMPONENT);
+}
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/src/security/v2/certificate.hpp b/src/security/v2/certificate.hpp
new file mode 100644
index 0000000..19d377c
--- /dev/null
+++ b/src/security/v2/certificate.hpp
@@ -0,0 +1,179 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ */
+
+#ifndef NDN_SECURITY_V2_CERTIFICATE_HPP
+#define NDN_SECURITY_V2_CERTIFICATE_HPP
+
+#include "../../data.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+/**
+ * @brief The certificate following the certificate format naming convention
+ *
+ * Overview of NDN certificate format:
+ *
+ *     CertificateV2 ::= DATA-TLV TLV-LENGTH
+ *                         Name      (= /<NameSpace>/KEY/[KeyId]/[IssuerId]/[Version])
+ *                         MetaInfo  (.ContentType = KEY)
+ *                         Content   (= X509PublicKeyContent)
+ *                         SignatureInfo (= CertificateV2SignatureInfo)
+ *                         SignatureValue
+ *
+ *     X509PublicKeyContent ::= CONTENT-TLV TLV-LENGTH
+ *                                BYTE+ (= public key bits in PKCS#8 format)
+ *
+ *     CertificateV2SignatureInfo ::= SIGNATURE-INFO-TYPE TLV-LENGTH
+ *                                      SignatureType
+ *                                      KeyLocator
+ *                                      ValidityPeriod
+ *                                      ... optional critical or non-critical extension blocks ...
+ *
+ * An example of NDN certificate name:
+ *
+ *     /edu/ucla/cs/yingdi/KEY/%03%CD...%F1/%9F%D3...%B7/%FD%d2...%8E
+ *     \_________________/    \___________/ \___________/\___________/
+ *    Certificate Namespace      Key Id       Issuer Id     Version
+ *         (Identity)
+ *     \__________________________________/
+ *                   Key Name
+ *
+ * Notes:
+ *
+ * - `Key Id` is opaque name component to identify an instance of the public key for the
+ *   certificate namespace.  The value of `Key ID` is controlled by the namespace owner.  The
+ *   library includes helpers for generation of key IDs using 8-byte random number, SHA-256
+ *   digest of the public key, timestamp, and the specified numerical identifiers.
+ *
+ * - `Issuer Id` is opaque name component to identify issuer of the certificate.  The value is
+ *   controlled by the issuer.  The library includes helpers to set issuer ID to a 8-byte
+ *   random number, SHA-256 digest of the issuer's public key, and the specified numerical
+ *   identifiers.
+ *
+ * - `Key Name` is a logical name of the key used for management pursposes. Key Name includes
+ *   the certificate namespace, keyword `KEY`, and `KeyId` components.
+ *
+ * @see doc/specs/certificate-format.rst
+ */
+class Certificate : public Data
+{
+public:
+  Certificate();
+
+  /**
+   * @brief Construct certificate from a data object
+   * @throw tlv::Error if data does not follow certificate format
+   */
+  explicit
+  Certificate(Data&& data);
+
+  /**
+   * @brief Construct certificate from a data object
+   * @throw tlv::Error if data does not follow certificate format
+   */
+  explicit
+  Certificate(const Data& data);
+
+  /**
+   * @brief Construct certificate from a wire encoding
+   * @throw tlv::Error if wire encoding is invalid or does not follow certificate format
+   */
+  explicit
+  Certificate(const Block& block);
+
+  /**
+   * @brief Get key name
+   */
+  Name
+  getKeyName() const;
+
+  /**
+   * @brief Get identity name
+   */
+  Name
+  getIdentity() const;
+
+  /**
+   * @brief Get key ID
+   */
+  name::Component
+  getKeyId() const;
+
+  /**
+   * @brief Get issuer ID
+   */
+  name::Component
+  getIssuerId() const;
+
+  /**
+   * @brief Get public key bits (in PKCS#8 format)
+   * @throw Error If content is empty
+   */
+  const Buffer
+  getPublicKey() const;
+
+  /**
+   * @brief Get validity period of the certificate
+   */
+  ValidityPeriod
+  getValidityPeriod() const;
+
+  /**
+   * @brief Check if the certificate is valid at @p ts.
+   */
+  bool
+  isValid(const time::system_clock::TimePoint& ts = time::system_clock::now()) const;
+
+  /**
+   * @brief Get extension with TLV @p type
+   * @throw ndn::SignatureInfo::Error if the specified block type does not exist
+   */
+  const Block&
+  getExtension(uint32_t type) const;
+
+  // @TODO Implement extension enumeration (Issue #3907)
+public:
+  /**
+   * @brief Check if the specified name follows the naming convention for the certificate
+   */
+  static bool
+  isValidName(const Name& certName);
+
+public:
+  static const ssize_t VERSION_OFFSET;
+  static const ssize_t ISSUER_ID_OFFSET;
+  static const ssize_t KEY_COMPONENT_OFFSET;
+  static const ssize_t KEY_ID_OFFSET;
+  static const size_t MIN_CERT_NAME_LENGTH;
+  static const size_t MIN_KEY_NAME_LENGTH;
+  static const name::Component KEY_COMPONENT;
+};
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_CERTIFICATE_HPP
diff --git a/tests/unit-tests/security/v2/certificate.t.cpp b/tests/unit-tests/security/v2/certificate.t.cpp
new file mode 100644
index 0000000..8836e05
--- /dev/null
+++ b/tests/unit-tests/security/v2/certificate.t.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ */
+
+#include "security/v2/certificate.hpp"
+
+#include "boost-test.hpp"
+#include "unit-tests/unit-test-time-fixture.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestCertificate, UnitTestTimeFixture)
+
+const uint8_t PUBLIC_KEY[] = {
+  0x30, 0x81, 0x9d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x03, 0x81, 0x8b, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9e,
+  0x06, 0x3e, 0x47, 0x85, 0xb2, 0x34, 0x37, 0xaa, 0x85, 0x47, 0xac, 0x03, 0x24, 0x83, 0xb5,
+  0x9c, 0xa8, 0x05, 0x3a, 0x24, 0x1e, 0xeb, 0x89, 0x01, 0xbb, 0xe9, 0x9b, 0xb2, 0xc3, 0x22,
+  0xac, 0x68, 0xe3, 0xf0, 0x6c, 0x02, 0xce, 0x68, 0xa6, 0xc4, 0xd0, 0xa7, 0x06, 0x90, 0x9c,
+  0xaa, 0x1b, 0x08, 0x1d, 0x8b, 0x43, 0x9a, 0x33, 0x67, 0x44, 0x6d, 0x21, 0xa3, 0x1b, 0x88,
+  0x9a, 0x97, 0x5e, 0x59, 0xc4, 0x15, 0x0b, 0xd9, 0x2c, 0xbd, 0x51, 0x07, 0x61, 0x82, 0xad,
+  0xc1, 0xb8, 0xd7, 0xbf, 0x9b, 0xcf, 0x7d, 0x24, 0xc2, 0x63, 0xf3, 0x97, 0x17, 0xeb, 0xfe,
+  0x62, 0x25, 0xba, 0x5b, 0x4d, 0x8a, 0xc2, 0x7a, 0xbd, 0x43, 0x8a, 0x8f, 0xb8, 0xf2, 0xf1,
+  0xc5, 0x6a, 0x30, 0xd3, 0x50, 0x8c, 0xc8, 0x9a, 0xdf, 0xef, 0xed, 0x35, 0xe7, 0x7a, 0x62,
+  0xea, 0x76, 0x7c, 0xbb, 0x08, 0x26, 0xc7, 0x02, 0x01, 0x11
+};
+
+const uint8_t SIG_INFO[] = {
+  0x16, 0x55, 0x1B, 0x01, 0x01, 0x1C, 0x26, 0x07, 0x24, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x05,
+  0x73, 0x69, 0x74, 0x65, 0x31, 0x08, 0x11, 0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34,
+  0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39, 0x34, 0x08, 0x03, 0x4B, 0x45, 0x59, 0xFD, 0x00, 0xFD,
+  0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32,
+  0x33, 0x37, 0x33, 0x39, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38,
+  0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38
+};
+
+const uint8_t SIG_VALUE[] = {
+  0x17, 0x80, // SignatureValue
+    0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+    0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+    0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+    0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+    0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+    0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+    0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+    0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+    0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+    0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+const uint8_t CERT[] = {
+  0x06, 0xFD, 0x01, 0xBB, // Data
+    0x07, 0x33, // Name /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+      0x08, 0x03, 0x6E, 0x64, 0x6E,
+      0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+      0x08, 0x03, 0x4B, 0x45, 0x59,
+      0x08, 0x11,
+        0x6B, 0x73, 0x6B, 0x2D, 0x31, 0x34, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+        0x34,
+      0x08, 0x04, 0x30, 0x31, 0x32, 0x33,
+      0x08, 0x07, 0xFD, 0x00, 0x00, 0x01, 0x49, 0xC9, 0x8B,
+    0x14, 0x09, // MetaInfo
+      0x18, 0x01, 0x02, // ContentType = Key
+      0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, // FreshnessPeriod = 3600000 ms
+    0x15, 0xA0, // Content
+      0x30, 0x81, 0x9D, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+      0x05, 0x00, 0x03, 0x81, 0x8B, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9E, 0x06, 0x3E,
+      0x47, 0x85, 0xB2, 0x34, 0x37, 0xAA, 0x85, 0x47, 0xAC, 0x03, 0x24, 0x83, 0xB5, 0x9C, 0xA8, 0x05,
+      0x3A, 0x24, 0x1E, 0xEB, 0x89, 0x01, 0xBB, 0xE9, 0x9B, 0xB2, 0xC3, 0x22, 0xAC, 0x68, 0xE3, 0xF0,
+      0x6C, 0x02, 0xCE, 0x68, 0xA6, 0xC4, 0xD0, 0xA7, 0x06, 0x90, 0x9C, 0xAA, 0x1B, 0x08, 0x1D, 0x8B,
+      0x43, 0x9A, 0x33, 0x67, 0x44, 0x6D, 0x21, 0xA3, 0x1B, 0x88, 0x9A, 0x97, 0x5E, 0x59, 0xC4, 0x15,
+      0x0B, 0xD9, 0x2C, 0xBD, 0x51, 0x07, 0x61, 0x82, 0xAD, 0xC1, 0xB8, 0xD7, 0xBF, 0x9B, 0xCF, 0x7D,
+      0x24, 0xC2, 0x63, 0xF3, 0x97, 0x17, 0xEB, 0xFE, 0x62, 0x25, 0xBA, 0x5B, 0x4D, 0x8A, 0xC2, 0x7A,
+      0xBD, 0x43, 0x8A, 0x8F, 0xB8, 0xF2, 0xF1, 0xC5, 0x6A, 0x30, 0xD3, 0x50, 0x8C, 0xC8, 0x9A, 0xDF,
+      0xEF, 0xED, 0x35, 0xE7, 0x7A, 0x62, 0xEA, 0x76, 0x7C, 0xBB, 0x08, 0x26, 0xC7, 0x02, 0x01, 0x11,
+    0x16, 0x55, // SignatureInfo
+      0x1B, 0x01, 0x01, // SignatureType
+      0x1C, 0x26, // KeyLocator: /ndn/site1/KEY/ksk-2516425377094
+        0x07, 0x24,
+          0x08, 0x03, 0x6E, 0x64, 0x6E,
+          0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+          0x08, 0x03, 0x4B, 0x45, 0x59,
+          0x08, 0x11,
+            0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+            0x34,
+      0xFD, 0x00, 0xFD, 0x26, // ValidityPeriod: (20150814T223739, 20150818T223738)
+        0xFD, 0x00, 0xFE, 0x0F,
+          0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x39,
+        0xFD, 0x00, 0xFF, 0x0F,
+          0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38,
+    0x17, 0x80, // SignatureValue
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+Signature
+generateFakeSignature()
+{
+  Block block1(SIG_INFO, sizeof(SIG_INFO));
+  SignatureInfo signatureInfo(block1);
+
+  Name keyLocatorName("/ndn/site1/KEY/ksk-2516425377094");
+  KeyLocator keyLocator(keyLocatorName);
+  signatureInfo.setKeyLocator(keyLocator);
+
+  ValidityPeriod period(time::fromIsoString("20141111T050000"), time::fromIsoString("20141111T060000"));
+  signatureInfo.setValidityPeriod(period);
+
+  Signature signature(signatureInfo);
+  Block block2(SIG_VALUE, sizeof(SIG_VALUE));
+  signature.setValue(block2);
+
+  return signature;
+}
+
+BOOST_AUTO_TEST_CASE(Construction)
+{
+  Block block(CERT, sizeof(CERT));
+  Certificate certificate(block);
+
+  BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+  BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+  BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+  BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+  BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20150814T223739, 20150818T223738)");
+
+  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+  BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+
+  Data data(block);
+  Certificate certificate2(std::move(data));
+  BOOST_CHECK_EQUAL(certificate, certificate2);
+}
+
+BOOST_AUTO_TEST_CASE(Setters)
+{
+  Certificate certificate;
+  certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  certificate.setFreshnessPeriod(time::seconds(3600));
+  certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+  certificate.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+  BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+  BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+  BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+  BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20141111T050000, 20141111T060000)");
+
+  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+  BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+}
+
+BOOST_AUTO_TEST_CASE(ValidityPeriodChecking)
+{
+  Certificate certificate;
+  certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  certificate.setFreshnessPeriod(time::seconds(3600));
+  certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+  certificate.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_EQUAL(certificate.isValid(), true);
+  BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T045959")), false);
+  BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T060001")), false);
+}
+
+// This fixture prepares a well-formed certificate. A test case then modifies one of the
+// fields, and verifies the Certificate class correctly identifies the certificate as
+// malformed.
+class InvalidCertFixture
+{
+public:
+  InvalidCertFixture()
+  {
+    Certificate certBase(Block(CERT, sizeof(CERT)));
+    BOOST_CHECK_NO_THROW((Certificate(certBase)));
+
+    m_certBase = Data(certBase);
+    m_certBase.setSignature(generateFakeSignature());
+
+    BOOST_CHECK_NO_THROW((Certificate(m_certBase)));
+  }
+
+public:
+  Data m_certBase;
+};
+
+BOOST_FIXTURE_TEST_CASE(InvalidName, InvalidCertFixture)
+{
+  Data data(m_certBase);
+  data.setName("/ndn/site1/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  data.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+  BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(InvalidType, InvalidCertFixture)
+{
+  Data data(m_certBase);
+  data.setContentType(tlv::ContentType_Blob);
+  data.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+  BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(EmptyContent, InvalidCertFixture)
+{
+  Data data(m_certBase);
+  data.setContent(nullptr, 0);
+  data.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+  BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+
+  Certificate cert(m_certBase);
+  cert.setContent(nullptr, 0);
+  cert.setSignature(generateFakeSignature());
+  BOOST_CHECK_THROW(cert.getPublicKey(), Certificate::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificate
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn