security: Generalize signature verification to allow digest-sha256
This patch effectively enables use of DigestSha256 signatures in
ValidatorConfig.
Change-Id: I233c069935b617efb8a52cc45996f00307c86a2e
diff --git a/ndn-cxx/security/validation-state.cpp b/ndn-cxx/security/validation-state.cpp
index 294d9fe..4bb1ab9 100644
--- a/ndn-cxx/security/validation-state.cpp
+++ b/ndn-cxx/security/validation-state.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -105,7 +105,7 @@
}
void
-DataValidationState::verifyOriginalPacket(const Certificate& trustedCert)
+DataValidationState::verifyOriginalPacket(const optional<Certificate>& trustedCert)
{
if (verifySignature(m_data, trustedCert)) {
NDN_LOG_TRACE_DEPTH("OK signature for data `" << m_data.getName() << "`");
@@ -165,7 +165,7 @@
}
void
-InterestValidationState::verifyOriginalPacket(const Certificate& trustedCert)
+InterestValidationState::verifyOriginalPacket(const optional<Certificate>& trustedCert)
{
if (verifySignature(m_interest, trustedCert)) {
NDN_LOG_TRACE_DEPTH("OK signature for interest `" << m_interest.getName() << "`");
diff --git a/ndn-cxx/security/validation-state.hpp b/ndn-cxx/security/validation-state.hpp
index 063bc8b..ee538c4 100644
--- a/ndn-cxx/security/validation-state.hpp
+++ b/ndn-cxx/security/validation-state.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -108,10 +108,12 @@
/**
* @brief Verify signature of the original packet
*
- * @param trustCert The certificate that signs the original packet
+ * @param trustCert The certificate that signs the original packet, nullopt if certificate is
+ * not needed for verification (e.g., sha256 digest or attribute based
+ * signature)
*/
virtual void
- verifyOriginalPacket(const Certificate& trustedCert) = 0;
+ verifyOriginalPacket(const optional<Certificate>& trustedCert) = 0;
/**
* @brief Call success callback of the original packet without signature validation
@@ -188,7 +190,7 @@
private:
void
- verifyOriginalPacket(const Certificate& trustedCert) final;
+ verifyOriginalPacket(const optional<Certificate>& trustedCert) final;
void
bypassValidation() final;
@@ -237,7 +239,7 @@
private:
void
- verifyOriginalPacket(const Certificate& trustedCert) final;
+ verifyOriginalPacket(const optional<Certificate>& trustedCert) final;
void
bypassValidation() final;
diff --git a/ndn-cxx/security/validator.cpp b/ndn-cxx/security/validator.cpp
index 479bbb9..2a86be4 100644
--- a/ndn-cxx/security/validator.cpp
+++ b/ndn-cxx/security/validator.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -150,6 +150,11 @@
return;
}
+ if (certRequest->interest.getName() == SigningInfo::getDigestSha256Identity()) {
+ state->verifyOriginalPacket(nullopt);
+ return;
+ }
+
if (state->hasSeenCertificateName(certRequest->interest.getName())) {
state->fail({ValidationError::Code::LOOP_DETECTED,
"Validation loop detected for certificate `" + certRequest->interest.getName().toUri() + "`"});
diff --git a/ndn-cxx/security/verification-helpers.cpp b/ndn-cxx/security/verification-helpers.cpp
index 17df3f9..60bc7d1 100644
--- a/ndn-cxx/security/verification-helpers.cpp
+++ b/ndn-cxx/security/verification-helpers.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -45,14 +45,16 @@
public:
ParseResult() = default;
- ParseResult(InputBuffers bufs, const uint8_t* sig, size_t sigLen)
- : bufs(std::move(bufs))
+ ParseResult(SignatureInfo info, InputBuffers bufs, const uint8_t* sig, size_t sigLen)
+ : info(std::move(info))
+ , bufs(std::move(bufs))
, sig(sig)
, sigLen(sigLen)
{
}
public:
+ SignatureInfo info;
InputBuffers bufs;
const uint8_t* sig = nullptr;
size_t sigLen = 0;
@@ -110,7 +112,8 @@
parse(const Data& data)
{
try {
- return ParseResult(data.extractSignedRanges(),
+ return ParseResult(data.getSignatureInfo(),
+ data.extractSignedRanges(),
data.getSignatureValue().value(),
data.getSignatureValue().value_size());
}
@@ -128,7 +131,8 @@
if (interest.getSignatureInfo() && interest.getSignatureValue().isValid()) {
// Verify using v0.3 Signed Interest semantics
Block sigValue = interest.getSignatureValue();
- return ParseResult(interest.extractSignedRanges(),
+ return ParseResult(*interest.getSignatureInfo(),
+ interest.extractSignedRanges(),
sigValue.value(),
sigValue.value_size());
}
@@ -141,7 +145,9 @@
const Block& nameBlock = interestName.wireEncode();
Block sigValue = interestName[signed_interest::POS_SIG_VALUE].blockFromValue();
- return ParseResult({{nameBlock.value(),
+ SignatureInfo info(interestName[signed_interest::POS_SIG_INFO].blockFromValue());
+ return ParseResult(info,
+ {{nameBlock.value(),
nameBlock.value_size() - interestName[signed_interest::POS_SIG_VALUE].size()}},
sigValue.value(),
sigValue.value_size());
@@ -208,15 +214,39 @@
}
bool
-verifySignature(const Data& data, const v2::Certificate& cert)
+verifySignature(const Data& data, const optional<v2::Certificate>& cert)
{
- return verifySignature(parse(data), cert.getContent().value(), cert.getContent().value_size());
+ auto parsed = parse(data);
+ if (cert) {
+ return verifySignature(parsed, cert->getContent().value(), cert->getContent().value_size());
+ }
+ else {
+ if (parsed.info.getSignatureType() == tlv::SignatureTypeValue::DigestSha256) {
+ return verifyDigest(data, DigestAlgorithm::SHA256);
+ }
+ // Add any other self-verifying signatures here (if any)
+ else {
+ return false;
+ }
+ }
}
bool
-verifySignature(const Interest& interest, const v2::Certificate& cert)
+verifySignature(const Interest& interest, const optional<v2::Certificate>& cert)
{
- return verifySignature(parse(interest), cert.getContent().value(), cert.getContent().value_size());
+ auto parsed = parse(interest);
+ if (cert) {
+ return verifySignature(parsed, cert->getContent().value(), cert->getContent().value_size());
+ }
+ else {
+ if (parsed.info.getSignatureType() == tlv::SignatureTypeValue::DigestSha256) {
+ return verifyDigest(interest, DigestAlgorithm::SHA256);
+ }
+ // Add any other self-verifying signatures here (if any)
+ else {
+ return false;
+ }
+ }
}
bool
diff --git a/ndn-cxx/security/verification-helpers.hpp b/ndn-cxx/security/verification-helpers.hpp
index 2e8f913..2826999 100644
--- a/ndn-cxx/security/verification-helpers.hpp
+++ b/ndn-cxx/security/verification-helpers.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -124,17 +124,21 @@
/**
* @brief Verify @p data using @p cert.
+ *
+ * If @p cert is nullopt, @p data assumed to be self-verifiable (with digest or attributes)
*/
bool
-verifySignature(const Data& data, const v2::Certificate& cert);
+verifySignature(const Data& data, const optional<v2::Certificate>& cert);
/**
* @brief Verify @p interest using @p cert.
* @note This method verifies only signature of the signed interest.
* @sa docs/specs/signed-interest.rst
+ *
+ * If @p cert is nullptr, @p interest assumed to be self-verifiable (with digest or attributes)
*/
bool
-verifySignature(const Interest& interest, const v2::Certificate& cert);
+verifySignature(const Interest& interest, const optional<v2::Certificate>& cert);
/**
* @brief Verify @p data using @p tpm and @p keyName with the @p digestAlgorithm.
diff --git a/tests/unit/security/validation-policy-config.t.cpp b/tests/unit/security/validation-policy-config.t.cpp
index 0ee5172..3c67315 100644
--- a/tests/unit/security/validation-policy-config.t.cpp
+++ b/tests/unit/security/validation-policy-config.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -382,7 +382,11 @@
packet = unsignedPacket;
this->m_keyChain.sign(packet, signingWithSha256());
- VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+ VALIDATE_FAILURE(packet, "Should not be accepted, doesn't pass checker /localhost/identity/digest-sha256");
+
+ packet = Packet("/localhost/identity/digest-sha256/foobar");
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Should not be accepted, no rule for the name /localhost/identity/digest-sha256");
packet = unsignedPacket;
this->m_keyChain.sign(packet, signingByIdentity(this->identity));
@@ -417,7 +421,11 @@
packet = unsignedPacket;
this->m_keyChain.sign(packet, signingWithSha256());
- VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+ VALIDATE_FAILURE(packet, "Should not be accepted, doesn't pass checker /localhost/identity/digest-sha256");
+
+ packet = Packet("/localhost/identity/digest-sha256/foobar");
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Should not be accepted, no rule for the name /localhost/identity/digest-sha256");
packet = unsignedPacket;
this->m_keyChain.sign(packet, signingByIdentity(this->identity));
@@ -436,6 +444,79 @@
VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
}
+BOOST_FIXTURE_TEST_CASE(DigestSha256, HierarchicalValidatorFixture<ValidationPolicyConfig>)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+ this->policy.load(R"CONF(
+ rule
+ {
+ id test-rule-data-id
+ for data
+ filter
+ {
+ type name
+ name /localhost/identity/digest-sha256
+ relation is-prefix-of
+ }
+ checker
+ {
+ type customized
+ sig-type sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^(<>*)$
+ k-expand \\1
+ h-relation is-prefix-of
+ p-regex ^(<>*)$
+ p-expand \\1
+ }
+ }
+ }
+ }
+ rule
+ {
+ id test-rule-interest-id
+ for interest
+ filter
+ {
+ type name
+ name /localhost/identity/digest-sha256
+ relation is-prefix-of
+ }
+ checker
+ {
+ type customized
+ sig-type sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^(<>*)$
+ k-expand \\1
+ h-relation is-prefix-of
+ p-regex ^(<>*)$
+ p-expand \\1
+ }
+ }
+ }
+ }
+ )CONF", "test-config");
+
+
+ Interest interest("/localhost/identity/digest-sha256/foobar");
+ interest.setCanBePrefix(false);
+ this->m_keyChain.sign(interest, signingWithSha256());
+ VALIDATE_SUCCESS(interest, "Should be accepted");
+
+ Data data("/localhost/identity/digest-sha256/foobar");
+ this->m_keyChain.sign(data, signingWithSha256());
+ VALIDATE_SUCCESS(data, "Should be accepted");
+}
+
BOOST_FIXTURE_TEST_CASE(Reload, HierarchicalValidatorFixture<ValidationPolicyConfig>)
{
BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
diff --git a/tests/unit/security/validation-policy-simple-hierarchy.t.cpp b/tests/unit/security/validation-policy-simple-hierarchy.t.cpp
index c0729f4..d5734a8 100644
--- a/tests/unit/security/validation-policy-simple-hierarchy.t.cpp
+++ b/tests/unit/security/validation-policy-simple-hierarchy.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -46,7 +46,11 @@
packet = Packet::makePacket(name);
m_keyChain.sign(packet, signingWithSha256());
- VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+ VALIDATE_FAILURE(packet, "Should not be accepted, name not prefix of /localhost/identity/digest-sha256");
+
+ packet = Packet::makePacket("/localhost/identity/digest-sha256/foobar");
+ m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_SUCCESS(packet, "Should be accepted, as name is prefix of /localhost/identity/digest-sha256");
packet = Packet::makePacket(name);
m_keyChain.sign(packet, signingByIdentity(identity));
diff --git a/tests/unit/security/validator-fixture.hpp b/tests/unit/security/validator-fixture.hpp
index 343e363..c56a014 100644
--- a/tests/unit/security/validator-fixture.hpp
+++ b/tests/unit/security/validator-fixture.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -159,7 +159,7 @@
private:
void
- verifyOriginalPacket(const Certificate&) override
+ verifyOriginalPacket(const optional<Certificate>&) override
{
// do nothing
}
diff --git a/tests/unit/security/verification-helpers.t.cpp b/tests/unit/security/verification-helpers.t.cpp
index 476bbe3..d930281 100644
--- a/tests/unit/security/verification-helpers.t.cpp
+++ b/tests/unit/security/verification-helpers.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2020 Regents of the University of California.
+ * Copyright (c) 2013-2021 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -640,14 +640,21 @@
Interest badSigInterestOldFormat(Block(dataset.badSigInterestOldFormat.data(),
dataset.badSigInterestOldFormat.size()));
+ BOOST_CHECK(verifySignature(data, nullopt));
BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
+ BOOST_CHECK(verifySignature(interest, nullopt));
BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
+ BOOST_CHECK(verifySignature(interestOldFormat, nullopt));
BOOST_CHECK(verifyDigest(interestOldFormat, DigestAlgorithm::SHA256));
BOOST_CHECK(!verifyDigest(badSigData, DigestAlgorithm::SHA256));
BOOST_CHECK(!verifyDigest(badSigInterest, DigestAlgorithm::SHA256));
BOOST_CHECK(!verifyDigest(badSigInterestOldFormat, DigestAlgorithm::SHA256));
+ BOOST_CHECK(!verifySignature(badSigData, nullopt));
+ BOOST_CHECK(!verifySignature(badSigInterest, nullopt));
+ BOOST_CHECK(!verifySignature(badSigInterestOldFormat, nullopt));
+
Data unsignedData("/some/data");
Interest unsignedInterest1("/some/interest/with/several/name/components");
unsignedInterest1.setCanBePrefix(false);
@@ -658,6 +665,10 @@
BOOST_CHECK(!verifyDigest(unsignedInterest1, DigestAlgorithm::SHA256));
BOOST_CHECK(!verifyDigest(unsignedInterest2, DigestAlgorithm::SHA256));
+ BOOST_CHECK(!verifySignature(unsignedData, nullopt));
+ BOOST_CHECK(!verifySignature(unsignedInterest1, nullopt));
+ BOOST_CHECK(!verifySignature(unsignedInterest2, nullopt));
+
// - base version of verifyDigest is tested transitively
}