blob: e564326659ca60e535f8ffdc6690907bd3208f18 [file] [log] [blame]
/* -*- 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 "../security-common.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:
* EC 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:
* EC 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
*/
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
using security::v2::KeyChain;
} // namespace ndn
#endif // NDN_SECURITY_V2_KEY_CHAIN_HPP