security: Add new v2::KeyChain
Change-Id: I5fdf51ecd96b50db2a7cbf730c6e8b1d9fbe09e9
Refs: #2926
diff --git a/src/security/v2/key-chain.hpp b/src/security/v2/key-chain.hpp
new file mode 100644
index 0000000..ca394a5
--- /dev/null
+++ b/src/security/v2/key-chain.hpp
@@ -0,0 +1,508 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 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.
+ */
+
+#ifndef NDN_SECURITY_V2_KEY_CHAIN_HPP
+#define NDN_SECURITY_V2_KEY_CHAIN_HPP
+
+#include "certificate.hpp"
+#include "../key-params.hpp"
+#include "../pib/pib.hpp"
+#include "../safe-bag.hpp"
+#include "../signing-info.hpp"
+#include "../tpm/tpm.hpp"
+#include "../../interest.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+
+/**
+ * @brief The interface of signing key management.
+ *
+ * The KeyChain class provides an interface to manage entities related to packet signing,
+ * such as Identity, Key, and Certificates. It consists of two parts: a private key module
+ * (TPM) and a public key information base (PIB). Managing signing key and its related
+ * entities through KeyChain interface guarantees the consistency between TPM and PIB.
+ */
+class KeyChain : noncopyable
+{
+public:
+ class Error : public std::runtime_error
+ {
+ public:
+ explicit
+ Error(const std::string& what)
+ : std::runtime_error(what)
+ {
+ }
+ };
+
+ /**
+ * @brief Error indicating that the supplied TPM locator does not match the locator stored in PIB.
+ */
+ class LocatorMismatchError : public Error
+ {
+ public:
+ explicit
+ LocatorMismatchError(const std::string& what)
+ : Error(what)
+ {
+ }
+ };
+
+ /**
+ * @brief Error indicating that the supplied SigningInfo is invalid.
+ */
+ class InvalidSigningInfoError : public Error
+ {
+ public:
+ explicit
+ InvalidSigningInfoError(const std::string& what)
+ : Error(what)
+ {
+ }
+ };
+
+ /**
+ * @brief Constructor to create KeyChain with default PIB and TPM.
+ *
+ * Default PIB and TPM are platform-dependent and can be overriden system-wide or
+ * individually for the user.
+ *
+ * @sa manpage ndn-client.conf
+ *
+ * @todo Add detailed description about config file behavior here
+ */
+ KeyChain();
+
+ /**
+ * @brief KeyChain constructor
+ *
+ * @sa manpage ndn-client.conf
+ *
+ * @param pibLocator PIB locator, e.g., pib-sqlite3:/example/dir
+ * @param tpmLocator TPM locator, e.g., tpm-memory:
+ * @param allowReset if true, the PIB will be reset when the supplied tpmLocator
+ * mismatches the one in PIB
+ */
+ KeyChain(const std::string& pibLocator, const std::string& tpmLocator, bool allowReset = false);
+
+ ~KeyChain();
+
+ const Pib&
+ getPib() const
+ {
+ return *m_pib;
+ }
+
+ const Tpm&
+ getTpm() const
+ {
+ return *m_tpm;
+ }
+
+public: // Identity management
+ /**
+ * @brief Create an identity @p identityName.
+ *
+ * This method will check if the identity exists in PIB and whether the identity has a
+ * default key and default certificate. If the identity does not exist, this method will
+ * create the identity in PIB. If the identity's default key does not exist, this method
+ * will create a key pair and set it as the identity's default key. If the key's default
+ * certificate is missing, this method will create a self-signed certificate for the key.
+ *
+ * If @p identityName did not exist and no default identity was selected before, the created
+ * identity will be set as the default identity
+ *
+ * @param identityName The name of the identity.
+ * @param params The key parameters if a key needs to be created for the identity (default:
+ * ECDSA key with random key id)
+ * @return The created Identity instance.
+ */
+ Identity
+ createIdentity(const Name& identityName, const KeyParams& params = getDefaultKeyParams());
+
+ /**
+ * @brief delete @p identity.
+ *
+ * @pre @p identity must be valid.
+ * @post @p identity becomes invalid.
+ */
+ void
+ deleteIdentity(const Identity& identity);
+
+ /**
+ * @brief Set @p identity as the default identity.
+ * @pre @p identity must be valid.
+ */
+ void
+ setDefaultIdentity(const Identity& identity);
+
+public: // Key management
+ /**
+ * @brief Create a key for @p identity according to @p params.
+ *
+ * @param identity reference to a valid Identity object
+ * @param params The key parameters if a key needs to be created for the identity (default:
+ * ECDSA key with random key id)
+ *
+ * If @p identity had no default key selected, the created key will be set as the default for
+ * this identity.
+ *
+ * This method will also create a self-signed certificate for the created key.
+ * @pre @p identity must be valid.
+ */
+ Key
+ createKey(const Identity& identity, const KeyParams& params = getDefaultKeyParams());
+
+ /**
+ * @brief Delete a key @p key of @p identity.
+ *
+ * @pre @p identity must be valid.
+ * @pre @p key must be valid.
+ * @post @p key becomes invalid.
+ * @throw std::invalid_argument @p key does not belong to @p identity
+ */
+ void
+ deleteKey(const Identity& identity, const Key& key);
+
+ /**
+ * @brief Set @p key as the default key of @p identity.
+ *
+ * @pre @p identity must be valid.
+ * @pre @p key must be valid.
+ * @throw std::invalid_argument @p key does not belong to @p identity
+ */
+ void
+ setDefaultKey(const Identity& identity, const Key& key);
+
+public: // Certificate management
+ /**
+ * @brief Add a certificate @p certificate for @p key
+ *
+ * If @p key had no default certificate selected, the added certificate will be set as the
+ * default certificate for this key.
+ *
+ * @note This method overwrites certificate with the same name, without considering the
+ * implicit digest.
+ *
+ * @pre @p key must be valid.
+ * @throw std::invalid_argument @p key does not match @p certificate
+ * @throw Pib::Error a certificate with the same name already exists
+ */
+ void
+ addCertificate(const Key& key, const Certificate& certificate);
+
+ /**
+ * @brief delete a certificate with name @p certificateName of @p key.
+ *
+ * If the certificate @p certificateName does not exist, this method has no effect.
+ *
+ * @pre @p key must be valid.
+ * @throw std::invalid_argument @p certificateName does not follow certificate naming convention.
+ */
+ void
+ deleteCertificate(const Key& key, const Name& certificateName);
+
+ /**
+ * @brief Set @p cert as the default certificate of @p key.
+ *
+ * The certificate @p cert will be added to the @p key, potentially overriding existing
+ * certificate if it has the same name (without considering implicit digest).
+ *
+ * @pre @p key must be valid.
+ */
+ void
+ setDefaultCertificate(const Key& key, const Certificate& cert);
+
+public: // signing
+ /**
+ * @brief Sign data according to the supplied signing information.
+ *
+ * This method uses the supplied signing information @p params to create the SignatureInfo block:
+ * - it selects a private key and its certificate to sign the packet
+ * - sets the KeyLocator field with the certificate name, and
+ * - adds other requested information to the SignatureInfo block.
+ *
+ * After that, the method assigns the created SignatureInfo to the data packets, generate a
+ * signature and sets as part of the SignatureValue block.
+ *
+ * @note The exception throwing semantics has changed from v1::KeyChain.
+ * If the requested identity/key/certificate does not exist, it will **not** be created
+ * and exception will be thrown.
+ *
+ * @param data The data to sign
+ * @param params The signing parameters.
+ * @throw Error signing fails
+ * @throw InvalidSigningInfoError invalid @p params is specified or specified identity, key,
+ * or certificate does not exist
+ * @see SigningInfo
+ */
+ void
+ sign(Data& data, const SigningInfo& params = getDefaultSigningInfo());
+
+ /**
+ * @brief Sign interest according to the supplied signing information
+ *
+ * This method uses the supplied signing information @p params to create the SignatureInfo block:
+ * - it selects a private key and its certificate to sign the packet
+ * - sets the KeyLocator field with the certificate name, and
+ * - adds other requested information to the SignatureInfo block.
+ *
+ * After that, the method appends the created SignatureInfo to the interest name, generate a
+ * signature and appends it as part of the SignatureValue block to the interest name.
+ *
+ * @note The exception throwing semantics has changed from v1::KeyChain. If the requested
+ * identity/key/certificate does not exist, it will **not** be created and exception
+ * will be thrown.
+ *
+ * @param interest The interest to sign
+ * @param params The signing parameters.
+ * @throw Error signing fails
+ * @throw InvalidSigningInfoError invalid @p params is specified or specified identity, key,
+ * or certificate does not exist
+ * @see SigningInfo
+ * @see docs/specs/signed-interest.rst
+ */
+ void
+ sign(Interest& interest, const SigningInfo& params = getDefaultSigningInfo());
+
+ /**
+ * @brief Sign buffer according to the supplied signing information @p params
+ *
+ * If @p params refers to an identity, the method selects the default key of the identity.
+ * If @p params refers to a key or certificate, the method select the corresponding key.
+ *
+ * @param buffer The buffer to sign
+ * @param bufferLength The buffer size
+ * @param params The signing parameters.
+ * @return a SignatureValue TLV block
+ * @throw Error signing fails
+ * @see SigningInfo
+ */
+ Block
+ sign(const uint8_t* buffer, size_t bufferLength, const SigningInfo& params = getDefaultSigningInfo());
+
+public: // export & import
+ /**
+ * @brief export a certificate of name @p certificateName and its corresponding private key.
+ *
+ * @param certificate The certificate to export.
+ * @param pw The password to secure the private key.
+ * @param pwLen The length of password.
+ * @return A SafeBag carrying the certificate and encrypted private key.
+ * @throw Error the certificate or private key does not exist
+ */
+ shared_ptr<SafeBag>
+ exportSafeBag(const Certificate& certificate, const char* pw, size_t pwLen);
+
+ /**
+ * @brief Import a pair of certificate and its corresponding private key encapsulated in a SafeBag.
+ *
+ * If the certificate and key are imported properly, the default setting will be updated as if
+ * a new key and certificate is added into KeyChain.
+ *
+ * @param safeBag The encoded data to import.
+ * @param pw The password to secure the private key.
+ * @param pwLen The length of password.
+ * @throw Error any of following conditions:
+ * - the safebag cannot be decoded or its content does not match;
+ * - private key cannot be imported;
+ * - a private/public key of the same name already exists;
+ * - a certificate of the same name already exists.
+ */
+ void
+ importSafeBag(const SafeBag& safeBag, const char* pw, size_t pwLen);
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /**
+ * @brief Derive SignatureTypeValue according to key type and digest algorithm.
+ */
+ static tlv::SignatureTypeValue
+ getSignatureType(KeyType keyType, DigestAlgorithm digestAlgorithm);
+
+public: // PIB & TPM backend registry
+ /**
+ * @brief Register a new PIB backend
+ * @param scheme Name for the registered PIB backend scheme
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+ template<class PibBackendType>
+ static void
+ registerPibBackend(const std::string& scheme);
+
+ /**
+ * @brief Register a new TPM backend
+ * @param scheme Name for the registered TPM backend scheme
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+ template<class TpmBackendType>
+ static void
+ registerTpmBackend(const std::string& scheme);
+
+private:
+ typedef std::map<std::string, function<unique_ptr<pib::PibImpl>(const std::string& location)>> PibFactories;
+ typedef std::map<std::string, function<unique_ptr<tpm::BackEnd>(const std::string& location)>> TpmFactories;
+
+ static PibFactories&
+ getPibFactories();
+
+ static TpmFactories&
+ getTpmFactories();
+
+ static std::tuple<std::string/*type*/, std::string/*location*/>
+ parseAndCheckPibLocator(const std::string& pibLocator);
+
+ static std::tuple<std::string/*type*/, std::string/*location*/>
+ parseAndCheckTpmLocator(const std::string& tpmLocator);
+
+ static const std::string&
+ getDefaultPibScheme();
+
+ static const std::string&
+ getDefaultTpmScheme();
+
+ /**
+ * @brief Create a PIB according to @p pibLocator
+ */
+ static unique_ptr<Pib>
+ createPib(const std::string& pibLocator);
+
+ /**
+ * @brief Create a TPM according to @p tpmLocator
+ */
+ static unique_ptr<Tpm>
+ createTpm(const std::string& tpmLocator);
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ static const std::string&
+ getDefaultPibLocator();
+
+ static const std::string&
+ getDefaultTpmLocator();
+
+private: // signing
+ /**
+ * @brief Generate a self-signed certificate for a public key.
+ *
+ * The self-signed certificate will also be added into PIB
+ *
+ * @param keyName The name of the public key
+ * @return The generated certificate
+ */
+ Certificate
+ selfSign(Key& key);
+
+ /**
+ * @brief Prepare a SignatureInfo TLV according to signing information and return the signing
+ * key name
+ *
+ * @param sigInfo The SignatureInfo to prepare.
+ * @param params The signing parameters.
+ * @return The signing key name and prepared SignatureInfo.
+ * @throw InvalidSigningInfoError when the requested signing method cannot be satisfied.
+ */
+ std::tuple<Name, SignatureInfo>
+ prepareSignatureInfo(const SigningInfo& params);
+
+ /**
+ * @brief Generate a SignatureValue block for a buffer @p buf with size @p size using
+ * a key with name @p keyName and digest algorithm @p digestAlgorithm.
+ */
+ Block
+ sign(const uint8_t* buf, size_t size, const Name& keyName, DigestAlgorithm digestAlgorithm) const;
+
+public:
+ static const SigningInfo&
+ getDefaultSigningInfo();
+
+ static const KeyParams&
+ getDefaultKeyParams();
+
+private:
+ std::unique_ptr<Pib> m_pib;
+ std::unique_ptr<Tpm> m_tpm;
+
+ static std::string s_defaultPibLocator;
+ static std::string s_defaultTpmLocator;
+};
+
+template<class PibType>
+inline void
+KeyChain::registerPibBackend(const std::string& scheme)
+{
+ getPibFactories().emplace(scheme, [] (const std::string& locator) {
+ return unique_ptr<pib::PibImpl>(new PibType(locator));
+ });
+}
+
+template<class TpmType>
+inline void
+KeyChain::registerTpmBackend(const std::string& scheme)
+{
+ getTpmFactories().emplace(scheme, [] (const std::string& locator) {
+ return unique_ptr<tpm::BackEnd>(new TpmType(locator));
+ });
+}
+
+/**
+ * @brief Register Pib backend class in KeyChain
+ *
+ * This macro should be placed once in the implementation file of the
+ * Pib backend class within the namespace where the type is declared.
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+#define NDN_CXX_V2_KEYCHAIN_REGISTER_PIB_BACKEND(PibType) \
+static class NdnCxxAuto ## PibType ## PibRegistrationClass \
+{ \
+public: \
+ NdnCxxAuto ## PibType ## PibRegistrationClass() \
+ { \
+ ::ndn::security::v2::KeyChain::registerPibBackend<PibType>(PibType::getScheme()); \
+ } \
+} ndnCxxAuto ## PibType ## PibRegistrationVariable
+
+/**
+ * @brief Register Tpm backend class in KeyChain
+ *
+ * This macro should be placed once in the implementation file of the
+ * Tpm backend class within the namespace where the type is declared.
+ *
+ * @note This interface is implementation detail and may change without notice.
+ */
+#define NDN_CXX_V2_KEYCHAIN_REGISTER_TPM_BACKEND(TpmType) \
+static class NdnCxxAuto ## TpmType ## TpmRegistrationClass \
+{ \
+public: \
+ NdnCxxAuto ## TpmType ## TpmRegistrationClass() \
+ { \
+ ::ndn::security::v2::KeyChain::registerTpmBackend<TpmType>(TpmType::getScheme()); \
+ } \
+} ndnCxxAuto ## TpmType ## TpmRegistrationVariable
+
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_SECURITY_V2_KEY_CHAIN_HPP