tests: rewrite and improve KeyChain signing test case

Added testing of:
 * non-default identity
 * non-default key
 * different types of asymmetric keys
 * SigningInfo(const std::string&) overload

Change-Id: Id16c77250cc25203f4fa49ecdbb9ee7ded218052
diff --git a/tests/unit/security/key-chain.t.cpp b/tests/unit/security/key-chain.t.cpp
index a9e1eaf..d3fb67e 100644
--- a/tests/unit/security/key-chain.t.cpp
+++ b/tests/unit/security/key-chain.t.cpp
@@ -27,6 +27,8 @@
 #include "tests/key-chain-fixture.hpp"
 #include "tests/unit/test-home-env-saver.hpp"
 
+#include <boost/mpl/vector.hpp>
+
 namespace ndn {
 namespace security {
 inline namespace v2 {
@@ -136,7 +138,7 @@
   const std::string PATH = "build/config-file-empty2-home/";
 };
 
-BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
+BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig2, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
 {
   createClientConf({"tpm=tpm-memory:"});
 
@@ -153,11 +155,10 @@
   const std::string PATH = "build/config-file-malformed-home/";
 };
 
-BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
+BOOST_FIXTURE_TEST_CASE(ConstructorBadConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
 {
   createClientConf({"pib=lord", "tpm=ring"});
-
-  BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+  BOOST_CHECK_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
 }
 
 struct PibPathConfigFileMalformed2Home
@@ -165,11 +166,10 @@
   const std::string PATH = "build/config-file-malformed2-home/";
 };
 
-BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
+BOOST_FIXTURE_TEST_CASE(ConstructorBadConfig2, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
 {
   createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
-
-  BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+  BOOST_CHECK_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
 }
 
 struct PibPathConfigFileNonCanonicalTpm
@@ -222,6 +222,13 @@
   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)
 {
   Name identityName("/test/id");
@@ -315,172 +322,252 @@
   m_keyChain.deleteIdentity(id);
   // The identity instance should not be valid any more
   BOOST_CHECK(!id);
-  BOOST_REQUIRE_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
+  BOOST_CHECK_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
   BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
 }
 
-BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, KeyChainFixture)
+struct DataPkt
 {
-  Identity id = m_keyChain.createIdentity("/id");
-  Key key = id.getDefaultKey();
-  Certificate cert = key.getDefaultCertificate();
+  Data packet{"/data"};
+  SignedInterestFormat sigFormat = SignedInterestFormat::V02; // irrelevant for Data
 
-  Name hmacKeyName = m_keyChain.createHmacKey();
-  const Tpm& tpm = m_keyChain.getTpm();
+  SignatureInfo
+  getSignatureInfo() const
+  {
+    return packet.getSignatureInfo();
+  }
+};
 
-  std::vector<SigningInfo> signingInfos = {
-    // New signed Interest format
-    SigningInfo().setSignedInterestFormat(SignedInterestFormat::V03),
+struct InterestV02Pkt
+{
+  Interest packet = Interest{"/interest02"}.setCanBePrefix(false);
+  SignedInterestFormat sigFormat = SignedInterestFormat::V02;
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName())
-      .setSignedInterestFormat(SignedInterestFormat::V03),
-    signingByIdentity(id.getName()).setSignedInterestFormat(SignedInterestFormat::V03),
+  SignatureInfo
+  getSignatureInfo() const
+  {
+    return SignatureInfo(packet.getName()[signed_interest::POS_SIG_INFO].blockFromValue());
+  }
+};
 
-    SigningInfo(id).setSignedInterestFormat(SignedInterestFormat::V03),
-    signingByIdentity(id).setSignedInterestFormat(SignedInterestFormat::V03),
+struct InterestV03Pkt
+{
+  Interest packet = Interest{"/interest03"}.setCanBePrefix(false);
+  SignedInterestFormat sigFormat = SignedInterestFormat::V03;
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName())
-      .setSignedInterestFormat(SignedInterestFormat::V03),
-    signingByKey(key.getName()).setSignedInterestFormat(SignedInterestFormat::V03),
+  SignatureInfo
+  getSignatureInfo() const
+  {
+    return packet.getSignatureInfo().value(); // use .value() for checked access
+  }
+};
 
-    SigningInfo(key).setSignedInterestFormat(SignedInterestFormat::V03),
-    signingByKey(key).setSignedInterestFormat(SignedInterestFormat::V03),
+template<typename KeyParams>
+struct DefaultIdentity
+{
+  Identity
+  operator()(KeyChain& keyChain) const
+  {
+    auto id = keyChain.createIdentity("/id", KeyParams());
+    BOOST_ASSERT(keyChain.getPib().getDefaultIdentity() == id);
+    return id;
+  }
+};
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName())
-      .setSignedInterestFormat(SignedInterestFormat::V03),
-    signingByCertificate(cert.getName()).setSignedInterestFormat(SignedInterestFormat::V03),
-    signingByCertificate(cert).setSignedInterestFormat(SignedInterestFormat::V03),
+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;
+  }
+};
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, hmacKeyName)
-      .setSignedInterestFormat(SignedInterestFormat::V03),
-    SigningInfo("hmac-sha256:QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENUI0OTdGREZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg==")
-      .setSignedInterestFormat(SignedInterestFormat::V03),
+template<typename KeyParams>
+struct DefaultKey
+{
+  Key
+  operator()(KeyChain&, const Identity& id) const
+  {
+    auto key = id.getDefaultKey();
+    BOOST_ASSERT(key.getKeyType() == KeyParams().getKeyType());
+    return key;
+  }
+};
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_SHA256)
-      .setSignedInterestFormat(SignedInterestFormat::V03),
-    signingWithSha256().setSignedInterestFormat(SignedInterestFormat::V03),
+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;
+  }
+};
 
-    // Deprecated signed Interest format
+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();
+
+  const uint32_t expectedSigType = SignatureTypeTlvValue;
+  const bool shouldHaveKeyLocator = true;
+  const optional<KeyLocator> expectedKeyLocator = cert.getName().getPrefix(-2);
+
+  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(SigningInfo::SIGNER_TYPE_ID, id.getName()),
-    signingByIdentity(id.getName()),
+    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(id),
-    signingByIdentity(id),
+    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_KEY, key.getName()),
-    signingByKey(key.getName()),
+    SigningInfo(SigningInfo::SIGNER_TYPE_CERT, this->cert.getName()),
+    SigningInfo("cert:" + this->cert.getName().toUri()),
+    signingByCertificate(this->cert),
+    signingByCertificate(this->cert.getName()),
+  };
+};
 
-    SigningInfo(key),
-    signingByKey(key),
+template<typename PacketType>
+using RsaSigning = AsymmetricSigning<PacketType, RsaKeyParams, tlv::SignatureSha256WithRsa>;
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
-    signingByCertificate(cert.getName()),
-    signingByCertificate(cert),
+template<typename PacketType>
+using EcdsaSigning = AsymmetricSigning<PacketType, EcKeyParams, tlv::SignatureSha256WithEcdsa>;
 
-    SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, hmacKeyName),
+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=="),
+  };
 
+  const uint32_t expectedSigType = SignatureTypeTlvValue;
+  const bool shouldHaveKeyLocator = true;
+  const optional<KeyLocator> expectedKeyLocator = 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()
   };
 
-  for (const auto& signingInfo : signingInfos) {
-    BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
-    Data data("/data");
-    Interest interest("/interest");
-    interest.setCanBePrefix(false);
+  const uint32_t expectedSigType = tlv::DigestSha256;
+  const bool shouldHaveKeyLocator = false;
+  const optional<KeyLocator> expectedKeyLocator = nullopt;
 
-    m_keyChain.sign(data, signingInfo);
-    m_keyChain.sign(interest, signingInfo);
+  bool
+  verify(const SigningInfo&) const
+  {
+    return verifyDigest(this->packet, DigestAlgorithm::SHA256);
+  }
+};
 
-    if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
-      BOOST_CHECK_EQUAL(data.getSignatureType(), tlv::DigestSha256);
+using SigningTests = boost::mpl::vector<
+  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>,
+  SigningWithNonDefaultKey<DataPkt>
+>;
 
-      if (signingInfo.getSignedInterestFormat() == SignedInterestFormat::V03) {
-        BOOST_REQUIRE(interest.getSignatureInfo() != nullopt);
-        BOOST_CHECK_EQUAL(interest.getSignatureInfo()->getSignatureType(), tlv::DigestSha256);
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(SigningInterface, T, SigningTests, T)
+{
+  BOOST_TEST_CONTEXT("Packet = " << this->packet.getName()) {
+    for (auto signingInfo : this->signingInfos) {
+      signingInfo.setSignedInterestFormat(this->sigFormat);
+
+      BOOST_TEST_CONTEXT("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));
       }
-      else {
-        SignatureInfo sigInfo(interest.getName()[signed_interest::POS_SIG_INFO].blockFromValue());
-        BOOST_CHECK_EQUAL(sigInfo.getSignatureType(), tlv::DigestSha256);
-      }
-
-      BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
-      BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
-    }
-    else if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_HMAC) {
-      Name keyName = signingInfo.getSignerName();
-      BOOST_CHECK_EQUAL(data.getSignatureType(), tlv::SignatureHmacWithSha256);
-
-      if (signingInfo.getSignedInterestFormat() == SignedInterestFormat::V03) {
-        BOOST_REQUIRE(interest.getSignatureInfo() != nullopt);
-        BOOST_CHECK_EQUAL(interest.getSignatureInfo()->getSignatureType(),
-                          tlv::SignatureHmacWithSha256);
-      }
-      else {
-        SignatureInfo sigInfo(interest.getName()[signed_interest::POS_SIG_INFO].blockFromValue());
-        BOOST_CHECK_EQUAL(sigInfo.getSignatureType(), tlv::SignatureHmacWithSha256);
-      }
-
-      BOOST_CHECK(verifySignature(data, tpm, keyName, DigestAlgorithm::SHA256));
-      BOOST_CHECK(verifySignature(interest, tpm, keyName, DigestAlgorithm::SHA256));
-    }
-    else {
-      BOOST_CHECK_EQUAL(data.getSignatureType(), tlv::SignatureSha256WithEcdsa);
-
-      if (signingInfo.getSignedInterestFormat() == SignedInterestFormat::V03) {
-        BOOST_REQUIRE(interest.getSignatureInfo() != nullopt);
-        BOOST_CHECK_EQUAL(interest.getSignatureInfo()->getSignatureType(),
-                          tlv::SignatureSha256WithEcdsa);
-      }
-      else {
-        SignatureInfo sigInfo(interest.getName()[signed_interest::POS_SIG_INFO].blockFromValue());
-        BOOST_CHECK_EQUAL(sigInfo.getSignatureType(), tlv::SignatureSha256WithEcdsa);
-      }
-
-      BOOST_CHECK_EQUAL(data.getKeyLocator()->getName(), cert.getName().getPrefix(-2));
-
-      if (signingInfo.getSignedInterestFormat() == SignedInterestFormat::V03) {
-        BOOST_CHECK_EQUAL(interest.getSignatureInfo()->getKeyLocator().getName(),
-                          cert.getName().getPrefix(-2));
-      }
-      else {
-        SignatureInfo sigInfo(interest.getName()[signed_interest::POS_SIG_INFO].blockFromValue());
-        BOOST_CHECK_EQUAL(sigInfo.getKeyLocator().getName(), cert.getName().getPrefix(-2));
-      }
-
-      BOOST_CHECK(verifySignature(data, key));
-      BOOST_CHECK(verifySignature(interest, key));
     }
   }
 }
 
-BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, KeyChainFixture)
-{
-  Data data("/test/data");
-
-  // Identity will be created with generated key and self-signed cert with default parameters
-  BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), KeyChain::InvalidSigningInfoError);
-
-  // Create identity with EC key and the corresponding self-signed certificate
-  Identity id = m_keyChain.createIdentity("/ndn/test/ec", EcKeyParams());
-  BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
-  BOOST_CHECK_EQUAL(data.getSignatureType(),
-                    KeyChain::getSignatureType(EcKeyParams().getKeyType(), DigestAlgorithm::SHA256));
-  BOOST_REQUIRE(data.getKeyLocator().has_value());
-  BOOST_CHECK(id.getName().isPrefixOf(data.getKeyLocator()->getName()));
-
-  // Create identity with RSA key and the corresponding self-signed certificate
-  id = m_keyChain.createIdentity("/ndn/test/rsa", RsaKeyParams());
-  BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
-  BOOST_CHECK_EQUAL(data.getSignatureType(),
-                    KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
-  BOOST_REQUIRE(data.getKeyLocator().has_value());
-  BOOST_CHECK(id.getName().isPrefixOf(data.getKeyLocator()->getName()));
-}
-
 BOOST_FIXTURE_TEST_CASE(ImportPrivateKey, KeyChainFixture)
 {
   Name keyName("/test/device2");