| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2013-2024 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/transform/private-key.hpp" |
| #include "ndn-cxx/security/verification-helpers.hpp" |
| |
| #include "tests/boost-test.hpp" |
| #include "tests/key-chain-fixture.hpp" |
| #include "tests/unit/clock-fixture.hpp" |
| #include "tests/unit/test-home-env-saver.hpp" |
| |
| #include <boost/mp11/list.hpp> |
| |
| namespace ndn::tests { |
| |
| using namespace ndn::security; |
| |
| BOOST_AUTO_TEST_SUITE(Security) |
| BOOST_FIXTURE_TEST_SUITE(TestKeyChain, TestHomeEnvSaver) |
| |
| template<class Path> |
| class TestHomeAndPibFixture : public TestHomeFixture<Path> |
| { |
| public: |
| TestHomeAndPibFixture() |
| { |
| unsetenv("NDN_CLIENT_PIB"); |
| unsetenv("NDN_CLIENT_TPM"); |
| } |
| |
| ~TestHomeAndPibFixture() |
| { |
| KeyChain::resetDefaultLocators(); |
| } |
| }; |
| |
| struct PibPathConfigFileHome |
| { |
| static constexpr std::string_view PATH = "build/config-file-home/"; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>) |
| { |
| createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"}); |
| |
| KeyChain keyChain; |
| BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:"); |
| BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:"); |
| } |
| |
| struct PibPathConfigFileEmptyHome |
| { |
| static constexpr std::string_view PATH = "build/config-file-empty-home/"; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>) |
| { |
| createClientConf({"pib=pib-memory:"}); |
| |
| #ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS |
| std::string oldHOME; |
| if (std::getenv("OLD_HOME")) |
| oldHOME = std::getenv("OLD_HOME"); |
| |
| std::string HOME; |
| if (std::getenv("HOME")) |
| HOME = std::getenv("HOME"); |
| |
| if (!oldHOME.empty()) |
| setenv("HOME", oldHOME.c_str(), 1); |
| else |
| unsetenv("HOME"); |
| #endif |
| |
| KeyChain keyChain; |
| BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:"); |
| |
| #ifdef NDN_CXX_WITH_OSX_KEYCHAIN |
| BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:"); |
| #else |
| BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:"); |
| #endif |
| |
| #ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS |
| if (!HOME.empty()) |
| setenv("HOME", HOME.c_str(), 1); |
| else |
| unsetenv("HOME"); |
| |
| if (!oldHOME.empty()) |
| setenv("OLD_HOME", oldHOME.c_str(), 1); |
| else |
| unsetenv("OLD_HOME"); |
| #endif |
| } |
| |
| struct PibPathConfigFileEmpty2Home |
| { |
| static constexpr std::string_view PATH = "build/config-file-empty2-home/"; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig2, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>) |
| { |
| createClientConf({"tpm=tpm-memory:"}); |
| |
| KeyChain keyChain; |
| BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:"); |
| BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:"); |
| } |
| |
| struct PibPathConfigFileMalformedHome |
| { |
| static constexpr std::string_view PATH = "build/config-file-malformed-home/"; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(ConstructorBadConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>) |
| { |
| createClientConf({"pib=lord", "tpm=ring"}); |
| BOOST_CHECK_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected. |
| } |
| |
| struct PibPathConfigFileMalformed2Home |
| { |
| static constexpr std::string_view PATH = "build/config-file-malformed2-home/"; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(ConstructorBadConfig2, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>) |
| { |
| createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"}); |
| BOOST_CHECK_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected. |
| } |
| |
| struct PibPathConfigFileNonCanonicalTpm |
| { |
| static constexpr std::string_view PATH = "build/config-file-non-canonical-tpm/"; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(ConstructorNonCanonicalTpm, TestHomeAndPibFixture<PibPathConfigFileNonCanonicalTpm>, |
| * ut::description("test for bug #4297")) |
| { |
| createClientConf({"pib=pib-sqlite3:", "tpm=tpm-file"}); |
| |
| { |
| KeyChain keyChain; |
| keyChain.createIdentity("/test"); |
| BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:"); |
| } |
| |
| { |
| KeyChain keyChain; |
| BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:"); |
| BOOST_CHECK(keyChain.getPib().getIdentities().find("/test") != keyChain.getPib().getIdentities().end()); |
| } |
| } |
| |
| BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib) |
| { |
| KeyChain keyChain("pib-memory", "tpm-memory"); |
| BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:"); |
| BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:"); |
| BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:"); |
| |
| BOOST_CHECK_NO_THROW(KeyChain("pib-memory:", "tpm-memory:")); |
| BOOST_CHECK_NO_THROW(KeyChain("pib-memory:/something", "tpm-memory:/something")); |
| } |
| |
| BOOST_FIXTURE_TEST_CASE(SigningWithCorruptedPibTpm, KeyChainFixture) |
| { |
| Identity id = m_keyChain.createIdentity("/test"); |
| |
| Data data("/foobar"); |
| BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id))); |
| |
| // now, "corrupting TPM" |
| const_cast<Tpm&>(m_keyChain.getTpm()).deleteKey(id.getDefaultKey().getName()); |
| |
| BOOST_CHECK_NO_THROW(id.getDefaultKey()); |
| BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity(id)), KeyChain::InvalidSigningInfoError); |
| } |
| |
| BOOST_FIXTURE_TEST_CASE(SigningWithNonExistingIdentity, KeyChainFixture) |
| { |
| Data data("/test/data"); |
| BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), |
| KeyChain::InvalidSigningInfoError); |
| } |
| |
| BOOST_FIXTURE_TEST_CASE(Management, KeyChainFixture) |
| { |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0); |
| BOOST_CHECK_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error); |
| |
| // Create an identity |
| Name idName("/test/id"); |
| Identity id = m_keyChain.createIdentity(idName); |
| BOOST_REQUIRE(id); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentity(idName), id); |
| |
| // The first added identity becomes the default identity |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity(), id); |
| // The default key of the added identity must exist |
| Key key = id.getDefaultKey(); |
| BOOST_REQUIRE(key); |
| BOOST_CHECK_EQUAL(id.getKeys().size(), 1); |
| // The default certificate of the default key must exist |
| BOOST_CHECK_NO_THROW(key.getDefaultCertificate()); |
| BOOST_CHECK_EQUAL(key.getCertificates().size(), 1); |
| |
| // Delete key |
| Name key1Name = key.getName(); |
| m_keyChain.deleteKey(id, key); |
| // The key instance should not be valid any more |
| BOOST_CHECK(!key); |
| BOOST_CHECK_THROW(id.getDefaultKey(), Pib::Error); |
| BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error); |
| BOOST_CHECK_EQUAL(id.getKeys().size(), 0); |
| |
| // Create another key |
| m_keyChain.createKey(id); |
| // The added key becomes the default key |
| Key key2 = id.getDefaultKey(); |
| BOOST_REQUIRE(key2); |
| BOOST_CHECK_NE(key2.getName(), key1Name); |
| BOOST_CHECK_EQUAL(id.getKeys().size(), 1); |
| BOOST_CHECK_NO_THROW(key2.getDefaultCertificate()); |
| BOOST_CHECK_EQUAL(key2.getCertificates().size(), 1); |
| |
| // Create a third key |
| Key key3 = m_keyChain.createKey(id); |
| BOOST_CHECK_NE(key3.getName(), key2.getName()); |
| BOOST_CHECK_EQUAL(id.getKeys().size(), 2); |
| // The added key will not be the default key, because the default was already set |
| BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName()); |
| |
| // Delete cert |
| BOOST_REQUIRE_EQUAL(key3.getCertificates().size(), 1); |
| Certificate key3Cert1 = *key3.getCertificates().begin(); |
| Name key3CertName = key3Cert1.getName(); |
| m_keyChain.deleteCertificate(key3, key3CertName); |
| BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0); |
| BOOST_CHECK_THROW(key3.getDefaultCertificate(), Pib::Error); |
| |
| // Add cert |
| m_keyChain.addCertificate(key3, key3Cert1); |
| BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1); |
| BOOST_CHECK_NO_THROW(key3.getDefaultCertificate()); |
| m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work |
| BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1); |
| |
| // Add another cert |
| Certificate key3Cert2 = key3Cert1; |
| Name key3Cert2Name = key3.getName(); |
| key3Cert2Name.append("Self").appendVersion(); |
| key3Cert2.setName(key3Cert2Name); |
| m_keyChain.addCertificate(key3, key3Cert2); |
| BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2); |
| |
| // Add empty cert |
| Certificate key3Cert3 = key3Cert1; |
| key3Cert3.unsetContent(); |
| BOOST_CHECK_THROW(m_keyChain.addCertificate(key3, key3Cert3), std::invalid_argument); |
| |
| // Create another identity |
| Name id2Name("/test/id2"); |
| Identity id2 = m_keyChain.createIdentity(id2Name); |
| BOOST_REQUIRE(id2); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 2); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentity(id2Name), id2); |
| |
| // Default identity setting |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName()); |
| m_keyChain.setDefaultIdentity(id2); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName()); |
| |
| // Default key setting |
| BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName()); |
| m_keyChain.setDefaultKey(id, key3); |
| BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName()); |
| // Set key of a different identity as default |
| BOOST_CHECK_THROW(m_keyChain.setDefaultKey(id, id2.getDefaultKey()), std::invalid_argument); |
| |
| // Default certificate setting |
| BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName); |
| m_keyChain.setDefaultCertificate(key3, key3Cert2); |
| BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name); |
| // Set certificate of a different key as default |
| BOOST_CHECK_THROW(m_keyChain.setDefaultCertificate(key3, key2.getDefaultCertificate()), |
| std::invalid_argument); |
| |
| // Delete certificate name mismatch |
| BOOST_CHECK_THROW(m_keyChain.deleteCertificate(key2, key3CertName), std::invalid_argument); |
| // Delete key name mismatch |
| BOOST_CHECK_THROW(m_keyChain.deleteKey(id, id2.getDefaultKey()), std::invalid_argument); |
| |
| // Delete identity |
| m_keyChain.deleteIdentity(id); |
| // The identity instance should not be valid any more |
| BOOST_CHECK(!id); |
| BOOST_CHECK_THROW(m_keyChain.getPib().getIdentity(idName), Pib::Error); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1); |
| } |
| |
| struct DataPkt |
| { |
| Data packet{"/data"}; |
| static constexpr SignedInterestFormat sigFormat = SignedInterestFormat::V02; // irrelevant for Data |
| |
| SignatureInfo |
| getSignatureInfo() const |
| { |
| return packet.getSignatureInfo(); |
| } |
| }; |
| |
| struct InterestV02Pkt |
| { |
| Interest packet{"/interest02"}; |
| static constexpr SignedInterestFormat sigFormat = SignedInterestFormat::V02; |
| |
| SignatureInfo |
| getSignatureInfo() const |
| { |
| return SignatureInfo(packet.getName()[signed_interest::POS_SIG_INFO].blockFromValue()); |
| } |
| }; |
| |
| struct InterestV03Pkt |
| { |
| Interest packet{"/interest03"}; |
| static constexpr SignedInterestFormat sigFormat = SignedInterestFormat::V03; |
| |
| SignatureInfo |
| getSignatureInfo() const |
| { |
| return packet.getSignatureInfo().value(); // use .value() for checked access |
| } |
| }; |
| |
| template<typename KeyParams> |
| struct DefaultIdentity |
| { |
| Identity |
| operator()(KeyChain& keyChain) const |
| { |
| auto id = keyChain.createIdentity("/id", KeyParams()); |
| BOOST_ASSERT(keyChain.getPib().getDefaultIdentity() == id); |
| return id; |
| } |
| }; |
| |
| template<typename KeyParams> |
| struct NonDefaultIdentity |
| { |
| Identity |
| operator()(KeyChain& keyChain) const |
| { |
| auto id = keyChain.createIdentity("/id"); |
| auto id2 = keyChain.createIdentity("/id2", KeyParams()); |
| BOOST_ASSERT(keyChain.getPib().getDefaultIdentity() == id); |
| return id2; |
| } |
| }; |
| |
| template<typename KeyParams> |
| struct DefaultKey |
| { |
| Key |
| operator()(KeyChain&, const Identity& id) const |
| { |
| auto key = id.getDefaultKey(); |
| BOOST_ASSERT(key.getKeyType() == KeyParams().getKeyType()); |
| return key; |
| } |
| }; |
| |
| template<typename KeyParams> |
| struct NonDefaultKey |
| { |
| Key |
| operator()(KeyChain& keyChain, const Identity& id) const |
| { |
| auto key2 = keyChain.createKey(id, KeyParams()); |
| BOOST_ASSERT(id.getDefaultKey() != key2); |
| return key2; |
| } |
| }; |
| |
| template<typename PacketType, |
| template<typename> class IdentityMaker = DefaultIdentity, |
| template<typename> class KeyMaker = DefaultKey, |
| typename AsymmetricKeyParams = EcKeyParams, |
| uint32_t SignatureTypeTlvValue = tlv::SignatureSha256WithEcdsa> |
| struct AsymmetricSigningBase : protected KeyChainFixture, protected PacketType |
| { |
| const Identity id = IdentityMaker<AsymmetricKeyParams>()(m_keyChain); |
| const Key key = KeyMaker<AsymmetricKeyParams>()(m_keyChain, id); |
| const Certificate cert = key.getDefaultCertificate(); |
| |
| static constexpr uint32_t expectedSigType = SignatureTypeTlvValue; |
| static constexpr bool shouldHaveKeyLocator = true; |
| const std::optional<KeyLocator> expectedKeyLocator = cert.getName(); |
| |
| bool |
| verify(const SigningInfo&) const |
| { |
| return verifySignature(this->packet, key); |
| } |
| }; |
| |
| template<typename PacketType, |
| typename AsymmetricKeyParams, |
| uint32_t SignatureTypeTlvValue> |
| struct AsymmetricSigning : protected AsymmetricSigningBase<PacketType, DefaultIdentity, DefaultKey, |
| AsymmetricKeyParams, SignatureTypeTlvValue> |
| { |
| const std::vector<SigningInfo> signingInfos = { |
| SigningInfo(), |
| SigningInfo(""), |
| |
| SigningInfo(this->id), |
| SigningInfo(SigningInfo::SIGNER_TYPE_ID, this->id.getName()), |
| SigningInfo("id:" + this->id.getName().toUri()), |
| signingByIdentity(this->id), |
| signingByIdentity(this->id.getName()), |
| |
| SigningInfo(this->key), |
| SigningInfo(SigningInfo::SIGNER_TYPE_KEY, this->key.getName()), |
| SigningInfo("key:" + this->key.getName().toUri()), |
| signingByKey(this->key), |
| signingByKey(this->key.getName()), |
| |
| SigningInfo(SigningInfo::SIGNER_TYPE_CERT, this->cert.getName()), |
| SigningInfo("cert:" + this->cert.getName().toUri()), |
| signingByCertificate(this->cert), |
| signingByCertificate(this->cert.getName()), |
| }; |
| }; |
| |
| template<typename PacketType> |
| using RsaSigning = AsymmetricSigning<PacketType, RsaKeyParams, tlv::SignatureSha256WithRsa>; |
| |
| template<typename PacketType> |
| using EcdsaSigning = AsymmetricSigning<PacketType, EcKeyParams, tlv::SignatureSha256WithEcdsa>; |
| |
| template<typename PacketType> |
| struct SigningWithNonDefaultIdentity : protected AsymmetricSigningBase<PacketType, NonDefaultIdentity> |
| { |
| const std::vector<SigningInfo> signingInfos = { |
| signingByIdentity(this->id), |
| signingByIdentity(this->id.getName()), |
| signingByKey(this->key), |
| signingByCertificate(this->cert), |
| }; |
| }; |
| |
| template<typename PacketType> |
| struct SigningWithNonDefaultKey : protected AsymmetricSigningBase<PacketType, NonDefaultIdentity, NonDefaultKey> |
| { |
| const std::vector<SigningInfo> signingInfos = { |
| signingByKey(this->key), |
| signingByKey(this->key.getName()), |
| signingByCertificate(this->cert), |
| }; |
| }; |
| |
| template<typename PacketType, |
| DigestAlgorithm DigestAlgo = DigestAlgorithm::SHA256, |
| uint32_t SignatureTypeTlvValue = tlv::SignatureHmacWithSha256> |
| struct HmacSigning : protected KeyChainFixture, protected PacketType |
| { |
| const std::vector<SigningInfo> signingInfos = { |
| SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, m_keyChain.createHmacKey()), |
| SigningInfo("hmac-sha256:QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENUI0OTdGREZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg=="), |
| }; |
| |
| static constexpr uint32_t expectedSigType = SignatureTypeTlvValue; |
| static constexpr bool shouldHaveKeyLocator = true; |
| const std::optional<KeyLocator> expectedKeyLocator = std::nullopt; // don't check KeyLocator value |
| |
| bool |
| verify(const SigningInfo& si) const |
| { |
| return verifySignature(this->packet, m_keyChain.getTpm(), si.getSignerName(), DigestAlgo); |
| } |
| }; |
| |
| template<typename PacketType> |
| struct Sha256Signing : protected KeyChainFixture, protected PacketType |
| { |
| const std::vector<SigningInfo> signingInfos = { |
| SigningInfo(SigningInfo::SIGNER_TYPE_SHA256), |
| SigningInfo("id:" + SigningInfo::getDigestSha256Identity().toUri()), |
| signingWithSha256() |
| }; |
| |
| static constexpr uint32_t expectedSigType = tlv::DigestSha256; |
| static constexpr bool shouldHaveKeyLocator = false; |
| const std::optional<KeyLocator> expectedKeyLocator = std::nullopt; |
| |
| bool |
| verify(const SigningInfo&) const |
| { |
| return verifySignature(this->packet, std::nullopt); |
| } |
| }; |
| |
| using SigningTests = boost::mp11::mp_list< |
| RsaSigning<DataPkt>, |
| RsaSigning<InterestV02Pkt>, |
| RsaSigning<InterestV03Pkt>, |
| EcdsaSigning<DataPkt>, |
| EcdsaSigning<InterestV02Pkt>, |
| EcdsaSigning<InterestV03Pkt>, |
| HmacSigning<DataPkt>, |
| HmacSigning<InterestV02Pkt>, |
| HmacSigning<InterestV03Pkt>, |
| Sha256Signing<DataPkt>, |
| Sha256Signing<InterestV02Pkt>, |
| Sha256Signing<InterestV03Pkt>, |
| SigningWithNonDefaultIdentity<DataPkt>, |
| SigningWithNonDefaultIdentity<InterestV03Pkt>, |
| SigningWithNonDefaultKey<DataPkt>, |
| SigningWithNonDefaultKey<InterestV03Pkt> |
| >; |
| |
| BOOST_FIXTURE_TEST_CASE_TEMPLATE(SigningInterface, T, SigningTests, T) |
| { |
| BOOST_TEST_INFO_SCOPE("Packet = " << this->packet.getName()); |
| |
| for (auto signingInfo : this->signingInfos) { |
| signingInfo.setSignedInterestFormat(this->sigFormat); |
| BOOST_TEST_INFO_SCOPE("SigningInfo = " << signingInfo); |
| |
| this->m_keyChain.sign(this->packet, signingInfo); |
| |
| auto sigInfo = this->getSignatureInfo(); |
| BOOST_CHECK_EQUAL(sigInfo.getSignatureType(), this->expectedSigType); |
| BOOST_CHECK_EQUAL(sigInfo.hasKeyLocator(), this->shouldHaveKeyLocator); |
| if (this->expectedKeyLocator) { |
| BOOST_CHECK_EQUAL(sigInfo.getKeyLocator(), *this->expectedKeyLocator); |
| } |
| BOOST_CHECK(this->verify(signingInfo)); |
| } |
| } |
| |
| class MakeCertificateFixture : public ClockFixture |
| { |
| public: |
| MakeCertificateFixture() |
| : requesterKeyChain("pib-memory:", "tpm-memory:") |
| , signerKeyChain("pib-memory:", "tpm-memory:") |
| { |
| m_systemClock->setNow(time::fromIsoString("20091117T203458,651387237").time_since_epoch()); |
| |
| requester = requesterKeyChain.createIdentity("/requester").getDefaultKey(); |
| Name signerIdentityName("/signer"); |
| signerKey = signerKeyChain.createIdentity(signerIdentityName).getDefaultKey(); |
| signerParams = signingByIdentity(signerIdentityName); |
| } |
| |
| void |
| checkKeyLocatorName(const Certificate& cert, |
| const std::optional<Name>& klName = std::nullopt) const |
| { |
| auto kl = cert.getKeyLocator(); |
| if (!kl.has_value()) { |
| BOOST_ERROR("KeyLocator is missing"); |
| return; |
| } |
| BOOST_CHECK_EQUAL(kl->getName(), klName.value_or(signerKey.getDefaultCertificate().getName())); |
| } |
| |
| void |
| checkCertFromDefaults(const Certificate& cert) const |
| { |
| BOOST_CHECK(Certificate::isValidName(cert.getName())); |
| BOOST_CHECK_EQUAL(cert.getKeyName(), requester.getName()); |
| BOOST_CHECK_EQUAL(cert.getName()[-2], name::Component("NA")); |
| BOOST_CHECK(cert.getName()[-1].isVersion()); |
| |
| BOOST_CHECK_EQUAL(cert.getContentType(), tlv::ContentType_Key); |
| BOOST_CHECK_EQUAL(cert.getFreshnessPeriod(), 1_h); |
| |
| BOOST_TEST(cert.getPublicKey() == requester.getPublicKey(), boost::test_tools::per_element()); |
| |
| checkKeyLocatorName(cert); |
| |
| BOOST_CHECK(cert.isValid()); |
| auto vp = cert.getValidityPeriod().getPeriod(); |
| BOOST_CHECK_EQUAL(vp.first, time::fromIsoString("20091117T203458")); |
| BOOST_CHECK_EQUAL(vp.second, time::fromIsoString("20101117T203458")); |
| |
| auto adBlock = cert.getSignatureInfo().getCustomTlv(tlv::AdditionalDescription); |
| BOOST_CHECK(!adBlock.has_value()); |
| } |
| |
| public: |
| KeyChain requesterKeyChain; |
| pib::Key requester; |
| |
| KeyChain signerKeyChain; |
| pib::Key signerKey; |
| Name signerCertificateName; |
| SigningInfo signerParams; |
| }; |
| |
| BOOST_FIXTURE_TEST_SUITE(MakeCertificate, MakeCertificateFixture) |
| |
| BOOST_AUTO_TEST_CASE(DefaultsFromKey) |
| { |
| auto cert = signerKeyChain.makeCertificate(requester, signerParams); |
| checkCertFromDefaults(cert); |
| } |
| |
| BOOST_AUTO_TEST_CASE(DefaultsFromCert) |
| { |
| auto cert = signerKeyChain.makeCertificate(requester.getDefaultCertificate(), signerParams); |
| checkCertFromDefaults(cert); |
| } |
| |
| BOOST_AUTO_TEST_CASE(Options) |
| { |
| MakeCertificateOptions opts; |
| opts.issuerId = name::Component::fromUri("ISSUER"); |
| opts.version = 41218268; |
| opts.freshnessPeriod = 321_s; |
| opts.validity.emplace(time::fromIsoString("20060702T150405"), |
| time::fromIsoString("20160702T150405")); |
| |
| SignatureInfo sigInfo; |
| sigInfo.setKeyLocator(signerKey.getName()); |
| sigInfo.setValidityPeriod(ValidityPeriod(time::fromIsoString("20060102T150405"), |
| time::fromIsoString("20160102T150405"))); |
| sigInfo.addCustomTlv(Block(0xF0)); |
| signerParams.setSignatureInfo(sigInfo); |
| |
| auto cert = signerKeyChain.makeCertificate(requester, signerParams, opts); |
| |
| BOOST_CHECK_EQUAL(cert.getName(), |
| Name(requester.getName()).append(PartialName("ISSUER/v=41218268"))); |
| BOOST_CHECK_EQUAL(cert.getFreshnessPeriod(), 321_s); |
| checkKeyLocatorName(cert, signerKey.getName()); |
| |
| auto vp = cert.getValidityPeriod().getPeriod(); |
| BOOST_CHECK_EQUAL(vp.first, time::fromIsoString("20060702T150405")); |
| BOOST_CHECK_EQUAL(vp.second, time::fromIsoString("20160702T150405")); |
| |
| BOOST_CHECK(cert.getSignatureInfo().getCustomTlv(0xF0).has_value()); |
| } |
| |
| BOOST_AUTO_TEST_CASE(ErrSigner) |
| { |
| signerParams = signingByIdentity("/nonexistent"); |
| BOOST_CHECK_THROW(signerKeyChain.makeCertificate(requester, signerParams), KeyChain::Error); |
| } |
| |
| BOOST_AUTO_TEST_CASE(ErrZeroFreshness) |
| { |
| MakeCertificateOptions opts; |
| opts.freshnessPeriod = 0_ms; |
| BOOST_CHECK_THROW(signerKeyChain.makeCertificate(requester, signerParams, opts), |
| std::invalid_argument); |
| } |
| |
| BOOST_AUTO_TEST_CASE(ErrNegativeFreshness) |
| { |
| MakeCertificateOptions opts; |
| opts.freshnessPeriod = -1_ms; |
| BOOST_CHECK_THROW(signerKeyChain.makeCertificate(requester, signerParams, opts), |
| std::invalid_argument); |
| } |
| |
| BOOST_AUTO_TEST_CASE(ErrContent) |
| { |
| Certificate request(requester.getDefaultCertificate()); |
| |
| // malformed public key |
| const auto& oldContent = request.getContent(); |
| std::vector<uint8_t> content(oldContent.value_begin(), oldContent.value_end()); |
| content[0] ^= 0x80; |
| request.setContent(content); |
| BOOST_CHECK_THROW(signerKeyChain.makeCertificate(request, signerParams), std::invalid_argument); |
| |
| // empty content |
| request.setContent(""); |
| BOOST_CHECK_THROW(signerKeyChain.makeCertificate(request, signerParams), std::invalid_argument); |
| } |
| |
| BOOST_AUTO_TEST_SUITE_END() // MakeCertificate |
| |
| BOOST_FIXTURE_TEST_CASE(ImportPrivateKey, KeyChainFixture) |
| { |
| const Name keyName("/test/device2"); |
| const uint8_t rawKey[] = "nPSNOHyZKsg2WLqHAs7MXGb0sjQb4zCT"; |
| auto key = make_shared<transform::PrivateKey>(); |
| key->loadRaw(KeyType::HMAC, rawKey); |
| |
| m_keyChain.importPrivateKey(keyName, key); |
| BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(keyName), true); |
| BOOST_CHECK_THROW(m_keyChain.importPrivateKey(keyName, key), KeyChain::Error); |
| } |
| |
| BOOST_FIXTURE_TEST_CASE(ExportImport, KeyChainFixture) |
| { |
| Identity id = m_keyChain.createIdentity("/TestKeyChain/ExportIdentity"); |
| Certificate cert = id.getDefaultKey().getDefaultCertificate(); |
| |
| shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4); |
| const auto& block = exported->wireEncode(); |
| |
| m_keyChain.deleteIdentity(id); |
| BOOST_CHECK_EXCEPTION(m_keyChain.exportSafeBag(cert, "1234", 4), KeyChain::Error, [] (const auto& e) { |
| return std::string(e.what()).find("Failed to export private key") == 0; |
| }); |
| |
| BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0); |
| |
| SafeBag imported(block); |
| m_keyChain.importSafeBag(imported, "1234", 4); |
| BOOST_CHECK_EXCEPTION(m_keyChain.importSafeBag(imported, "1234", 4), KeyChain::Error, [] (const auto& e) { |
| return std::string(e.what()).find("already exists") != std::string::npos; |
| }); |
| |
| BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true); |
| BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1); |
| Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity()); |
| BOOST_CHECK_EQUAL(newId.getKeys().size(), 1); |
| Key newKey = newId.getKey(cert.getKeyName()); |
| BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1); |
| Certificate newCert = newKey.getCertificate(cert.getName()); |
| BOOST_CHECK_EQUAL(newCert, cert); |
| } |
| |
| BOOST_FIXTURE_TEST_CASE(ImportInvalid, KeyChainFixture) |
| { |
| Identity id = m_keyChain.createIdentity("/TestKeyChain/ExportIdentity"); |
| Certificate cert = id.getDefaultKey().getDefaultCertificate(); |
| |
| auto exported = m_keyChain.exportSafeBag(cert, "1234", 4); |
| m_keyChain.deleteIdentity(id); |
| |
| Identity id2 = m_keyChain.createIdentity("/TestKeyChain/AnotherIdentity"); |
| Certificate cert2 = id2.getDefaultKey().getDefaultCertificate(); |
| m_keyChain.deleteIdentity(id2); |
| |
| SafeBag mismatch(cert2, exported->getEncryptedKey()); |
| BOOST_CHECK_EXCEPTION(m_keyChain.importSafeBag(mismatch, "1234", 4), KeyChain::Error, [] (const auto& e) { |
| return std::string(e.what()).find("do not match") != std::string::npos; |
| }); |
| |
| SafeBag invalidPriv(cert2, {0xCA, 0xFE}); |
| BOOST_CHECK_EXCEPTION(m_keyChain.importSafeBag(invalidPriv, "1234", 4), KeyChain::Error, [] (const auto& e) { |
| return std::string(e.what()).find("Failed to import private key") == 0; |
| }); |
| } |
| |
| BOOST_FIXTURE_TEST_CASE(SelfSignedCertValidity, KeyChainFixture) |
| { |
| Certificate cert = m_keyChain.createIdentity("/Security/TestKeyChain/SelfSignedCertValidity") |
| .getDefaultKey() |
| .getDefaultCertificate(); |
| BOOST_CHECK(cert.isValid()); |
| BOOST_CHECK(cert.isValid(time::system_clock::now() + 10 * 365_days)); |
| BOOST_CHECK_GT(cert.getValidityPeriod().getPeriod().second, time::system_clock::now() + 10 * 365_days); |
| } |
| |
| BOOST_AUTO_TEST_SUITE_END() // TestKeyChain |
| BOOST_AUTO_TEST_SUITE_END() // Security |
| |
| } // namespace ndn::tests |