security: add HMAC support in KeyChain and SigningInfo
Refs: #3075
Change-Id: Ia1f557ca7c83f4df7f9f87bbe1b4fc214940dcd8
diff --git a/tests/unit/security/signing-info.t.cpp b/tests/unit/security/signing-info.t.cpp
index 816cb52..e32d5b1 100644
--- a/tests/unit/security/signing-info.t.cpp
+++ b/tests/unit/security/signing-info.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -88,6 +88,16 @@
BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ std::string encodedKey("QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENUI0OTdGRE"
+ "ZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg==");
+ info.setSigningHmacKey(encodedKey);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_HMAC);
+ BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoHmac(SigningInfo::SIGNER_TYPE_HMAC, info.getSignerName());
+ BOOST_CHECK_EQUAL(infoHmac.getSignerType(), SigningInfo::SIGNER_TYPE_HMAC);
+ BOOST_CHECK_EQUAL(infoHmac.getDigestAlgorithm(), DigestAlgorithm::SHA256);
}
BOOST_AUTO_TEST_CASE(CustomSignatureInfo)
@@ -127,6 +137,12 @@
BOOST_CHECK_EQUAL(infoCert.getSignerName(), "/my-cert");
BOOST_CHECK_EQUAL(infoCert.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+ SigningInfo infoHmac("hmac-sha256:QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENU"
+ "I0OTdGREZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg==");
+ BOOST_CHECK_EQUAL(infoHmac.getSignerType(), SigningInfo::SIGNER_TYPE_HMAC);
+ BOOST_CHECK_EQUAL(infoHmac.getSignerName().getPrefix(3), SigningInfo::getHmacIdentity());
+ BOOST_CHECK_EQUAL(infoHmac.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
SigningInfo infoSha("id:/localhost/identity/digest-sha256");
BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
@@ -149,6 +165,9 @@
BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
SigningInfo(SigningInfo::SIGNER_TYPE_SHA256)),
"id:/localhost/identity/digest-sha256");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+ SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, "/localhost/identity/hmac/1234")),
+ "id:/localhost/identity/hmac/1234");
}
BOOST_AUTO_TEST_CASE(Chaining)
diff --git a/tests/unit/security/tpm/back-end.t.cpp b/tests/unit/security/tpm/back-end.t.cpp
index 1ef374b..aee042f 100644
--- a/tests/unit/security/tpm/back-end.t.cpp
+++ b/tests/unit/security/tpm/back-end.t.cpp
@@ -87,6 +87,28 @@
BOOST_CHECK(tpm.getKeyHandle(keyName) == nullptr);
}
+BOOST_AUTO_TEST_CASE(CreateHmacKey)
+{
+ Name identity("/Test/Identity/HMAC");
+
+ BackEndWrapperMem mem;
+ BackEnd& memTpm = mem.getTpm();
+ auto key = memTpm.createKey(identity, HmacKeyParams());
+ BOOST_REQUIRE(key != nullptr);
+ BOOST_CHECK(!key->getKeyName().empty());
+ BOOST_CHECK(memTpm.hasKey(key->getKeyName()));
+
+ BackEndWrapperFile file;
+ BackEnd& fileTpm = file.getTpm();
+ BOOST_CHECK_THROW(fileTpm.createKey(identity, HmacKeyParams()), BackEnd::Error);
+
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+ BackEndWrapperOsx osx;
+ BackEnd& osxTpm = osx.getTpm();
+ BOOST_CHECK_THROW(osxTpm.createKey(identity, HmacKeyParams()), BackEnd::Error);
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
+}
+
BOOST_AUTO_TEST_CASE_TEMPLATE(RsaSigning, T, TestBackEnds)
{
T wrapper;
diff --git a/tests/unit/security/transform/private-key.t.cpp b/tests/unit/security/transform/private-key.t.cpp
index 79b3599..2317479 100644
--- a/tests/unit/security/transform/private-key.t.cpp
+++ b/tests/unit/security/transform/private-key.t.cpp
@@ -53,6 +53,7 @@
PrivateKey sKey;
BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::NONE);
BOOST_CHECK_EQUAL(sKey.getKeySize(), 0);
+ BOOST_CHECK_THROW(sKey.getKeyDigest(DigestAlgorithm::SHA256), PrivateKey::Error);
BOOST_CHECK_THROW(sKey.derivePublicKey(), PrivateKey::Error);
const uint8_t theAnswer = 42;
BOOST_CHECK_THROW(sKey.decrypt(&theAnswer, sizeof(theAnswer)), PrivateKey::Error);
@@ -62,6 +63,21 @@
BOOST_CHECK_THROW(sKey.savePkcs8(os, passwd.data(), passwd.size()), PrivateKey::Error);
}
+BOOST_AUTO_TEST_CASE(KeyDigest)
+{
+ const Buffer buf(16);
+ PrivateKey sKey;
+ sKey.loadRaw(KeyType::HMAC, buf.data(), buf.size());
+ auto digest = sKey.getKeyDigest(DigestAlgorithm::SHA256);
+
+ const uint8_t expected[] = {
+ 0x37, 0x47, 0x08, 0xff, 0xf7, 0x71, 0x9d, 0xd5, 0x97, 0x9e, 0xc8, 0x75, 0xd5, 0x6c, 0xd2, 0x28,
+ 0x6f, 0x6d, 0x3c, 0xf7, 0xec, 0x31, 0x7a, 0x3b, 0x25, 0x63, 0x2a, 0xab, 0x28, 0xec, 0x37, 0xbb,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(digest->begin(), digest->end(),
+ expected, expected + sizeof(expected));
+}
+
BOOST_AUTO_TEST_CASE(LoadRaw)
{
const Buffer buf(32);
diff --git a/tests/unit/security/v2/key-chain.t.cpp b/tests/unit/security/v2/key-chain.t.cpp
index 8f5844b..17f8569 100644
--- a/tests/unit/security/v2/key-chain.t.cpp
+++ b/tests/unit/security/v2/key-chain.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -22,6 +22,7 @@
#include "ndn-cxx/security/v2/key-chain.hpp"
#include "ndn-cxx/security/signing-helpers.hpp"
#include "ndn-cxx/security/verification-helpers.hpp"
+#include "ndn-cxx/security/transform/private-key.hpp"
#include "tests/boost-test.hpp"
#include "tests/identity-management-fixture.hpp"
@@ -222,12 +223,11 @@
BOOST_CHECK(id);
BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) != m_keyChain.getPib().getIdentities().end());
// The first added identity becomes the default identity
- BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
+ BOOST_CHECK_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
// The default key of the added identity must exist
- Key key;
- BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
+ Key key = id.getDefaultKey();
// The default certificate of the default key must exist
- BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
+ BOOST_CHECK_NO_THROW(key.getDefaultCertificate());
// Delete key
Name key1Name = key.getName();
@@ -242,12 +242,11 @@
// Create another key
m_keyChain.createKey(id);
// The added key becomes the default key.
- BOOST_REQUIRE_NO_THROW(id.getDefaultKey());
Key key2 = id.getDefaultKey();
BOOST_REQUIRE(key2);
BOOST_CHECK_NE(key2.getName(), key1Name);
BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
- BOOST_REQUIRE_NO_THROW(key2.getDefaultCertificate());
+ BOOST_CHECK_NO_THROW(key2.getDefaultCertificate());
// Create the third key
Key key3 = m_keyChain.createKey(id);
@@ -255,7 +254,7 @@
// The added key will not be the default key, because the default key already exists
BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
- BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+ BOOST_CHECK_NO_THROW(key3.getDefaultCertificate());
// Delete cert
BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
@@ -268,7 +267,7 @@
// Add cert
m_keyChain.addCertificate(key3, key3Cert1);
BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
- BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+ 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
@@ -310,7 +309,10 @@
Key key = id.getDefaultKey();
Certificate cert = key.getDefaultCertificate();
- std::list<SigningInfo> signingInfos = {
+ Name hmacKeyName = m_keyChain.createHmacKey();
+ const Tpm& tpm = m_keyChain.getTpm();
+
+ std::vector<SigningInfo> signingInfos = {
SigningInfo(),
SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
@@ -329,6 +331,9 @@
signingByCertificate(cert.getName()),
signingByCertificate(cert),
+ SigningInfo(SigningInfo::SIGNER_TYPE_HMAC, hmacKeyName),
+ SigningInfo("hmac-sha256:QjM3NEEyNkE3MTQ5MDQzN0FBMDI0RTRGQURENUI0OTdGREZGMUE4RUE2RkYxMkY2RkI2NUFGMjcyMEI1OUNDRg=="),
+
SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
signingWithSha256()
};
@@ -356,6 +361,25 @@
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.getSignature().getType(), tlv::SignatureHmacWithSha256);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureHmacWithSha256);
+
+ BOOST_CHECK(bool(tpm.verify(data.wireEncode().value(),
+ data.wireEncode().value_size() - data.getSignature().getValue().size(),
+ data.getSignature().getValue().value(),
+ data.getSignature().getValue().value_size(),
+ keyName, DigestAlgorithm::SHA256)));
+
+ const Name& interestName = interest.getName();
+ auto nameBlock = interestName.wireEncode();
+ BOOST_CHECK(bool(tpm.verify(nameBlock.value(),
+ nameBlock.value_size() - interestName[signed_interest::POS_SIG_VALUE].size(),
+ interestName[signed_interest::POS_SIG_VALUE].blockFromValue().value(),
+ interestName[signed_interest::POS_SIG_VALUE].blockFromValue().value_size(),
+ keyName, DigestAlgorithm::SHA256)));
+ }
else {
BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
@@ -391,9 +415,21 @@
BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
}
+BOOST_FIXTURE_TEST_CASE(ImportPrivateKey, IdentityManagementFixture)
+{
+ Name keyName("/test/device2");
+ std::string rawKey("nPSNOHyZKsg2WLqHAs7MXGb0sjQb4zCT");
+ auto key = make_shared<transform::PrivateKey>();
+ key->loadRaw(KeyType::HMAC, reinterpret_cast<const uint8_t*>(rawKey.data()), rawKey.size());
+
+ 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, IdentityManagementFixture)
{
- Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
+ Identity id = addIdentity("/TestKeyChain/ExportIdentity");
Certificate cert = id.getDefaultKey().getDefaultCertificate();
shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
@@ -412,13 +448,11 @@
BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true);
BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1);
- BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getIdentity(cert.getIdentity()));
Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity());
BOOST_CHECK_EQUAL(newId.getKeys().size(), 1);
- BOOST_REQUIRE_NO_THROW(newId.getKey(cert.getKeyName()));
Key newKey = newId.getKey(cert.getKeyName());
BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1);
- BOOST_REQUIRE_NO_THROW(newKey.getCertificate(cert.getName()));
+ BOOST_CHECK_NO_THROW(newKey.getCertificate(cert.getName()));
m_keyChain.deleteIdentity(newId);
BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);