security: introduce KeyChain::makeCertificate
KeyChain::makeCertificate() captures a common routine of creating and
signing a certificate. Having it in the library allows deduplicating
similar code elsewhere.
Also add "find by certificate name" tests for CertificateCache and
TrustAnchorContainer.
refs #5112
Change-Id: I954587e1c03d6b372e3b4f04e702339d1ff1533e
diff --git a/tests/key-chain-fixture.cpp b/tests/key-chain-fixture.cpp
index af62de5..7bbcf6a 100644
--- a/tests/key-chain-fixture.cpp
+++ b/tests/key-chain-fixture.cpp
@@ -43,36 +43,6 @@
}
}
-Certificate
-KeyChainFixture::makeCert(const Key& key, const std::string& issuer, const Key& signingKey,
- optional<KeyLocator> keyLocator)
-{
- const Key& signer = signingKey ? signingKey : key;
-
- Certificate cert;
- cert.setName(Name(key.getName())
- .append(issuer)
- .appendVersion());
-
- // set metainfo
- cert.setContentType(tlv::ContentType_Key);
- cert.setFreshnessPeriod(1_h);
-
- // set content
- cert.setContent(key.getPublicKey());
-
- // set signature info
- ndn::SignatureInfo info;
- auto now = time::system_clock::now();
- info.setValidityPeriod(ValidityPeriod(now - 30_days, now + 30_days));
- if (keyLocator) {
- info.setKeyLocator(*keyLocator);
- }
-
- m_keyChain.sign(cert, signingByKey(signer).setSignatureInfo(info));
- return cert;
-}
-
bool
KeyChainFixture::saveCert(const Data& cert, const std::string& filename)
{
diff --git a/tests/key-chain-fixture.hpp b/tests/key-chain-fixture.hpp
index 9c4c699..ec1cac3 100644
--- a/tests/key-chain-fixture.hpp
+++ b/tests/key-chain-fixture.hpp
@@ -45,19 +45,6 @@
public:
/**
- * @brief Creates and returns a certificate for a given key
- * @param key The key for which to make a certificate
- * @param issuer The IssuerId to include in the certificate name
- * @param signingKey The key with which to sign the certificate; if not provided, the
- * certificate will be self-signed
- * @param keyLocator The KeyLocator name in the generated certificate; if nullopt,
- * @p signingKey 's default certificate will be used
- */
- Certificate
- makeCert(const Key& key, const std::string& issuer, const Key& signingKey = Key(),
- optional<KeyLocator> keyLocator = nullopt);
-
- /**
* @brief Saves an NDN certificate to a file
* @return true if successful, false otherwise
*/
diff --git a/tests/unit/security/certificate-cache.t.cpp b/tests/unit/security/certificate-cache.t.cpp
index f3b400e..8c715eb 100644
--- a/tests/unit/security/certificate-cache.t.cpp
+++ b/tests/unit/security/certificate-cache.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2021 Regents of the University of California.
+ * Copyright (c) 2013-2022 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -42,6 +42,23 @@
cert = identity.getDefaultKey().getDefaultCertificate();
}
+ void
+ checkFindByInterest(const Name& name, bool canBePrefix, optional<Certificate> expected) const
+ {
+ Interest interest(name);
+ interest.setCanBePrefix(canBePrefix);
+ BOOST_TEST_CONTEXT(interest) {
+ auto found = certCache.find(interest);
+ if (expected) {
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), expected->getName());
+ }
+ else {
+ BOOST_CHECK(found == nullptr);
+ }
+ }
+ }
+
public:
CertificateCache certCache;
Identity identity;
@@ -75,18 +92,13 @@
{
BOOST_CHECK_NO_THROW(certCache.insert(cert));
- Interest i;
- i.setCanBePrefix(true);
- i.setName(cert.getIdentity());
- BOOST_CHECK(certCache.find(i) != nullptr);
- i.setName(cert.getKeyName());
- BOOST_CHECK(certCache.find(i) != nullptr);
- i.setName(Name(cert.getName()).appendVersion());
- BOOST_CHECK(certCache.find(i) == nullptr);
+ checkFindByInterest(cert.getIdentity(), true, cert);
+ checkFindByInterest(cert.getKeyName(), true, cert);
+ checkFindByInterest(cert.getName(), false, cert);
+ checkFindByInterest(Name(cert.getName()).appendVersion(), true, nullopt);
advanceClocks(12_s);
- i.setName(cert.getIdentity());
- BOOST_CHECK(certCache.find(i) == nullptr);
+ checkFindByInterest(cert.getIdentity(), true, nullopt);
}
BOOST_AUTO_TEST_SUITE_END() // TestCertificateCache
diff --git a/tests/unit/security/key-chain.t.cpp b/tests/unit/security/key-chain.t.cpp
index 94e75a0..b7aef11 100644
--- a/tests/unit/security/key-chain.t.cpp
+++ b/tests/unit/security/key-chain.t.cpp
@@ -26,6 +26,7 @@
#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/mpl/vector.hpp>
@@ -573,6 +574,146 @@
}
}
+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, optional<Name> klName = 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.getContent().value_bytes() == 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::fromEscapedString("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());
+ 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);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // MakeCertificate
+
BOOST_FIXTURE_TEST_CASE(ImportPrivateKey, KeyChainFixture)
{
const Name keyName("/test/device2");
diff --git a/tests/unit/security/trust-anchor-container.t.cpp b/tests/unit/security/trust-anchor-container.t.cpp
index bb336b5..19d6867 100644
--- a/tests/unit/security/trust-anchor-container.t.cpp
+++ b/tests/unit/security/trust-anchor-container.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2021 Regents of the University of California.
+ * Copyright (c) 2013-2022 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -60,6 +60,23 @@
boost::filesystem::remove_all(certDirPath);
}
+ void
+ checkFindByInterest(const Name& name, bool canBePrefix, optional<Certificate> expected) const
+ {
+ Interest interest(name);
+ interest.setCanBePrefix(canBePrefix);
+ BOOST_TEST_CONTEXT(interest) {
+ auto found = anchorContainer.find(interest);
+ if (expected) {
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), expected->getName());
+ }
+ else {
+ BOOST_CHECK(found == nullptr);
+ }
+ }
+ }
+
public:
const boost::filesystem::path certDirPath{boost::filesystem::path(UNIT_TESTS_TMPDIR) / "test-cert-dir"};
const boost::filesystem::path certPath1{certDirPath / "trust-anchor-1.cert"};
@@ -150,29 +167,31 @@
BOOST_AUTO_TEST_CASE(FindByInterest)
{
anchorContainer.insert("group1", certPath1.string(), 1_s);
- Interest interest1;
- interest1.setCanBePrefix(true);
- interest1.setName(identity1.getName());
- BOOST_CHECK(anchorContainer.find(interest1) != nullptr);
- interest1.setName(identity1.getName().getPrefix(-1));
- BOOST_CHECK(anchorContainer.find(interest1) != nullptr);
- interest1.setName(Name(identity1.getName()).appendVersion());
- BOOST_CHECK(anchorContainer.find(interest1) == nullptr);
- auto cert3 = makeCert(identity1.getDefaultKey(), "3");
- auto cert4 = makeCert(identity1.getDefaultKey(), "4");
- auto cert5 = makeCert(identity1.getDefaultKey(), "5");
+ checkFindByInterest(identity1.getName(), true, cert1);
+ checkFindByInterest(identity1.getName().getPrefix(-1), true, cert1);
+ checkFindByInterest(cert1.getKeyName(), true, cert1);
+ checkFindByInterest(cert1.getName(), false, cert1);
+ checkFindByInterest(Name(identity1.getName()).appendVersion(), true, nullopt);
+
+ auto makeIdentity1Cert = [=] (const std::string& issuerId) {
+ auto key = identity1.getDefaultKey();
+ MakeCertificateOptions opts;
+ opts.issuerId = name::Component::fromEscapedString(issuerId);
+ return m_keyChain.makeCertificate(key, signingByKey(key), opts);
+ };
+
+ auto cert3 = makeIdentity1Cert("3");
+ auto cert4 = makeIdentity1Cert("4");
+ auto cert5 = makeIdentity1Cert("5");
Certificate cert3Copy = cert3;
anchorContainer.insert("group2", std::move(cert3Copy));
anchorContainer.insert("group3", std::move(cert4));
anchorContainer.insert("group4", std::move(cert5));
- auto interest3 = Interest(cert3.getKeyName()).setCanBePrefix(true);
- const Certificate* foundCert = anchorContainer.find(interest3);
- BOOST_REQUIRE(foundCert != nullptr);
- BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
- BOOST_CHECK_EQUAL(foundCert->getName(), cert3.getName());
+ checkFindByInterest(cert3.getKeyName(), true, cert3);
+ checkFindByInterest(cert3.getName(), false, cert3);
}
BOOST_AUTO_TEST_SUITE_END() // TestTrustAnchorContainer
diff --git a/tests/unit/security/validator.t.cpp b/tests/unit/security/validator.t.cpp
index 0a45c72..4acbe4d 100644
--- a/tests/unit/security/validator.t.cpp
+++ b/tests/unit/security/validator.t.cpp
@@ -262,7 +262,12 @@
// create another key for the same identity and sign it properly
Key parentKey = m_keyChain.createKey(subIdentity);
Key requestedKey = subIdentity.getKey(interest.getName());
- auto cert = makeCert(requestedKey, "looper", parentKey, parentKey.getName());
+
+ SignatureInfo sigInfo;
+ sigInfo.setKeyLocator(parentKey.getName());
+ auto si = signingByKey(parentKey).setSignatureInfo(sigInfo);
+
+ auto cert = m_keyChain.makeCertificate(requestedKey, si);
face.receive(cert);
};
@@ -292,7 +297,11 @@
auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
auto makeLoopCert = [this] (Key& key, const Key& signer) {
- auto cert = this->makeCert(key, "looper", signer, signer.getName());
+ SignatureInfo sigInfo;
+ sigInfo.setKeyLocator(signer.getName());
+ auto si = signingByKey(signer).setSignatureInfo(sigInfo);
+
+ auto cert = m_keyChain.makeCertificate(key, si);
m_keyChain.setDefaultCertificate(key, cert);
cache.insert(cert);
};
diff --git a/tests/unit/security/validity-period.t.cpp b/tests/unit/security/validity-period.t.cpp
index 5dc8d9d..4a28df2 100644
--- a/tests/unit/security/validity-period.t.cpp
+++ b/tests/unit/security/validity-period.t.cpp
@@ -35,6 +35,34 @@
BOOST_AUTO_TEST_SUITE(Security)
BOOST_AUTO_TEST_SUITE(TestValidityPeriod)
+BOOST_AUTO_TEST_SUITE(MakeRelative)
+
+BOOST_AUTO_TEST_CASE(FromNow)
+{
+ auto vp = ValidityPeriod::makeRelative(-1_s, 365_days, time::fromIsoString("20091117T203458,651387237"));
+ auto period = vp.getPeriod();
+ BOOST_CHECK_EQUAL(period.first, time::fromIsoString("20091117T203458"));
+ BOOST_CHECK_EQUAL(period.second, time::fromIsoString("20101117T203458"));
+}
+
+BOOST_AUTO_TEST_CASE(Positive)
+{
+ auto vp = ValidityPeriod::makeRelative(10_s, 1_days, time::fromIsoString("20091117T203458,651387237"));
+ auto period = vp.getPeriod();
+ BOOST_CHECK_EQUAL(period.first, time::fromIsoString("20091117T203509"));
+ BOOST_CHECK_EQUAL(period.second, time::fromIsoString("20091118T203458"));
+}
+
+BOOST_AUTO_TEST_CASE(Negative)
+{
+ auto vp = ValidityPeriod::makeRelative(-1_days, -10_s, time::fromIsoString("20091117T203458,651387237"));
+ auto period = vp.getPeriod();
+ BOOST_CHECK_EQUAL(period.first, time::fromIsoString("20091116T203459"));
+ BOOST_CHECK_EQUAL(period.second, time::fromIsoString("20091117T203448"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // MakeRelative
+
BOOST_FIXTURE_TEST_CASE(ConstructorSetter, ClockFixture)
{
auto now = m_systemClock->getNow();