blob: 1d9f100d6fd23f834648fec8c5dc0d4b80f840cf [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2013-2014 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.
*
* @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
*/
#include "key-chain.hpp"
#include "sec-public-info-sqlite3.hpp"
#include "sec-tpm-file.hpp"
#ifdef NDN_CXX_HAVE_OSX_SECURITY
#include "sec-tpm-osx.hpp"
#endif
#include "../util/random.hpp"
#include "../util/config-file.hpp"
namespace ndn {
// Use a GUID as a magic number of KeyChain::DEFAULT_PREFIX identifier
const Name KeyChain::DEFAULT_PREFIX("/723821fd-f534-44b3-80d9-44bf5f58bbbb");
const RsaKeyParams KeyChain::DEFAULT_KEY_PARAMS;
KeyChain::KeyChain()
: m_pib(0)
, m_tpm(0)
, m_lastTimestamp(time::toUnixTimestamp(time::system_clock::now()))
{
ConfigFile config;
const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
std::string pibName;
try
{
pibName = parsed.get<std::string>("pib");
}
catch (boost::property_tree::ptree_bad_path& error)
{
// pib is not specified, take the default
}
catch (boost::property_tree::ptree_bad_data& error)
{
throw ConfigFile::Error(error.what());
}
std::string tpmName;
try
{
tpmName = parsed.get<std::string>("tpm");
}
catch (boost::property_tree::ptree_bad_path& error)
{
// tpm is not specified, take the default
}
catch (boost::property_tree::ptree_bad_data& error)
{
throw ConfigFile::Error(error.what());
}
if (pibName.empty() || pibName == "sqlite3")
m_pib = new SecPublicInfoSqlite3;
else
throw Error("PIB type '" + pibName + "' is not supported");
if (tpmName.empty())
#if defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
m_tpm = new SecTpmOsx();
#else
m_tpm = new SecTpmFile();
#endif // defined(NDN_CXX_HAVE_OSX_SECURITY) and defined(NDN_CXX_WITH_OSX_KEYCHAIN)
else if (tpmName == "osx-keychain")
#if defined(NDN_CXX_HAVE_OSX_SECURITY)
m_tpm = new SecTpmOsx();
#else
throw Error("TPM type '" + tpmName + "' is not supported on this platform");
#endif // NDN_CXX_HAVE_OSX_SECURITY
else if (tpmName == "file")
m_tpm = new SecTpmFile();
else
throw Error("TPM type '" + tpmName + "' is not supported");
}
KeyChain::KeyChain(const std::string& pibName,
const std::string& tpmName)
: m_pib(0)
, m_tpm(0)
, m_lastTimestamp(time::toUnixTimestamp(time::system_clock::now()))
{
if (pibName == "sqlite3")
m_pib = new SecPublicInfoSqlite3;
else
throw Error("PIB type '" + pibName + "' is not supported");
if (tpmName == "file")
m_tpm = new SecTpmFile;
#if defined(NDN_CXX_HAVE_OSX_SECURITY)
else if (tpmName == "osx-keychain")
m_tpm = new SecTpmOsx();
#endif //NDN_CXX_HAVE_OSX_SECURITY
else
throw Error("TPM type '" + tpmName + "' is not supported");
}
KeyChain::~KeyChain()
{
if (m_pib != 0)
delete m_pib;
if (m_tpm != 0)
delete m_tpm;
}
Name
KeyChain::createIdentity(const Name& identityName, const KeyParams& params)
{
m_pib->addIdentity(identityName);
Name keyName;
try
{
keyName = m_pib->getDefaultKeyNameForIdentity(identityName);
shared_ptr<PublicKey> key = m_pib->getPublicKey(keyName);
if (key->getKeyType() != params.getKeyType())
{
keyName = generateKeyPair(identityName, true, params);
m_pib->setDefaultKeyNameForIdentity(keyName);
}
}
catch (SecPublicInfo::Error& e)
{
keyName = generateKeyPair(identityName, true, params);
m_pib->setDefaultKeyNameForIdentity(keyName);
}
Name certName;
try
{
certName = m_pib->getDefaultCertificateNameForKey(keyName);
}
catch (SecPublicInfo::Error& e)
{
shared_ptr<IdentityCertificate> selfCert = selfSign(keyName);
m_pib->addCertificateAsIdentityDefault(*selfCert);
certName = selfCert->getName();
}
return certName;
}
Name
KeyChain::generateRsaKeyPairAsDefault(const Name& identityName, bool isKsk, uint32_t keySize)
{
RsaKeyParams params(keySize);
Name keyName = generateKeyPair(identityName, isKsk, params);
m_pib->setDefaultKeyNameForIdentity(keyName);
return keyName;
}
Name
KeyChain::generateEcdsaKeyPairAsDefault(const Name& identityName, bool isKsk, uint32_t keySize)
{
EcdsaKeyParams params(keySize);
Name keyName = generateKeyPair(identityName, isKsk, params);
m_pib->setDefaultKeyNameForIdentity(keyName);
return keyName;
}
shared_ptr<IdentityCertificate>
KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
const Name& signingIdentity,
const time::system_clock::TimePoint& notBefore,
const time::system_clock::TimePoint& notAfter,
const std::vector<CertificateSubjectDescription>& subjectDescription,
const Name& certPrefix)
{
shared_ptr<PublicKey> publicKey;
try
{
publicKey = m_pib->getPublicKey(keyName);
}
catch (SecPublicInfo::Error& e)
{
return shared_ptr<IdentityCertificate>();
}
return prepareUnsignedIdentityCertificate(keyName, *publicKey, signingIdentity,
notBefore, notAfter,
subjectDescription, certPrefix);
}
shared_ptr<IdentityCertificate>
KeyChain::prepareUnsignedIdentityCertificate(const Name& keyName,
const PublicKey& publicKey,
const Name& signingIdentity,
const time::system_clock::TimePoint& notBefore,
const time::system_clock::TimePoint& notAfter,
const std::vector<CertificateSubjectDescription>& subjectDescription,
const Name& certPrefix)
{
if (keyName.size() < 1)
return shared_ptr<IdentityCertificate>();
std::string keyIdPrefix = keyName.get(-1).toUri().substr(0, 4);
if (keyIdPrefix != "ksk-" && keyIdPrefix != "dsk-")
return shared_ptr<IdentityCertificate>();
shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
Name certName;
if (certPrefix == KeyChain::DEFAULT_PREFIX)
{
// No certificate prefix hint, infer the prefix
if (signingIdentity.isPrefixOf(keyName))
certName.append(signingIdentity)
.append("KEY")
.append(keyName.getSubName(signingIdentity.size()))
.append("ID-CERT")
.appendVersion();
else
certName.append(keyName.getPrefix(-1))
.append("KEY")
.append(keyName.get(-1))
.append("ID-CERT")
.appendVersion();
}
else
{
// cert prefix hint is supplied, determine the cert name.
if (certPrefix.isPrefixOf(keyName) && certPrefix != keyName)
certName.append(certPrefix)
.append("KEY")
.append(keyName.getSubName(certPrefix.size()))
.append("ID-CERT")
.appendVersion();
else
return shared_ptr<IdentityCertificate>();
}
certificate->setName(certName);
certificate->setNotBefore(notBefore);
certificate->setNotAfter(notAfter);
certificate->setPublicKeyInfo(publicKey);
if (subjectDescription.empty())
{
CertificateSubjectDescription subjectName(oid::ATTRIBUTE_NAME, keyName.getPrefix(-1).toUri());
certificate->addSubjectDescription(subjectName);
}
else
{
std::vector<CertificateSubjectDescription>::const_iterator sdIt =
subjectDescription.begin();
std::vector<CertificateSubjectDescription>::const_iterator sdEnd =
subjectDescription.end();
for(; sdIt != sdEnd; sdIt++)
certificate->addSubjectDescription(*sdIt);
}
certificate->encode();
return certificate;
}
Signature
KeyChain::sign(const uint8_t* buffer, size_t bufferLength, const Name& certificateName)
{
shared_ptr<IdentityCertificate> certificate = m_pib->getCertificate(certificateName);
shared_ptr<SignatureWithPublicKey> sig =
determineSignatureWithPublicKey(certificate->getPublicKeyInfo().getKeyType());
if (!static_cast<bool>(sig))
throw SecTpm::Error("unknown key type");
sig->setKeyLocator(certificate->getName().getPrefix(-1));
// For temporary usage, we support SHA256 only, but will support more.
sig->setValue(m_tpm->signInTpm(buffer, bufferLength,
certificate->getPublicKeyName(),
DIGEST_ALGORITHM_SHA256));
return *sig;
}
shared_ptr<IdentityCertificate>
KeyChain::selfSign(const Name& keyName)
{
shared_ptr<PublicKey> pubKey;
try
{
pubKey = m_pib->getPublicKey(keyName); // may throw an exception.
}
catch (SecPublicInfo::Error& e)
{
return shared_ptr<IdentityCertificate>();
}
shared_ptr<IdentityCertificate> certificate = make_shared<IdentityCertificate>();
Name certificateName = keyName.getPrefix(-1);
certificateName.append("KEY").append(keyName.get(-1)).append("ID-CERT").appendVersion();
certificate->setName(certificateName);
certificate->setNotBefore(time::system_clock::now());
certificate->setNotAfter(time::system_clock::now() + time::days(7300)); // ~20 years
certificate->setPublicKeyInfo(*pubKey);
certificate->addSubjectDescription(CertificateSubjectDescription(oid::ATTRIBUTE_NAME,
keyName.toUri()));
certificate->encode();
selfSign(*certificate);
return certificate;
}
void
KeyChain::selfSign(IdentityCertificate& cert)
{
Name keyName = IdentityCertificate::certificateNameToPublicKeyName(cert.getName());
if (!m_tpm->doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
throw SecTpm::Error("Private key does not exist");
shared_ptr<SignatureWithPublicKey> sig =
determineSignatureWithPublicKey(cert.getPublicKeyInfo().getKeyType());
if (!static_cast<bool>(sig))
throw SecTpm::Error("unknown key type");
sig->setKeyLocator(cert.getName().getPrefix(-1)); // implicit conversion should take care
signPacketWrapper(cert, *sig, keyName, DIGEST_ALGORITHM_SHA256);
}
shared_ptr<SecuredBag>
KeyChain::exportIdentity(const Name& identity, const std::string& passwordStr)
{
if (!m_pib->doesIdentityExist(identity))
throw SecPublicInfo::Error("Identity does not exist");
Name keyName = m_pib->getDefaultKeyNameForIdentity(identity);
ConstBufferPtr pkcs5;
try
{
pkcs5 = m_tpm->exportPrivateKeyPkcs5FromTpm(keyName, passwordStr);
}
catch (SecTpm::Error& e)
{
throw SecPublicInfo::Error("Fail to export PKCS5 of private key");
}
shared_ptr<IdentityCertificate> cert;
try
{
cert = m_pib->getCertificate(m_pib->getDefaultCertificateNameForKey(keyName));
}
catch (SecPublicInfo::Error& e)
{
cert = selfSign(keyName);
m_pib->addCertificateAsIdentityDefault(*cert);
}
// make_shared on OSX 10.9 has some strange problem here
shared_ptr<SecuredBag> secureBag(new SecuredBag(*cert, pkcs5));
return secureBag;
}
void
KeyChain::importIdentity(const SecuredBag& securedBag, const std::string& passwordStr)
{
Name certificateName = securedBag.getCertificate().getName();
Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName);
Name identity = keyName.getPrefix(-1);
// Add identity
m_pib->addIdentity(identity);
// Add key
m_tpm->importPrivateKeyPkcs5IntoTpm(keyName,
securedBag.getKey()->buf(),
securedBag.getKey()->size(),
passwordStr);
shared_ptr<PublicKey> pubKey = m_tpm->getPublicKeyFromTpm(keyName.toUri());
// HACK! We should set key type according to the pkcs8 info.
m_pib->addPublicKey(keyName, KEY_TYPE_RSA, *pubKey);
m_pib->setDefaultKeyNameForIdentity(keyName);
// Add cert
m_pib->addCertificateAsIdentityDefault(securedBag.getCertificate());
}
shared_ptr<SignatureWithPublicKey>
KeyChain::determineSignatureWithPublicKey(KeyType keyType, DigestAlgorithm digestAlgorithm)
{
switch (keyType)
{
case KEY_TYPE_RSA:
{
// For temporary usage, we support SHA256 only, but will support more.
if (digestAlgorithm != DIGEST_ALGORITHM_SHA256)
return shared_ptr<SignatureWithPublicKey>();
return make_shared<SignatureSha256WithRsa>();
}
case KEY_TYPE_ECDSA:
{
// For temporary usage, we support SHA256 only, but will support more.
if (digestAlgorithm != DIGEST_ALGORITHM_SHA256)
return shared_ptr<SignatureWithPublicKey>();
return make_shared<SignatureSha256WithEcdsa>();
}
default:
return shared_ptr<SignatureWithPublicKey>();
}
}
void
KeyChain::setDefaultCertificateInternal()
{
m_pib->refreshDefaultCertificate();
if (!static_cast<bool>(m_pib->getDefaultCertificate()))
{
Name defaultIdentity;
try
{
defaultIdentity = m_pib->getDefaultIdentity();
}
catch (SecPublicInfo::Error& e)
{
uint32_t random = random::generateWord32();
defaultIdentity.append("tmp-identity")
.append(reinterpret_cast<uint8_t*>(&random), 4);
}
createIdentity(defaultIdentity);
m_pib->setDefaultIdentity(defaultIdentity);
m_pib->refreshDefaultCertificate();
}
}
Name
KeyChain::generateKeyPair(const Name& identityName, bool isKsk, const KeyParams& params)
{
Name keyName = m_pib->getNewKeyName(identityName, isKsk);
m_tpm->generateKeyPairInTpm(keyName.toUri(), params);
shared_ptr<PublicKey> pubKey = m_tpm->getPublicKeyFromTpm(keyName.toUri());
m_pib->addKey(keyName, *pubKey);
return keyName;
}
void
KeyChain::signPacketWrapper(Data& data, const Signature& signature,
const Name& keyName, DigestAlgorithm digestAlgorithm)
{
data.setSignature(signature);
EncodingBuffer encoder;
data.wireEncode(encoder, true);
Block signatureValue = m_tpm->signInTpm(encoder.buf(), encoder.size(),
keyName, digestAlgorithm);
data.wireEncode(encoder, signatureValue);
}
void
KeyChain::signPacketWrapper(Interest& interest, const Signature& signature,
const Name& keyName, DigestAlgorithm digestAlgorithm)
{
time::milliseconds timestamp = time::toUnixTimestamp(time::system_clock::now());
if (timestamp <= m_lastTimestamp)
{
timestamp = m_lastTimestamp + time::milliseconds(1);
}
Name signedName = interest.getName();
signedName
.append(name::Component::fromNumber(timestamp.count())) // timestamp
.append(name::Component::fromNumber(random::generateWord64())) // nonce
.append(signature.getInfo()); // signatureInfo
Block sigValue = m_tpm->signInTpm(signedName.wireEncode().value(),
signedName.wireEncode().value_size(),
keyName,
digestAlgorithm);
sigValue.encode();
signedName.append(sigValue); // signatureValue
interest.setName(signedName);
}
Signature
KeyChain::signByIdentity(const uint8_t* buffer, size_t bufferLength, const Name& identityName)
{
Name signingCertificateName;
try
{
signingCertificateName = m_pib->getDefaultCertificateNameForIdentity(identityName);
}
catch (SecPublicInfo::Error& e)
{
signingCertificateName = createIdentity(identityName);
// Ideally, no exception will be thrown out, unless something goes wrong in the TPM, which
// is a fatal error.
}
// We either get or create the signing certificate, sign data! (no exception unless fatal error
// in TPM)
return sign(buffer, bufferLength, signingCertificateName);
}
void
KeyChain::signWithSha256(Data& data)
{
DigestSha256 sig;
data.setSignature(sig);
Block sigValue(Tlv::SignatureValue,
crypto::sha256(data.wireEncode().value(),
data.wireEncode().value_size() -
data.getSignature().getValue().size()));
data.setSignatureValue(sigValue);
}
void
KeyChain::deleteCertificate(const Name& certificateName)
{
try
{
if (m_pib->getDefaultCertificateName() == certificateName)
return;
}
catch (SecPublicInfo::Error& e)
{
// Not a real error, just try to delete the certificate
}
m_pib->deleteCertificateInfo(certificateName);
}
void
KeyChain::deleteKey(const Name& keyName)
{
try
{
if (m_pib->getDefaultKeyNameForIdentity(m_pib->getDefaultIdentity()) == keyName)
return;
}
catch (SecPublicInfo::Error& e)
{
// Not a real error, just try to delete the key
}
m_pib->deletePublicKeyInfo(keyName);
m_tpm->deleteKeyPairInTpm(keyName);
}
void
KeyChain::deleteIdentity(const Name& identity)
{
try
{
if (m_pib->getDefaultIdentity() == identity)
return;
}
catch (SecPublicInfo::Error& e)
{
// Not a real error, just try to delete the identity
}
std::vector<Name> nameList;
m_pib->getAllKeyNamesOfIdentity(identity, nameList, true);
m_pib->getAllKeyNamesOfIdentity(identity, nameList, false);
m_pib->deleteIdentityInfo(identity);
std::vector<Name>::const_iterator it = nameList.begin();
for(; it != nameList.end(); it++)
m_tpm->deleteKeyPairInTpm(*it);
}
}