| /* -*- 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"); |
| |
| 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"); |
| } |
| |
| 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("2.5.4.41", 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) |
| { |
| if (!m_pib->doesCertificateExist(certificateName)) |
| throw SecPublicInfo::Error("Requested certificate [" |
| + certificateName.toUri() + "] doesn't exist"); |
| |
| Name keyName = IdentityCertificate::certificateNameToPublicKeyName(certificateName); |
| |
| SignatureSha256WithRsa signature; |
| // implicit conversion should take care |
| signature.setKeyLocator(certificateName.getPrefix(-1)); |
| |
| // For temporary usage, we support RSA + SHA256 only, but will support more. |
| signature.setValue(m_tpm->signInTpm(buffer, bufferLength, keyName, DIGEST_ALGORITHM_SHA256)); |
| return signature; |
| } |
| |
| 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("2.5.4.41", 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!"); |
| |
| SignatureSha256WithRsa signature; |
| signature.setKeyLocator(cert.getName().getPrefix(-1)); // implicit conversion should take care |
| |
| // For temporary usage, we support RSA + SHA256 only, but will support more. |
| signPacketWrapper(cert, signature, 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()); |
| } |
| |
| void |
| KeyChain::setDefaultCertificateInternal() |
| { |
| m_pib->refreshDefaultCertificate(); |
| |
| if (!static_cast<bool>(m_pib->defaultCertificate())) |
| { |
| 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(); |
| } |
| } |
| |
| } |