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
 }