Add build system, basic docs and crypto helpers module

Change-Id: I761cde5adac85596c5a3a53ec3e94a9a81fb5416
diff --git a/src/detail/crypto-helpers.cpp b/src/detail/crypto-helpers.cpp
new file mode 100644
index 0000000..3142268
--- /dev/null
+++ b/src/detail/crypto-helpers.cpp
@@ -0,0 +1,386 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "detail/crypto-helpers.hpp"
+
+#include <boost/endian/conversion.hpp>
+#include <cstring>
+#include <ndn-cxx/encoding/buffer-stream.hpp>
+#include <ndn-cxx/security/transform/base64-decode.hpp>
+#include <ndn-cxx/security/transform/base64-encode.hpp>
+#include <ndn-cxx/security/transform/buffer-source.hpp>
+#include <ndn-cxx/security/transform/stream-sink.hpp>
+#include <ndn-cxx/util/random.hpp>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/hmac.h>
+#include <openssl/kdf.h>
+#include <openssl/pem.h>
+
+namespace ndn {
+namespace ndncert {
+
+ECDHState::ECDHState()
+{
+  auto EC_NID = NID_X9_62_prime256v1;
+  // params context
+  EVP_PKEY_CTX* ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
+  if (ctx_params == nullptr) {
+    NDN_THROW(std::runtime_error("Could not create context"));
+  }
+  if (EVP_PKEY_paramgen_init(ctx_params) != 1) {
+    EVP_PKEY_CTX_free(ctx_params);
+    NDN_THROW(std::runtime_error("Could not initialize parameter generation"));
+  }
+  if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_params, EC_NID)) {
+    EVP_PKEY_CTX_free(ctx_params);
+    NDN_THROW(std::runtime_error("Likely unknown elliptical curve ID specified"));
+  }
+  // generate params
+  EVP_PKEY* params = nullptr;
+  if (!EVP_PKEY_paramgen(ctx_params, &params)) {
+    EVP_PKEY_CTX_free(ctx_params);
+    NDN_THROW(std::runtime_error("Could not create parameter object parameters"));
+  }
+  // key generation context
+  EVP_PKEY_CTX* ctx_keygen = EVP_PKEY_CTX_new(params, nullptr);
+  if (ctx_keygen == nullptr) {
+    EVP_PKEY_free(params);
+    EVP_PKEY_CTX_free(ctx_params);
+    NDN_THROW(std::runtime_error("Could not create the context for the key generation"));
+  }
+  if (1 != EVP_PKEY_keygen_init(ctx_keygen)) {
+    EVP_PKEY_CTX_free(ctx_keygen);
+    EVP_PKEY_free(params);
+    EVP_PKEY_CTX_free(ctx_params);
+    NDN_THROW(std::runtime_error("Could not init context for key generation"));
+  }
+  if (1 != EVP_PKEY_keygen(ctx_keygen, &m_privkey)) {
+    EVP_PKEY_CTX_free(ctx_keygen);
+    EVP_PKEY_free(params);
+    EVP_PKEY_CTX_free(ctx_params);
+    NDN_THROW(std::runtime_error("Could not generate DHE keys in final step"));
+  }
+  EVP_PKEY_CTX_free(ctx_keygen);
+  EVP_PKEY_free(params);
+  EVP_PKEY_CTX_free(ctx_params);
+}
+
+ECDHState::~ECDHState()
+{
+  if (m_privkey != nullptr) {
+    EVP_PKEY_free(m_privkey);
+  }
+}
+
+const std::vector<uint8_t>&
+ECDHState::getSelfPubKey()
+{
+  auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
+  if (privECKey == nullptr) {
+    NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
+  }
+  auto ecPoint = EC_KEY_get0_public_key(privECKey);
+  auto group = EC_KEY_get0_group(privECKey);
+  auto requiredBufLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
+  m_pubKey.resize(requiredBufLen);
+  auto rev = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED,
+                                m_pubKey.data(), requiredBufLen, nullptr);
+  EC_KEY_free(privECKey);
+  if (rev == 0) {
+    NDN_THROW(std::runtime_error("Could not convert EC_POINTS to octet string when calling EC_POINT_point2oct()"));
+  }
+  return m_pubKey;
+}
+
+const std::vector<uint8_t>&
+ECDHState::deriveSecret(const std::vector<uint8_t>& peerKey)
+{
+  // prepare self private key
+  auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
+  if (privECKey == nullptr) {
+    NDN_THROW(std::runtime_error("Cannot not get key when calling EVP_PKEY_get1_EC_KEY()"));
+  }
+  auto group = EC_KEY_get0_group(privECKey);
+  EC_KEY_free(privECKey);
+  // prepare the peer public key
+  auto peerPoint = EC_POINT_new(group);
+  if (peerPoint == nullptr) {
+    NDN_THROW(std::runtime_error("Cannot create the EC_POINT for peer key when calling EC_POINT_new()"));
+  }
+  if (EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr) == 0) {
+    EC_POINT_free(peerPoint);
+    NDN_THROW(std::runtime_error("Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()"));
+  }
+  EC_KEY* ecPeerkey = EC_KEY_new();
+  if (ecPeerkey == nullptr) {
+    EC_POINT_free(peerPoint);
+    NDN_THROW(std::runtime_error("Cannot create EC_KEY for peer key when calling EC_KEY_new()"));
+  }
+  if (EC_KEY_set_group(ecPeerkey, group) != 1) {
+    EC_POINT_free(peerPoint);
+    NDN_THROW(std::runtime_error("Cannot set group for peer key's EC_KEY when calling EC_KEY_set_group()"));
+  }
+  if (EC_KEY_set_public_key(ecPeerkey, peerPoint) == 0) {
+    EC_KEY_free(ecPeerkey);
+    EC_POINT_free(peerPoint);
+    NDN_THROW(std::runtime_error("Cannot initialize peer EC_KEY with the EC_POINT when calling EC_KEY_set_public_key()"));
+  }
+  EVP_PKEY* evpPeerkey = EVP_PKEY_new();
+  if (EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey) == 0) {
+    EC_KEY_free(ecPeerkey);
+    EC_POINT_free(peerPoint);
+    NDN_THROW(std::runtime_error("Cannot create EVP_PKEY for peer key when calling EVP_PKEY_new()"));
+  }
+  EC_KEY_free(ecPeerkey);
+  EC_POINT_free(peerPoint);
+  // ECDH context
+  EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
+  if (ctx == nullptr) {
+    EVP_PKEY_free(evpPeerkey);
+    NDN_THROW(std::runtime_error("Cannot create context for ECDH when calling EVP_PKEY_CTX_new()"));
+  }
+  // Initialize
+  if (1 != EVP_PKEY_derive_init(ctx)) {
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(evpPeerkey);
+    NDN_THROW(std::runtime_error("Cannot initialize context for ECDH when calling EVP_PKEY_derive_init()"));
+  }
+  // Provide the peer public key
+  if (1 != EVP_PKEY_derive_set_peer(ctx, evpPeerkey)) {
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(evpPeerkey);
+    NDN_THROW(std::runtime_error("Cannot set peer key for ECDH when calling EVP_PKEY_derive_set_peer()"));
+  }
+  // Determine buffer length for shared secret
+  size_t secretLen = 0;
+  if (1 != EVP_PKEY_derive(ctx, nullptr, &secretLen)) {
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(evpPeerkey);
+    NDN_THROW(std::runtime_error("Cannot determine the needed buffer length when calling EVP_PKEY_derive()"));
+  }
+  m_secret.resize(secretLen);
+  // Derive the shared secret
+  if (1 != (EVP_PKEY_derive(ctx, m_secret.data(), &secretLen))) {
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(evpPeerkey);
+    NDN_THROW(std::runtime_error("Cannot derive ECDH secret when calling EVP_PKEY_derive()"));
+  }
+  EVP_PKEY_CTX_free(ctx);
+  EVP_PKEY_free(evpPeerkey);
+  return m_secret;
+}
+
+void
+hmacSha256(const uint8_t* data, size_t dataLen,
+           const uint8_t* key, size_t keyLen,
+           uint8_t* result)
+{
+  auto ret = HMAC(EVP_sha256(), key, keyLen,
+                  data, dataLen, result, nullptr);
+  if (ret == nullptr) {
+    NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
+  }
+}
+
+size_t
+hkdf(const uint8_t* secret, size_t secretLen, const uint8_t* salt,
+     size_t saltLen, uint8_t* output, size_t outputLen,
+     const uint8_t* info, size_t infoLen)
+{
+  EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
+  if (EVP_PKEY_derive_init(pctx) <= 0) {
+    EVP_PKEY_CTX_free(pctx);
+    NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()"));
+  }
+  if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
+    EVP_PKEY_CTX_free(pctx);
+    NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()"));
+  }
+  if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen) <= 0) {
+    EVP_PKEY_CTX_free(pctx);
+    NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()"));
+  }
+  if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen) <= 0) {
+    EVP_PKEY_CTX_free(pctx);
+    NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()"));
+  }
+  if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen) <= 0) {
+    EVP_PKEY_CTX_free(pctx);
+    NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()"));
+  }
+  size_t outLen = outputLen;
+  if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
+    EVP_PKEY_CTX_free(pctx);
+    NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()"));
+  }
+  EVP_PKEY_CTX_free(pctx);
+  return outLen;
+}
+
+size_t
+aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
+                 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
+{
+  EVP_CIPHER_CTX* ctx;
+  int len;
+  size_t ciphertextLen;
+  if (!(ctx = EVP_CIPHER_CTX_new())) {
+    NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
+  }
+  if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot initialize the encryption operation when calling EVP_EncryptInit_ex()"));
+  }
+  if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
+  }
+  if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
+  }
+  if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
+  }
+  if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
+  }
+  ciphertextLen = len;
+  if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
+  }
+  ciphertextLen += len;
+  if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
+  }
+  EVP_CIPHER_CTX_free(ctx);
+  return ciphertextLen;
+}
+
+size_t
+aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
+                 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
+{
+  EVP_CIPHER_CTX* ctx;
+  int len;
+  size_t plaintextLen;
+  if (!(ctx = EVP_CIPHER_CTX_new())) {
+    NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
+  }
+  if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
+  }
+  if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
+  }
+  if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
+  }
+  if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
+  }
+  if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen)) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
+  }
+  plaintextLen = len;
+  if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)))) {
+    EVP_CIPHER_CTX_free(ctx);
+    NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl()"));
+  }
+  auto ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
+  EVP_CIPHER_CTX_free(ctx);
+  if (ret > 0) {
+    plaintextLen += len;
+    return plaintextLen;
+  }
+  else {
+    NDN_THROW(std::runtime_error("Cannot finalize the decryption when calling EVP_DecryptFinal_ex()"));
+  }
+}
+
+Block
+encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
+                         const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter)
+{
+  Buffer iv(12);
+  random::generateSecureBytes(iv.data(), 8);
+  if (tlvType == ndn::tlv::ApplicationParameters) {
+    // requester
+    iv[0] &= ~(1UL << 7);
+  }
+  else {
+    // CA
+    iv[0] |= 1UL << 7;
+  }
+  uint32_t temp = boost::endian::native_to_big(counter);
+  std::memcpy(&iv[8], reinterpret_cast<const uint8_t*>(&temp), 4);
+  uint32_t increment = (payloadSize + 15) / 16;
+  if (std::numeric_limits<uint32_t>::max() - counter < increment) {
+    NDN_THROW(std::runtime_error("Error incrementing the AES block counter: "
+                                 "too many blocks have been encrypted for the same request instance"));
+  }
+  else {
+    counter += increment;
+  }
+
+  // The spec of AES encrypted payload TLV used in NDNCERT:
+  //   https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
+  Buffer encryptedPayload(payloadSize);
+  uint8_t tag[16];
+  size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
+                                                key, iv.data(), encryptedPayload.data(), tag);
+  auto content = makeEmptyBlock(tlvType);
+  content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
+  content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
+  content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
+  content.encode();
+  return content;
+}
+
+Buffer
+decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
+{
+  // The spec of AES encrypted payload TLV used in NDNCERT:
+  //   https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
+  block.parse();
+  const auto& encryptedPayloadBlock = block.get(tlv::EncryptedPayload);
+  Buffer result(encryptedPayloadBlock.value_size());
+  auto resultLen = aesGcm128Decrypt(encryptedPayloadBlock.value(), encryptedPayloadBlock.value_size(),
+                                    associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
+                                    key, block.get(tlv::InitializationVector).value(), result.data());
+  if (resultLen != encryptedPayloadBlock.value_size()) {
+    NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
+                                 "Decrypted payload is of an unexpected size"));
+  }
+  return result;
+}
+
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/detail/crypto-helpers.hpp b/src/detail/crypto-helpers.hpp
new file mode 100644
index 0000000..56446e4
--- /dev/null
+++ b/src/detail/crypto-helpers.hpp
@@ -0,0 +1,173 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_DETAIL_CRYPTO_HELPERS_HPP
+#define NDNCERT_DETAIL_CRYPTO_HELPERS_HPP
+
+#include "detail/ndncert-common.hpp"
+#include <openssl/evp.h>
+
+namespace ndn {
+namespace ndncert {
+
+/**
+ * @brief State for ECDH.
+ *
+ * The ECDH is based on prime256v1.
+ */
+class ECDHState : noncopyable
+{
+public:
+  ECDHState();
+  ~ECDHState();
+
+  /**
+   * @brief Derive ECDH secret from peer's EC public key and self's private key.
+   *
+   * @param peerkey Peer's EC public key in the uncompressed octet string format.
+   *                See details in https://www.openssl.org/docs/man1.1.1/man3/EC_POINT_point2oct.html.
+   * @return const std::vector<uint8_t>& the derived secret.
+   */
+  const std::vector<uint8_t>&
+  deriveSecret(const std::vector<uint8_t>& peerkey);
+
+  /**
+   * @brief Get the Self Pub Key object
+   *
+   * @return const std::vector<uint8_t>& the Self public key in the uncompressed oct string format.
+   *         See details in https://www.openssl.org/docs/man1.1.1/man3/EC_POINT_point2oct.html.
+   */
+  const std::vector<uint8_t>&
+  getSelfPubKey();
+
+private:
+  EVP_PKEY* m_privkey = nullptr;
+  std::vector<uint8_t> m_pubKey;
+  std::vector<uint8_t> m_secret;
+};
+
+/**
+ * @brief HMAC based key derivation function (HKDF).
+ *
+ * @param secret The input to the HKDF.
+ * @param secretLen The length of the secret.
+ * @param salt The salt used in HKDF.
+ * @param saltLen The length of the salt.
+ * @param output The output of the HKDF.
+ * @param outputLen The length of expected output.
+ * @param info The additional information used in HKDF.
+ * @param infoLen The length of the additional information.
+ * @return size_t The length of the derived key if successful.
+ */
+size_t
+hkdf(const uint8_t* secret, size_t secretLen,
+     const uint8_t* salt, size_t saltLen,
+     uint8_t* output, size_t outputLen,
+     const uint8_t* info = nullptr, size_t infoLen = 0);
+
+/**
+ * @brief HMAC based on SHA-256.
+ *
+ * @param data The intput array to hmac.
+ * @param dataLen The length of the input array.
+ * @param key The HMAC key.
+ * @param keyLen The length of the HMAC key.
+ * @param result The result of the HMAC. Enough memory (32 Bytes) must be allocated beforehands.
+ * @throw runtime_error when an error occurred in the underlying HMAC.
+ */
+void
+hmacSha256(const uint8_t* data, size_t dataLen,
+           const uint8_t* key, size_t keyLen,
+           uint8_t* result);
+
+/**
+ * @brief Authenticated GCM 128 Encryption with associated data.
+ *
+ * @param plaintext The plaintext.
+ * @param plaintextLen The size of plaintext.
+ * @param associated The associated authentication data.
+ * @param associatedLen The size of associated authentication data.
+ * @param key 16 bytes AES key.
+ * @param iv 12 bytes IV.
+ * @param ciphertext The output and enough memory must be allocated beforehands.
+ * @param tag 16 bytes tag.
+ * @return size_t The size of ciphertext.
+ * @throw runtime_error When there is an error in the process of encryption.
+ */
+size_t
+aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
+                 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag);
+
+/**
+ * @brief Authenticated GCM 128 Decryption with associated data.
+ *
+ * @param ciphertext The ciphertext.
+ * @param ciphertextLen The size of ciphertext.
+ * @param associated The associated authentication data.
+ * @param associatedLen The size of associated authentication data.
+ * @param tag 16 bytes tag.
+ * @param key 16 bytes AES key.
+ * @param iv 12 bytes IV.
+ * @param plaintext The output and enough memory must be allocated beforehands.
+ * @return size_t The size of plaintext.
+ * @throw runtime_error When there is an error in the process of decryption.
+ */
+size_t
+aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
+                 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext);
+
+/**
+ * @brief Encode the payload into TLV block with Authenticated GCM 128 Encryption.
+ *
+ * The TLV spec: https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption.
+ *
+ * @param tlv_type The TLV TYPE of the encoded block, either ApplicationParameters or Content.
+ * @param key The AES key used for encryption.
+ * @param payload The plaintext payload.
+ * @param payloadSize The size of the plaintext payload.
+ * @param associatedData The associated data used for authentication.
+ * @param associatedDataSize The size of associated data.
+ * @param counter An opaque counter that must be passed to subsequent invocations of this function
+ *                with the same @p key.
+ * @return Block The TLV block with @p tlv_type TLV TYPE.
+ */
+Block
+encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
+                         const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter);
+
+/**
+ * @brief Decode the payload from TLV block with Authenticated GCM 128 Encryption.
+ *
+ * The TLV spec: https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption.
+ *
+ * @param block The TLV block in the format of NDNCERT protocol.
+ * @param key The AES key used for encryption.
+ * @param associatedData The associated data used for authentication.
+ * @param associatedDataSize The size of associated data.
+ * @return Buffer The plaintext buffer.
+ */
+Buffer
+decodeBlockWithAesGcm128(const Block& block, const uint8_t* key,
+                         const uint8_t* associatedData, size_t associatedDataSize);
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_DETAIL_CRYPTO_HELPERS_HPP
diff --git a/src/detail/ndncert-common.cpp b/src/detail/ndncert-common.cpp
new file mode 100644
index 0000000..7c210b7
--- /dev/null
+++ b/src/detail/ndncert-common.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#include "detail/ndncert-common.hpp"
+
+namespace ndn {
+namespace ndncert {
+
+std::ostream&
+operator<<(std::ostream& out, ErrorCode code)
+{
+  switch (code) {
+    case ErrorCode::NO_ERROR: out << "NO_ERROR"; break;
+    case ErrorCode::BAD_INTEREST_FORMAT: out << "BAD_INTEREST_FORMAT"; break;
+    case ErrorCode::BAD_PARAMETER_FORMAT: out << "BAD_PARAMETER_FORMAT"; break;
+    case ErrorCode::BAD_SIGNATURE: out << "BAD_SIGNATURE"; break;
+    case ErrorCode::INVALID_PARAMETER: out << "INVALID_PARAMETER"; break;
+    case ErrorCode::NAME_NOT_ALLOWED: out << "NAME_NOT_ALLOWED"; break;
+    case ErrorCode::BAD_VALIDITY_PERIOD: out << "BAD_VALIDITY_PERIOD"; break;
+    case ErrorCode::OUT_OF_TRIES: out << "OUT_OF_TRIES"; break;
+    case ErrorCode::OUT_OF_TIME: out << "OUT_OF_TIME"; break;
+    case ErrorCode::NO_AVAILABLE_NAMES: out << "NO_AVAILABLE_NAMES"; break;
+    default: out << "UNKNOWN_ERROR"; break;
+  }
+  return out;
+}
+
+std::ostream&
+operator<<(std::ostream& out, RequestType type)
+{
+  switch (type) {
+    case RequestType::NEW: out << "New"; break;
+    case RequestType::RENEW: out << "Renew"; break;
+    case RequestType::REVOKE: out << "Revoke"; break;
+    case RequestType::NOTINITIALIZED: out << "Not Initialized"; break;
+    default: out << "UNKNOWN_REQUEST_TYPE"; break;
+  }
+  return out;
+}
+
+} // namespace ndncert
+} // namespace ndn
diff --git a/src/detail/ndncert-common.hpp b/src/detail/ndncert-common.hpp
new file mode 100644
index 0000000..77f6ca2
--- /dev/null
+++ b/src/detail/ndncert-common.hpp
@@ -0,0 +1,131 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
+ *
+ * This file is part of ndncert, a certificate management system based on NDN.
+ *
+ * ndncert is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ndncert 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 General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ndncert, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndncert authors and contributors.
+ */
+
+#ifndef NDNCERT_DETAIL_NDNCERT_COMMON_HPP
+#define NDNCERT_DETAIL_NDNCERT_COMMON_HPP
+
+#include "detail/ndncert-config.hpp"
+
+#ifdef NDNCERT_HAVE_TESTS
+#define NDNCERT_VIRTUAL_WITH_TESTS virtual
+#define NDNCERT_PUBLIC_WITH_TESTS_ELSE_PROTECTED public
+#define NDNCERT_PUBLIC_WITH_TESTS_ELSE_PRIVATE public
+#define NDNCERT_PROTECTED_WITH_TESTS_ELSE_PRIVATE protected
+#else
+#define NDNCERT_VIRTUAL_WITH_TESTS
+#define NDNCERT_PUBLIC_WITH_TESTS_ELSE_PROTECTED protected
+#define NDNCERT_PUBLIC_WITH_TESTS_ELSE_PRIVATE private
+#define NDNCERT_PROTECTED_WITH_TESTS_ELSE_PRIVATE private
+#endif
+
+#include <cstddef>
+#include <cstdint>
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/encoding/block-helpers.hpp>
+#include <ndn-cxx/encoding/block.hpp>
+#include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/lp/nack.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/security/certificate.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/logger.hpp>
+#include <tuple>
+#include <boost/algorithm/string.hpp>
+#include <boost/assert.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/property_tree/info_parser.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
+
+namespace ndn {
+namespace ndncert {
+
+namespace tlv {
+
+enum : uint32_t {
+  CaPrefix = 129,
+  CaInfo = 131,
+  ParameterKey = 133,
+  ParameterValue = 135,
+  CaCertificate = 137,
+  MaxValidityPeriod = 139,
+  ProbeResponse = 141,
+  MaxSuffixLength = 143,
+  EcdhPub = 145,
+  CertRequest = 147,
+  Salt = 149,
+  RequestId = 151,
+  Challenge = 153,
+  Status = 155,
+  InitializationVector = 157,
+  EncryptedPayload = 159,
+  SelectedChallenge = 161,
+  ChallengeStatus = 163,
+  RemainingTries = 165,
+  RemainingTime = 167,
+  IssuedCertName = 169,
+  ErrorCode = 171,
+  ErrorInfo = 173,
+  AuthenticationTag = 175,
+  CertToRevoke = 177,
+  ProbeRedirect = 179
+};
+
+} // namespace tlv
+
+using boost::noncopyable;
+typedef boost::property_tree::ptree JsonSection;
+
+// NDNCERT error code
+enum class ErrorCode : uint64_t {
+  NO_ERROR = 0,
+  BAD_INTEREST_FORMAT = 1,
+  BAD_PARAMETER_FORMAT = 2,
+  BAD_SIGNATURE = 3,
+  INVALID_PARAMETER = 4,
+  NAME_NOT_ALLOWED = 5,
+  BAD_VALIDITY_PERIOD = 6,
+  OUT_OF_TRIES = 7,
+  OUT_OF_TIME = 8,
+  NO_AVAILABLE_NAMES = 9
+};
+
+// Convert error code to string
+std::ostream&
+operator<<(std::ostream& os, ErrorCode code);
+
+// NDNCERT request type
+enum class RequestType : uint64_t {
+  NOTINITIALIZED = 0,
+  NEW = 1,
+  RENEW = 2,
+  REVOKE = 3
+};
+
+// Convert request type to string
+std::ostream&
+operator<<(std::ostream& out, RequestType type);
+
+} // namespace ndncert
+} // namespace ndn
+
+#endif // NDNCERT_DETAIL_NDNCERT_COMMON_HPP