blob: bcedeb5da80b7c1622aee08223b6ad34d8758c95 [file] [log] [blame]
/* -*- 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