| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2013-2022 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. |
| */ |
| |
| #include "ndn-cxx/security/key-chain.hpp" |
| #include "ndn-cxx/security/signing-helpers.hpp" |
| #include "ndn-cxx/security/verification-helpers.hpp" |
| |
| #include "ndn-cxx/security/pib/impl/pib-memory.hpp" |
| #include "ndn-cxx/security/pib/impl/pib-sqlite3.hpp" |
| |
| #include "ndn-cxx/security/tpm/impl/back-end-file.hpp" |
| #include "ndn-cxx/security/tpm/impl/back-end-mem.hpp" |
| #ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS |
| #include "ndn-cxx/security/tpm/impl/back-end-osx.hpp" |
| #endif // NDN_CXX_HAVE_OSX_FRAMEWORKS |
| |
| #include "ndn-cxx/security/transform/buffer-source.hpp" |
| #include "ndn-cxx/security/transform/digest-filter.hpp" |
| #include "ndn-cxx/security/transform/private-key.hpp" |
| #include "ndn-cxx/security/transform/public-key.hpp" |
| #include "ndn-cxx/security/transform/stream-sink.hpp" |
| |
| #include "ndn-cxx/encoding/buffer-stream.hpp" |
| #include "ndn-cxx/util/config-file.hpp" |
| #include "ndn-cxx/util/logger.hpp" |
| #include "ndn-cxx/util/random.hpp" |
| |
| #include <boost/lexical_cast.hpp> |
| #include <cstdlib> // for std::getenv() |
| |
| namespace ndn { |
| namespace security { |
| |
| // When static library is used, not everything is compiled into the resulting binary. |
| // Therefore, the following standard PIB and TPMs need to be registered here. |
| // http://stackoverflow.com/q/9459980/2150331 |
| |
| namespace pib { |
| NDN_CXX_KEYCHAIN_REGISTER_PIB_BACKEND(PibSqlite3); |
| NDN_CXX_KEYCHAIN_REGISTER_PIB_BACKEND(PibMemory); |
| } // namespace pib |
| |
| namespace tpm { |
| #ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS |
| NDN_CXX_KEYCHAIN_REGISTER_TPM_BACKEND(BackEndOsx); |
| #endif // NDN_CXX_HAVE_OSX_FRAMEWORKS |
| NDN_CXX_KEYCHAIN_REGISTER_TPM_BACKEND(BackEndFile); |
| NDN_CXX_KEYCHAIN_REGISTER_TPM_BACKEND(BackEndMem); |
| } // namespace tpm |
| |
| inline namespace v2 { |
| |
| NDN_LOG_INIT(ndn.security.KeyChain); |
| |
| const name::Component SELF("self"); |
| |
| KeyChain::PibFactories& |
| KeyChain::getPibFactories() |
| { |
| static PibFactories pibFactories; |
| return pibFactories; |
| } |
| |
| KeyChain::TpmFactories& |
| KeyChain::getTpmFactories() |
| { |
| static TpmFactories tpmFactories; |
| return tpmFactories; |
| } |
| |
| static const auto& |
| getDefaultPibScheme() |
| { |
| return pib::PibSqlite3::getScheme(); |
| } |
| |
| static const auto& |
| getDefaultTpmScheme() |
| { |
| #if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS) && defined(NDN_CXX_WITH_OSX_KEYCHAIN) |
| return tpm::BackEndOsx::getScheme(); |
| #else |
| return tpm::BackEndFile::getScheme(); |
| #endif // defined(NDN_CXX_HAVE_OSX_FRAMEWORKS) && defined(NDN_CXX_WITH_OSX_KEYCHAIN) |
| } |
| |
| const KeyParams& |
| KeyChain::getDefaultKeyParams() |
| { |
| static EcKeyParams keyParams; |
| return keyParams; |
| } |
| |
| // |
| |
| class KeyChain::Locator |
| { |
| public: |
| NDN_CXX_NODISCARD bool |
| empty() const |
| { |
| return scheme.empty(); |
| } |
| |
| NDN_CXX_NODISCARD std::string |
| canonical() const |
| { |
| return scheme + ':' + location; |
| } |
| |
| friend bool |
| operator==(const Locator& lhs, const Locator& rhs) |
| { |
| return lhs.scheme == rhs.scheme && lhs.location == rhs.location; |
| } |
| |
| public: |
| std::string scheme; |
| std::string location; |
| }; |
| |
| KeyChain::Locator KeyChain::s_defaultPibLocator; |
| KeyChain::Locator KeyChain::s_defaultTpmLocator; |
| |
| // |
| |
| KeyChain::KeyChain() |
| : KeyChain(getDefaultPibLocator(), getDefaultTpmLocator(), true) |
| { |
| } |
| |
| KeyChain::KeyChain(const std::string& pibLocator, const std::string& tpmLocator, bool allowReset) |
| : KeyChain(parseAndCheckPibLocator(pibLocator), |
| parseAndCheckTpmLocator(tpmLocator), |
| allowReset) |
| { |
| } |
| |
| KeyChain::KeyChain(Locator pibLocator, Locator tpmLocator, bool allowReset) |
| { |
| // Create PIB |
| auto pibFactory = getPibFactories().find(pibLocator.scheme); |
| BOOST_ASSERT(pibFactory != getPibFactories().end()); |
| m_pib.reset(new Pib(pibLocator.canonical(), pibFactory->second(pibLocator.location))); |
| |
| // Figure out the TPM Locator |
| std::string oldTpmLocator = m_pib->getTpmLocator(); |
| if (pibLocator == getDefaultPibLocator()) { |
| // Default PIB must use default TPM |
| if (!oldTpmLocator.empty() && oldTpmLocator != getDefaultTpmLocator().canonical()) { |
| m_pib->reset(); |
| tpmLocator = getDefaultTpmLocator(); |
| } |
| } |
| else { |
| // non-default PIB check consistency |
| if (!oldTpmLocator.empty() && oldTpmLocator != tpmLocator.canonical()) { |
| if (allowReset) |
| m_pib->reset(); |
| else |
| NDN_THROW(LocatorMismatchError("Supplied TPM locator (" + tpmLocator.canonical() + |
| ") does not match TPM locator in PIB (" + oldTpmLocator + ")")); |
| } |
| } |
| // Note that key mismatch may still happen if the TPM locator is initially set to a |
| // wrong one or if the PIB was shared by more than one TPMs before. This is due to the |
| // old PIB not having TPM info, the new PIB should not have this problem. |
| |
| // Create TPM |
| auto tpmFactory = getTpmFactories().find(tpmLocator.scheme); |
| BOOST_ASSERT(tpmFactory != getTpmFactories().end()); |
| m_tpm.reset(new Tpm(tpmLocator.canonical(), tpmFactory->second(tpmLocator.location))); |
| |
| // Link PIB with TPM |
| m_pib->setTpmLocator(tpmLocator.canonical()); |
| } |
| |
| KeyChain::~KeyChain() = default; |
| |
| // public: management |
| |
| Identity |
| KeyChain::createIdentity(const Name& identityName, const KeyParams& params) |
| { |
| NDN_LOG_DEBUG("Requesting creation of identity " << identityName); |
| Identity id = m_pib->addIdentity(identityName); |
| |
| Key key; |
| try { |
| key = id.getDefaultKey(); |
| } |
| catch (const Pib::Error&) { |
| key = createKey(id, params); |
| } |
| |
| try { |
| key.getDefaultCertificate(); |
| } |
| catch (const Pib::Error&) { |
| NDN_LOG_DEBUG("No default certificate for " << key << ", requesting self-signing"); |
| selfSign(key); |
| } |
| |
| return id; |
| } |
| |
| void |
| KeyChain::deleteIdentity(const Identity& identity) |
| { |
| if (!identity) { |
| return; |
| } |
| |
| Name identityName = identity.getName(); |
| NDN_LOG_DEBUG("Requesting deletion of identity " << identityName); |
| |
| for (const auto& key : identity.getKeys()) { |
| m_tpm->deleteKey(key.getName()); |
| } |
| |
| m_pib->removeIdentity(identityName); |
| } |
| |
| void |
| KeyChain::setDefaultIdentity(const Identity& identity) |
| { |
| BOOST_ASSERT(identity); |
| |
| m_pib->setDefaultIdentity(identity.getName()); |
| } |
| |
| Key |
| KeyChain::createKey(const Identity& identity, const KeyParams& params) |
| { |
| BOOST_ASSERT(identity); |
| |
| // create key in TPM |
| Name keyName = m_tpm->createKey(identity.getName(), params); |
| |
| // set up key info in PIB |
| Key key = identity.addKey(*m_tpm->getPublicKey(keyName), keyName); |
| |
| NDN_LOG_DEBUG("Requesting self-signing for newly created key " << key); |
| selfSign(key); |
| |
| return key; |
| } |
| |
| Name |
| KeyChain::createHmacKey(const Name& prefix, const HmacKeyParams& params) |
| { |
| return m_tpm->createKey(prefix, params); |
| } |
| |
| void |
| KeyChain::deleteKey(const Identity& identity, const Key& key) |
| { |
| BOOST_ASSERT(identity); |
| if (!key) { |
| return; |
| } |
| |
| Name keyName = key.getName(); |
| identity.removeKey(keyName); |
| m_tpm->deleteKey(keyName); |
| } |
| |
| void |
| KeyChain::setDefaultKey(const Identity& identity, const Key& key) |
| { |
| BOOST_ASSERT(identity); |
| BOOST_ASSERT(key); |
| |
| identity.setDefaultKey(key.getName()); |
| } |
| |
| void |
| KeyChain::addCertificate(const Key& key, const Certificate& certificate) |
| { |
| BOOST_ASSERT(key); |
| |
| key.addCertificate(certificate); |
| } |
| |
| void |
| KeyChain::deleteCertificate(const Key& key, const Name& certName) |
| { |
| BOOST_ASSERT(key); |
| |
| key.removeCertificate(certName); |
| } |
| |
| void |
| KeyChain::setDefaultCertificate(const Key& key, const Certificate& cert) |
| { |
| BOOST_ASSERT(key); |
| |
| key.setDefaultCertificate(cert); |
| } |
| |
| shared_ptr<SafeBag> |
| KeyChain::exportSafeBag(const Certificate& certificate, const char* pw, size_t pwLen) |
| { |
| Name keyName = certificate.getKeyName(); |
| |
| ConstBufferPtr encryptedKey; |
| try { |
| encryptedKey = m_tpm->exportPrivateKey(keyName, pw, pwLen); |
| } |
| catch (const Tpm::Error&) { |
| NDN_THROW_NESTED(Error("Failed to export private key `" + keyName.toUri() + "`")); |
| } |
| |
| return make_shared<SafeBag>(certificate, *encryptedKey); |
| } |
| |
| void |
| KeyChain::importSafeBag(const SafeBag& safeBag, const char* pw, size_t pwLen) |
| { |
| Certificate cert(safeBag.getCertificate()); |
| Name identity = cert.getIdentity(); |
| Name keyName = cert.getKeyName(); |
| |
| // check if private key already exists |
| if (m_tpm->hasKey(keyName)) { |
| NDN_THROW(Error("Private key `" + keyName.toUri() + "` already exists")); |
| } |
| |
| // check if public key already exists |
| try { |
| m_pib->getIdentity(identity).getKey(keyName); |
| NDN_THROW(Error("Public key `" + keyName.toUri() + "` already exists")); |
| } |
| catch (const Pib::Error&) { |
| // Either identity or key doesn't exist. OK to import. |
| } |
| |
| try { |
| m_tpm->importPrivateKey(keyName, safeBag.getEncryptedKey(), pw, pwLen); |
| } |
| catch (const Tpm::Error&) { |
| NDN_THROW_NESTED(Error("Failed to import private key `" + keyName.toUri() + "`")); |
| } |
| |
| // check the consistency of private key and certificate (sign/verify a random message) |
| const auto r = random::generateWord64(); |
| const auto msg = make_span(reinterpret_cast<const uint8_t*>(&r), sizeof(r)); |
| ConstBufferPtr sigBits; |
| try { |
| sigBits = m_tpm->sign({msg}, keyName, DigestAlgorithm::SHA256); |
| } |
| catch (const std::runtime_error&) { |
| m_tpm->deleteKey(keyName); |
| NDN_THROW(Error("Invalid private key `" + keyName.toUri() + "`")); |
| } |
| if (!verifySignature({msg}, *sigBits, cert.getPublicKey())) { |
| m_tpm->deleteKey(keyName); |
| NDN_THROW(Error("Certificate `" + cert.getName().toUri() + "` " |
| "and private key `" + keyName.toUri() + "` do not match")); |
| } |
| |
| Identity id = m_pib->addIdentity(identity); |
| Key key = id.addKey(cert.getPublicKey(), keyName); |
| key.addCertificate(cert); |
| } |
| |
| void |
| KeyChain::importPrivateKey(const Name& keyName, shared_ptr<transform::PrivateKey> key) |
| { |
| if (m_tpm->hasKey(keyName)) { |
| NDN_THROW(Error("Private key `" + keyName.toUri() + "` already exists")); |
| } |
| |
| try { |
| m_tpm->importPrivateKey(keyName, std::move(key)); |
| } |
| catch (const Tpm::Error&) { |
| NDN_THROW_NESTED(Error("Failed to import private key `" + keyName.toUri() + "`")); |
| } |
| } |
| |
| // public: signing |
| |
| void |
| KeyChain::sign(Data& data, const SigningInfo& params) |
| { |
| Name keyName; |
| SignatureInfo sigInfo; |
| std::tie(keyName, sigInfo) = prepareSignatureInfo(params); |
| |
| data.setSignatureInfo(sigInfo); |
| |
| EncodingBuffer encoder; |
| data.wireEncode(encoder, true); |
| |
| auto sigValue = sign({encoder}, keyName, params.getDigestAlgorithm()); |
| data.wireEncode(encoder, *sigValue); |
| } |
| |
| void |
| KeyChain::sign(Interest& interest, const SigningInfo& params) |
| { |
| Name keyName; |
| SignatureInfo sigInfo; |
| std::tie(keyName, sigInfo) = prepareSignatureInfo(params); |
| |
| if (params.getSignedInterestFormat() == SignedInterestFormat::V03) { |
| interest.setSignatureInfo(sigInfo); |
| |
| // Extract function will throw if not all necessary elements are present in Interest |
| auto sigValue = sign(interest.extractSignedRanges(), keyName, params.getDigestAlgorithm()); |
| interest.setSignatureValue(std::move(sigValue)); |
| } |
| else { |
| Name signedName = interest.getName(); |
| |
| // We encode in Data format because this is the format used prior to Packet Specification v0.3 |
| const auto& sigInfoBlock = sigInfo.wireEncode(SignatureInfo::Type::Data); |
| signedName.append(sigInfoBlock); // SignatureInfo |
| |
| Block sigValue(tlv::SignatureValue, |
| sign({signedName.wireEncode().value_bytes()}, keyName, params.getDigestAlgorithm())); |
| sigValue.encode(); |
| signedName.append(sigValue); // SignatureValue |
| |
| interest.setName(signedName); |
| } |
| } |
| |
| Certificate |
| KeyChain::makeCertificate(const pib::Key& publicKey, const SigningInfo& params, |
| const MakeCertificateOptions& opts) |
| { |
| return makeCertificate(publicKey.getName(), publicKey.getPublicKey(), params, opts); |
| } |
| |
| Certificate |
| KeyChain::makeCertificate(const Certificate& certRequest, const SigningInfo& params, |
| const MakeCertificateOptions& opts) |
| { |
| auto pkcs8 = certRequest.getPublicKey(); |
| try { |
| transform::PublicKey pub; |
| pub.loadPkcs8(pkcs8); |
| } |
| catch (const transform::PublicKey::Error&) { |
| NDN_THROW_NESTED(std::invalid_argument("Certificate request contains invalid public key")); |
| } |
| |
| return makeCertificate(extractKeyNameFromCertName(certRequest.getName()), pkcs8, params, opts); |
| } |
| |
| // private: PIB/TPM locator helpers |
| |
| static std::tuple<std::string/*scheme*/, std::string/*location*/> |
| parseLocatorUri(const std::string& uri) |
| { |
| auto pos = uri.find(':'); |
| if (pos != std::string::npos) { |
| return {uri.substr(0, pos), uri.substr(pos + 1)}; |
| } |
| else { |
| return {uri, ""}; |
| } |
| } |
| |
| KeyChain::Locator |
| KeyChain::parseAndCheckPibLocator(const std::string& pibLocator) |
| { |
| std::string pibScheme, pibLocation; |
| std::tie(pibScheme, pibLocation) = parseLocatorUri(pibLocator); |
| |
| if (pibScheme.empty()) { |
| pibScheme = getDefaultPibScheme(); |
| } |
| |
| auto pibFactory = getPibFactories().find(pibScheme); |
| if (pibFactory == getPibFactories().end()) { |
| NDN_THROW(Error("PIB scheme `" + pibScheme + "` is not supported")); |
| } |
| |
| return {pibScheme, pibLocation}; |
| } |
| |
| KeyChain::Locator |
| KeyChain::parseAndCheckTpmLocator(const std::string& tpmLocator) |
| { |
| std::string tpmScheme, tpmLocation; |
| std::tie(tpmScheme, tpmLocation) = parseLocatorUri(tpmLocator); |
| |
| if (tpmScheme.empty()) { |
| tpmScheme = getDefaultTpmScheme(); |
| } |
| |
| auto tpmFactory = getTpmFactories().find(tpmScheme); |
| if (tpmFactory == getTpmFactories().end()) { |
| NDN_THROW(Error("TPM scheme `" + tpmScheme + "` is not supported")); |
| } |
| |
| return {tpmScheme, tpmLocation}; |
| } |
| |
| const KeyChain::Locator& |
| KeyChain::getDefaultPibLocator() |
| { |
| if (!s_defaultPibLocator.empty()) |
| return s_defaultPibLocator; |
| |
| std::string input; |
| const char* pibEnv = std::getenv("NDN_CLIENT_PIB"); |
| if (pibEnv != nullptr) { |
| input = pibEnv; |
| } |
| else { |
| ConfigFile config; |
| input = config.getParsedConfiguration().get<std::string>("pib", getDefaultPibScheme()); |
| } |
| |
| s_defaultPibLocator = parseAndCheckPibLocator(input); |
| BOOST_ASSERT(!s_defaultPibLocator.empty()); |
| return s_defaultPibLocator; |
| } |
| |
| const KeyChain::Locator& |
| KeyChain::getDefaultTpmLocator() |
| { |
| if (!s_defaultTpmLocator.empty()) |
| return s_defaultTpmLocator; |
| |
| std::string input; |
| const char* tpmEnv = std::getenv("NDN_CLIENT_TPM"); |
| if (tpmEnv != nullptr) { |
| input = tpmEnv; |
| } |
| else { |
| ConfigFile config; |
| input = config.getParsedConfiguration().get<std::string>("tpm", getDefaultTpmScheme()); |
| } |
| |
| s_defaultTpmLocator = parseAndCheckTpmLocator(input); |
| BOOST_ASSERT(!s_defaultTpmLocator.empty()); |
| return s_defaultTpmLocator; |
| } |
| |
| #ifdef NDN_CXX_HAVE_TESTS |
| void |
| KeyChain::resetDefaultLocators() |
| { |
| s_defaultPibLocator = {}; |
| s_defaultTpmLocator = {}; |
| } |
| #endif |
| |
| // private: signing |
| |
| Certificate |
| KeyChain::makeCertificate(const Name& keyName, span<const uint8_t> publicKey, |
| SigningInfo params, const MakeCertificateOptions& opts) |
| { |
| if (opts.freshnessPeriod <= 0_ms) { |
| // We cannot rely on Certificate constructor to check this, because |
| // it throws Certificate::Error, not std::invalid_argument |
| NDN_THROW(std::invalid_argument("FreshnessPeriod is not positive")); |
| } |
| |
| Name name(keyName); |
| name.append(opts.issuerId); |
| name.appendVersion(opts.version); |
| |
| Data data; |
| data.setName(name); |
| data.setContentType(tlv::ContentType_Key); |
| data.setFreshnessPeriod(opts.freshnessPeriod); |
| data.setContent(publicKey); |
| |
| auto sigInfo = params.getSignatureInfo(); |
| // Call ValidityPeriod::makeRelative here instead of in MakeCertificateOptions struct |
| // because the caller may prepare MakeCertificateOptions first and call makeCertificate |
| // at a later time. |
| sigInfo.setValidityPeriod(opts.validity.value_or(ValidityPeriod::makeRelative(-1_s, 365_days))); |
| params.setSignatureInfo(sigInfo); |
| |
| sign(data, params); |
| // let Certificate constructor double-check correctness of this function |
| return Certificate(std::move(data)); |
| } |
| |
| Certificate |
| KeyChain::selfSign(Key& key) |
| { |
| MakeCertificateOptions opts; |
| opts.issuerId = SELF; |
| // Note time::system_clock::max() or other NotAfter date results in incorrect encoded value |
| // because of overflow during conversion to boost::posix_time::ptime (bug #3915, bug #5176). |
| opts.validity = ValidityPeriod::makeRelative(-1_s, 20 * 365_days); |
| auto cert = makeCertificate(key, signingByKey(key), opts); |
| |
| key.addCertificate(cert); |
| return cert; |
| } |
| |
| std::tuple<Name, SignatureInfo> |
| KeyChain::prepareSignatureInfo(const SigningInfo& params) |
| { |
| switch (params.getSignerType()) { |
| case SigningInfo::SIGNER_TYPE_NULL: { |
| pib::Identity identity; |
| try { |
| identity = m_pib->getDefaultIdentity(); |
| } |
| catch (const Pib::Error&) { // no default identity, use sha256 for signing. |
| return prepareSignatureInfoSha256(params); |
| } |
| return prepareSignatureInfoWithIdentity(params, identity); |
| } |
| case SigningInfo::SIGNER_TYPE_ID: { |
| auto identity = params.getPibIdentity(); |
| if (!identity) { |
| auto identityName = params.getSignerName(); |
| try { |
| identity = m_pib->getIdentity(identityName); |
| } |
| catch (const Pib::Error&) { |
| NDN_THROW_NESTED(InvalidSigningInfoError("Signing identity `" + |
| identityName.toUri() + "` does not exist")); |
| } |
| } |
| if (!identity) { |
| NDN_THROW(InvalidSigningInfoError("Cannot determine signing parameters")); |
| } |
| return prepareSignatureInfoWithIdentity(params, identity); |
| } |
| case SigningInfo::SIGNER_TYPE_KEY: { |
| auto key = params.getPibKey(); |
| if (!key) { |
| auto keyName = params.getSignerName(); |
| auto identityName = extractIdentityFromKeyName(keyName); |
| try { |
| key = m_pib->getIdentity(identityName).getKey(keyName); |
| } |
| catch (const Pib::Error&) { |
| NDN_THROW_NESTED(InvalidSigningInfoError("Signing key `" + |
| keyName.toUri() + "` does not exist")); |
| } |
| } |
| if (!key) { |
| NDN_THROW(InvalidSigningInfoError("Cannot determine signing parameters")); |
| } |
| return prepareSignatureInfoWithKey(params, key); |
| } |
| case SigningInfo::SIGNER_TYPE_CERT: { |
| auto certName = params.getSignerName(); |
| auto keyName = extractKeyNameFromCertName(certName); |
| auto identityName = extractIdentityFromCertName(certName); |
| pib::Key key; |
| try { |
| key = m_pib->getIdentity(identityName).getKey(keyName); |
| } |
| catch (const Pib::Error&) { |
| NDN_THROW_NESTED(InvalidSigningInfoError("Signing certificate `" + |
| certName.toUri() + "` does not exist")); |
| } |
| return prepareSignatureInfoWithKey(params, key, certName); |
| } |
| case SigningInfo::SIGNER_TYPE_SHA256: { |
| return prepareSignatureInfoSha256(params); |
| } |
| case SigningInfo::SIGNER_TYPE_HMAC: { |
| return prepareSignatureInfoHmac(params, *m_tpm); |
| } |
| } |
| NDN_THROW(InvalidSigningInfoError("Unrecognized signer type " + |
| to_string(params.getSignerType()))); |
| } |
| |
| std::tuple<Name, SignatureInfo> |
| KeyChain::prepareSignatureInfoSha256(const SigningInfo& params) |
| { |
| auto sigInfo = params.getSignatureInfo(); |
| sigInfo.setSignatureType(tlv::DigestSha256); |
| |
| NDN_LOG_TRACE("Prepared signature info: " << sigInfo); |
| return {SigningInfo::getDigestSha256Identity(), sigInfo}; |
| } |
| |
| std::tuple<Name, SignatureInfo> |
| KeyChain::prepareSignatureInfoHmac(const SigningInfo& params, Tpm& tpm) |
| { |
| const Name& keyName = params.getSignerName(); |
| if (!tpm.hasKey(keyName)) { |
| tpm.importPrivateKey(keyName, params.getHmacKey()); |
| } |
| |
| auto sigInfo = params.getSignatureInfo(); |
| sigInfo.setSignatureType(getSignatureType(KeyType::HMAC, params.getDigestAlgorithm())); |
| sigInfo.setKeyLocator(keyName); |
| |
| NDN_LOG_TRACE("Prepared signature info: " << sigInfo); |
| return {keyName, sigInfo}; |
| } |
| |
| std::tuple<Name, SignatureInfo> |
| KeyChain::prepareSignatureInfoWithIdentity(const SigningInfo& params, const pib::Identity& identity) |
| { |
| pib::Key key; |
| try { |
| key = identity.getDefaultKey(); |
| } |
| catch (const Pib::Error&) { |
| NDN_THROW_NESTED(InvalidSigningInfoError("Signing identity `" + identity.getName().toUri() + |
| "` does not have a default key")); |
| } |
| return prepareSignatureInfoWithKey(params, key); |
| } |
| |
| std::tuple<Name, SignatureInfo> |
| KeyChain::prepareSignatureInfoWithKey(const SigningInfo& params, const pib::Key& key, |
| const optional<Name>& certName) |
| { |
| auto sigInfo = params.getSignatureInfo(); |
| sigInfo.setSignatureType(getSignatureType(key.getKeyType(), params.getDigestAlgorithm())); |
| if (!sigInfo.hasKeyLocator()) { |
| if (certName) { |
| sigInfo.setKeyLocator(certName); |
| } |
| else { |
| Name klName = key.getName(); |
| try { |
| klName = key.getDefaultCertificate().getName(); |
| } |
| catch (const Pib::Error&) { |
| } |
| sigInfo.setKeyLocator(klName); |
| } |
| } |
| |
| NDN_LOG_TRACE("Prepared signature info: " << sigInfo); |
| return {key.getName(), sigInfo}; |
| } |
| |
| ConstBufferPtr |
| KeyChain::sign(const InputBuffers& bufs, const Name& keyName, DigestAlgorithm digestAlgorithm) const |
| { |
| using namespace transform; |
| |
| if (keyName == SigningInfo::getDigestSha256Identity()) { |
| OBufferStream os; |
| bufferSource(bufs) >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os); |
| return os.buf(); |
| } |
| |
| auto signature = m_tpm->sign(bufs, keyName, digestAlgorithm); |
| if (!signature) { |
| NDN_THROW(InvalidSigningInfoError("TPM signing failed for key `" + keyName.toUri() + "` " |
| "(e.g., PIB contains info about the key, but TPM is missing " |
| "the corresponding private key)")); |
| } |
| |
| return signature; |
| } |
| |
| tlv::SignatureTypeValue |
| KeyChain::getSignatureType(KeyType keyType, DigestAlgorithm) |
| { |
| switch (keyType) { |
| case KeyType::RSA: |
| return tlv::SignatureSha256WithRsa; |
| case KeyType::EC: |
| return tlv::SignatureSha256WithEcdsa; |
| case KeyType::HMAC: |
| return tlv::SignatureHmacWithSha256; |
| default: |
| NDN_THROW(Error("Unsupported key type " + boost::lexical_cast<std::string>(keyType))); |
| } |
| } |
| |
| } // inline namespace v2 |
| } // namespace security |
| } // namespace ndn |