Rename 'tests/unit-tests' directory to 'tests/unit'

Change-Id: I78ea29938259fac288781bed12fb2399ac7eba26
diff --git a/tests/unit/security/command-interest-signer.t.cpp b/tests/unit/security/command-interest-signer.t.cpp
new file mode 100644
index 0000000..784a209
--- /dev/null
+++ b/tests/unit/security/command-interest-signer.t.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/command-interest-signer.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestCommandInterestSigner, IdentityManagementTimeFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  addIdentity("/test");
+
+  CommandInterestSigner signer(m_keyChain);
+  Interest i1 = signer.makeCommandInterest("/hello/world");
+  BOOST_CHECK_EQUAL(i1.getName().size(), 6);
+  BOOST_CHECK_EQUAL(i1.getName().at(command_interest::POS_SIG_VALUE).blockFromValue().type(), tlv::SignatureValue);
+  BOOST_CHECK_EQUAL(i1.getName().at(command_interest::POS_SIG_INFO).blockFromValue().type(), tlv::SignatureInfo);
+
+  time::milliseconds timestamp = toUnixTimestamp(time::system_clock::now());
+  BOOST_CHECK_EQUAL(i1.getName().at(command_interest::POS_TIMESTAMP).toNumber(), timestamp.count());
+
+  Interest i2 = signer.makeCommandInterest("/hello/world/!", signingByIdentity("/test"));
+  BOOST_CHECK_EQUAL(i2.getName().size(), 7);
+  BOOST_CHECK_EQUAL(i2.getName().at(command_interest::POS_SIG_VALUE).blockFromValue().type(), tlv::SignatureValue);
+  BOOST_CHECK_EQUAL(i2.getName().at(command_interest::POS_SIG_INFO).blockFromValue().type(), tlv::SignatureInfo);
+  BOOST_CHECK_GT(i2.getName().at(command_interest::POS_TIMESTAMP), i1.getName().at(command_interest::POS_TIMESTAMP));
+  BOOST_CHECK_NE(i2.getName().at(command_interest::POS_RANDOM_VAL),
+                 i1.getName().at(command_interest::POS_RANDOM_VAL)); // this sometimes can fail
+
+  advanceClocks(100_s);
+
+  i2 = signer.makeCommandInterest("/hello/world/!");
+  BOOST_CHECK_GT(i2.getName().at(command_interest::POS_TIMESTAMP), i1.getName().at(command_interest::POS_TIMESTAMP));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCommandInterestSigner
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/config-file-readme.txt b/tests/unit/security/config-file-readme.txt
new file mode 100644
index 0000000..44e5f61
--- /dev/null
+++ b/tests/unit/security/config-file-readme.txt
@@ -0,0 +1,11 @@
+In test, we set a test-specific "HOME", which cause OS X keychain look for the
+default keychain of a "different" user. If the default keychain does not exist,
+all subsequent calls to OS X keychain will fail. User interaction (such as
+specifying password) is required to create a keychain. However, user interaction
+is not feasible in automated tests.
+
+This problem is caused by the OS X system assumption that one user must have a
+login keychain, which is also the user's default keychain, because a user
+account is always created with a login keychain as default. Thus OS X system
+infers a user according to the HOME env, and did not expect user to change the
+HOME env in normal use.
diff --git a/tests/unit/security/digest-sha256.t.cpp b/tests/unit/security/digest-sha256.t.cpp
new file mode 100644
index 0000000..7c1d236
--- /dev/null
+++ b/tests/unit/security/digest-sha256.t.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/digest-sha256.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/sha256.hpp"
+#include "util/string-helper.hpp"
+
+#include "identity-management-fixture.hpp"
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestDigestSha256, IdentityManagementFixture)
+
+BOOST_AUTO_TEST_CASE(Sha256)
+{
+  char content[6] = "1234\n";
+  ConstBufferPtr buf = util::Sha256::computeDigest(reinterpret_cast<uint8_t*>(content), 5);
+
+  BOOST_CHECK_EQUAL(toHex(buf->data(), buf->size(), false),
+                    "a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4");
+}
+
+BOOST_AUTO_TEST_CASE(DataSignature)
+{
+  Name name("/TestSignatureSha/Basic");
+  Data testData(name);
+  char content[5] = "1234";
+  testData.setContent(reinterpret_cast<uint8_t*>(content), 5);
+  m_keyChain.sign(testData, security::SigningInfo(security::SigningInfo::SIGNER_TYPE_SHA256));
+
+  BOOST_CHECK_THROW(testData.getSignature().getKeyLocator(), ndn::SignatureInfo::Error);
+  verifyDigest(testData, DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_CASE(InterestSignature)
+{
+  Name name("/SecurityTestDigestSha256/InterestSignature/Interest1");
+  Interest testInterest(name);
+
+  m_keyChain.sign(testInterest, security::SigningInfo(security::SigningInfo::SIGNER_TYPE_SHA256));
+  verifyDigest(testInterest, DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDigestSha256
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/key-params.t.cpp b/tests/unit/security/key-params.t.cpp
new file mode 100644
index 0000000..991c346
--- /dev/null
+++ b/tests/unit/security/key-params.t.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/key-params.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestKeyParams)
+
+BOOST_AUTO_TEST_CASE(Rsa)
+{
+  RsaKeyParams params;
+  BOOST_CHECK_EQUAL(params.getKeyType(), KeyType::RSA);
+  BOOST_CHECK_EQUAL(params.getKeySize(), 2048);
+  BOOST_CHECK(params.getKeyIdType() == KeyIdType::RANDOM);
+
+  RsaKeyParams params2(4096, KeyIdType::SHA256);
+  BOOST_CHECK_EQUAL(params2.getKeyType(), KeyType::RSA);
+  BOOST_CHECK_EQUAL(params2.getKeySize(), 4096);
+  BOOST_CHECK(params2.getKeyIdType() == KeyIdType::SHA256);
+
+  BOOST_CHECK_THROW(RsaKeyParams(1024), KeyParams::Error);
+
+  name::Component keyId("keyId");
+  RsaKeyParams params4(keyId);
+  BOOST_CHECK(params4.getKeyType() == KeyType::RSA);
+  BOOST_CHECK_EQUAL(params4.getKeySize(), 2048);
+  BOOST_CHECK(params4.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+  BOOST_CHECK_EQUAL(params4.getKeyId(), keyId);
+}
+
+BOOST_AUTO_TEST_CASE(Ec)
+{
+  EcKeyParams params;
+  BOOST_CHECK_EQUAL(params.getKeyType(), KeyType::EC);
+  BOOST_CHECK_EQUAL(params.getKeySize(), 256);
+  BOOST_CHECK(params.getKeyIdType() == KeyIdType::RANDOM);
+
+  EcKeyParams params2(384, KeyIdType::SHA256);
+  BOOST_CHECK_EQUAL(params2.getKeyType(), KeyType::EC);
+  BOOST_CHECK_EQUAL(params2.getKeySize(), 384);
+  BOOST_CHECK(params2.getKeyIdType() == KeyIdType::SHA256);
+
+  BOOST_CHECK_THROW(EcKeyParams(3), KeyParams::Error);
+
+  name::Component keyId("keyId");
+  EcKeyParams params4(keyId);
+  BOOST_CHECK(params4.getKeyType() == KeyType::EC);
+  BOOST_CHECK_EQUAL(params4.getKeySize(), 256);
+  BOOST_CHECK(params4.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+  BOOST_CHECK_EQUAL(params4.getKeyId(), keyId);
+}
+
+BOOST_AUTO_TEST_CASE(Aes)
+{
+  name::Component keyId("keyId");
+  AesKeyParams params(keyId);
+  BOOST_CHECK_EQUAL(params.getKeyType(), KeyType::AES);
+  BOOST_CHECK_EQUAL(params.getKeySize(), 128);
+  BOOST_CHECK_EQUAL(params.getKeyIdType(), KeyIdType::USER_SPECIFIED);
+
+  AesKeyParams params2(keyId, 192);
+  BOOST_CHECK(params2.getKeyType() == KeyType::AES);
+  BOOST_CHECK_EQUAL(params2.getKeySize(), 192);
+  BOOST_CHECK(params.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+
+  AesKeyParams params3(keyId, 256);
+  BOOST_CHECK_EQUAL(params3.getKeyType(), KeyType::AES);
+  BOOST_CHECK_EQUAL(params3.getKeySize(), 256);
+  BOOST_CHECK(params.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+
+  BOOST_CHECK_THROW(AesKeyParams(keyId, 4), KeyParams::Error);
+
+  AesKeyParams params5(keyId);
+  BOOST_CHECK_EQUAL(params5.getKeyType(), KeyType::AES);
+  BOOST_CHECK_EQUAL(params5.getKeySize(), 128);
+  BOOST_CHECK_EQUAL(params5.getKeyIdType(), KeyIdType::USER_SPECIFIED);
+  BOOST_CHECK_EQUAL(params5.getKeyId(), keyId);
+
+  AesKeyParams params6(192);
+  BOOST_CHECK(params6.getKeyType() == KeyType::AES);
+  BOOST_CHECK_EQUAL(params6.getKeySize(), 192);
+  BOOST_CHECK(params6.getKeyIdType() == KeyIdType::RANDOM);
+}
+
+BOOST_AUTO_TEST_CASE(KeyIdTypeInfo)
+{
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(KeyIdType::USER_SPECIFIED), "USER_SPECIFIED");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(KeyIdType::SHA256), "SHA256");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(KeyIdType::RANDOM), "RANDOM");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<KeyIdType>(12345)), "12345");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyParams
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/security/pib/certificate-container.t.cpp b/tests/unit/security/pib/certificate-container.t.cpp
new file mode 100644
index 0000000..6614531
--- /dev/null
+++ b/tests/unit/security/pib/certificate-container.t.cpp
@@ -0,0 +1,167 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/certificate-container.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestCertificateContainer, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  // start with an empty container
+  CertificateContainer container(id1Key1Name, pibImpl);
+  BOOST_CHECK_EQUAL(container.size(), 0);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 0);
+
+  // add one cert
+  container.add(id1Key1Cert1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+
+  // add the same cert again
+  container.add(id1Key1Cert1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+
+  // add another cert
+  container.add(id1Key1Cert2);
+  BOOST_CHECK_EQUAL(container.size(), 2);
+  BOOST_CHECK_EQUAL(container.getCache().size(), 2);
+  BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+  BOOST_CHECK(container.find(id1Key1Cert2.getName()) != container.end());
+
+  // get certs
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key1Cert1.getName()));
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key1Cert2.getName()));
+  Name id1Key1Cert3Name = id1Key1Name;
+  id1Key1Cert3Name.append("issuer").appendVersion(3);
+  BOOST_CHECK_THROW(container.get(id1Key1Cert3Name), Pib::Error);
+
+  // check cert
+  v2::Certificate cert1 = container.get(id1Key1Cert1.getName());
+  v2::Certificate cert2 = container.get(id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(cert1, id1Key1Cert1);
+  BOOST_CHECK_EQUAL(cert2, id1Key1Cert2);
+
+  // create another container from the same PibImpl
+  // cache should be empty
+  CertificateContainer container2(id1Key1Name, pibImpl);
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 0);
+
+  // get certificate, cache should be filled
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Cert1.getName()));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 1);
+
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Cert2.getName()));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 2);
+
+  // remove a certificate
+  container2.remove(id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(container2.size(), 1);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 1);
+  BOOST_CHECK(container2.find(id1Key1Cert1.getName()) == container2.end());
+  BOOST_CHECK(container2.find(id1Key1Cert2.getName()) != container2.end());
+
+  // remove another certificate
+  container2.remove(id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(container2.size(), 0);
+  BOOST_CHECK_EQUAL(container2.getCache().size(), 0);
+  BOOST_CHECK(container2.find(id1Key1Cert2.getName()) == container2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  CertificateContainer container(id1Key1Name, pibImpl);
+
+  BOOST_CHECK_THROW(container.add(id1Key2Cert1), std::invalid_argument);
+  BOOST_CHECK_THROW(container.remove(id1Key2Cert1.getName()), std::invalid_argument);
+  BOOST_CHECK_THROW(container.get(id1Key2Cert1.getName()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  // start with an empty container
+  CertificateContainer container(id1Key1Name, pibImpl);
+  container.add(id1Key1Cert1);
+  container.add(id1Key1Cert2);
+
+  std::set<Name> certNames;
+  certNames.insert(id1Key1Cert1.getName());
+  certNames.insert(id1Key1Cert2.getName());
+
+  CertificateContainer::const_iterator it = container.begin();
+  std::set<Name>::const_iterator testIt = certNames.begin();
+  BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+  it++;
+  testIt++;
+  BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+  ++it;
+  testIt++;
+  BOOST_CHECK(it == container.end());
+
+  size_t count = 0;
+  testIt = certNames.begin();
+  for (const auto& cert : container) {
+    BOOST_CHECK_EQUAL(cert.getName(), *testIt);
+    testIt++;
+    count++;
+  }
+  BOOST_CHECK_EQUAL(count, 2);
+
+  BOOST_CHECK(CertificateContainer::const_iterator() == CertificateContainer::const_iterator());
+  BOOST_CHECK(CertificateContainer::const_iterator() == container.end());
+  BOOST_CHECK(container.end() == CertificateContainer::const_iterator());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateContainer
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/detail/identity-impl.t.cpp b/tests/unit/security/pib/detail/identity-impl.t.cpp
new file mode 100644
index 0000000..209a6b6
--- /dev/null
+++ b/tests/unit/security/pib/detail/identity-impl.t.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/detail/identity-impl.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "../pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(Detail)
+BOOST_FIXTURE_TEST_SUITE(TestIdentityImpl, ndn::security::tests::PibDataFixture)
+
+using security::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  IdentityImpl identity1(id1, pibImpl, true);
+
+  BOOST_CHECK_EQUAL(identity1.getName(), id1);
+}
+
+BOOST_AUTO_TEST_CASE(KeyOperation)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  IdentityImpl identity1(id1, pibImpl, true);
+  BOOST_CHECK_NO_THROW(IdentityImpl(id1, pibImpl, false));
+
+  // identity does not have any key
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 0);
+
+  // get non-existing key, throw Pib::Error
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  // get default key, throw Pib::Error
+  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+  // set non-existing key as default key, throw Pib::Error
+  BOOST_REQUIRE_THROW(identity1.setDefaultKey(id1Key1Name), Pib::Error);
+
+  // add key
+  identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
+
+  // new key becomes default key when there is no default key
+  BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+  const Key& defaultKey0 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey0.getName(), id1Key1Name);
+  BOOST_CHECK(defaultKey0.getPublicKey() == id1Key1);
+
+  // remove key
+  identity1.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+
+  // set default key directly
+  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.data(), id1Key1.size(), id1Key1Name));
+  BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+  BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
+
+  // check default key
+  const Key& defaultKey1 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey1.getName(), id1Key1Name);
+  BOOST_CHECK(defaultKey1.getPublicKey() == id1Key1);
+
+  // add another key
+  identity1.addKey(id1Key2.data(), id1Key2.size(), id1Key2Name);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 2);
+
+  // set default key through name
+  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key2Name));
+  BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+  const Key& defaultKey2 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey2.getName(), id1Key2Name);
+  BOOST_CHECK(defaultKey2.getPublicKey() == id1Key2);
+
+  // remove key
+  identity1.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 1);
+
+  // set default key directly again, change the default setting
+  BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.data(), id1Key1.size(), id1Key1Name));
+  const Key& defaultKey3 = identity1.getDefaultKey();
+  BOOST_CHECK_EQUAL(defaultKey3.getName(), id1Key1Name);
+  BOOST_CHECK(defaultKey3.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 2);
+
+  // remove all keys
+  identity1.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 1);
+  identity1.removeKey(id1Key2Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key2Name), Pib::Error);
+  BOOST_CHECK_EQUAL(identity1.getKeys().size(), 0);
+  BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Overwrite)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  IdentityImpl identity1(id1, pibImpl, true);
+
+  identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK(identity1.getKey(id1Key1Name).getPublicKey() == id1Key1);
+
+  identity1.addKey(id1Key2.data(), id1Key2.size(), id1Key1Name); // overwriting key should work
+  BOOST_CHECK(identity1.getKey(id1Key1Name).getPublicKey() == id1Key2);
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+
+  BOOST_CHECK_THROW(IdentityImpl(id1, pibImpl, false), Pib::Error);
+  IdentityImpl identity1(id1, pibImpl, true);
+
+  identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_THROW(identity1.addKey(id2Key1.data(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.removeKey(id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.getKey(id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.setDefaultKey(id2Key1.data(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(identity1.setDefaultKey(id2Key1Name), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentityImpl
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/detail/key-impl.t.cpp b/tests/unit/security/pib/detail/key-impl.t.cpp
new file mode 100644
index 0000000..517ea60
--- /dev/null
+++ b/tests/unit/security/pib/detail/key-impl.t.cpp
@@ -0,0 +1,200 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/detail/key-impl.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "../pib-data-fixture.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(Detail)
+BOOST_FIXTURE_TEST_SUITE(TestKeyImpl, security::tests::PibDataFixture)
+
+using security::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  KeyImpl key11(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+
+  BOOST_CHECK_EQUAL(key11.getName(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key11.getIdentity(), id1);
+  BOOST_CHECK_EQUAL(key11.getKeyType(), KeyType::EC);
+  BOOST_CHECK(key11.getPublicKey() == id1Key1);
+
+  KeyImpl key11Bak(id1Key1Name, pibImpl);
+  BOOST_CHECK_EQUAL(key11Bak.getName(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key11Bak.getIdentity(), id1);
+  BOOST_CHECK_EQUAL(key11Bak.getKeyType(), KeyType::EC);
+  BOOST_CHECK(key11Bak.getPublicKey() == id1Key1);
+}
+
+BOOST_AUTO_TEST_CASE(CertificateOperation)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+  KeyImpl key11(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+  BOOST_CHECK_NO_THROW(KeyImpl(id1Key1Name, pibImpl));
+
+  // key does not have any certificate
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 0);
+
+  // get non-existing certificate, throw Pib::Error
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  // get default certificate, throw Pib::Error
+  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+  // set non-existing certificate as default certificate, throw Pib::Error
+  BOOST_REQUIRE_THROW(key11.setDefaultCertificate(id1Key1Cert1.getName()), Pib::Error);
+
+  // add certificate
+  key11.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
+
+  // new certificate becomes default certificate when there was no default certificate
+  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  const auto& defaultCert0 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert0.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(defaultCert0, id1Key1Cert1);
+
+  // remove certificate
+  key11.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+
+  // set default certificate directly
+  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
+  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
+
+  // check default cert
+  const auto& defaultCert1 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert1.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(defaultCert1, id1Key1Cert1);
+
+  // add another certificate
+  key11.addCertificate(id1Key1Cert2);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 2);
+
+  // set default certificate through name
+  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert2.getName()));
+  BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+  const auto& defaultCert2 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert2.getName(), id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(defaultCert2, id1Key1Cert2);
+
+  // remove certificate
+  key11.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 1);
+
+  // set default certificate directly again, change the default setting
+  BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
+  const auto& defaultCert3 = key11.getDefaultCertificate();
+  BOOST_CHECK_EQUAL(defaultCert3.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(defaultCert3, id1Key1Cert1);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 2);
+
+  // remove all certificates
+  key11.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 1);
+  key11.removeCertificate(id1Key1Cert2.getName());
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert2.getName()), Pib::Error);
+  BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+  BOOST_CHECK_EQUAL(key11.getCertificates().size(), 0);
+}
+
+class OverwriteFixture : public ndn::security::tests::PibDataFixture,
+                         public ndn::tests::IdentityManagementFixture
+{
+};
+
+BOOST_FIXTURE_TEST_CASE(Overwrite, OverwriteFixture)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+
+  BOOST_CHECK_THROW(KeyImpl(id1Key1Name, pibImpl), Pib::Error);
+  KeyImpl(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+  KeyImpl key1(id1Key1Name, pibImpl);
+
+  KeyImpl(id1Key1Name, id1Key2.data(), id1Key2.size(), pibImpl); // overwriting of the key should work
+  KeyImpl key2(id1Key1Name, pibImpl);
+
+  BOOST_CHECK(key1.getPublicKey() != key2.getPublicKey()); // key1 cached the original public key
+  BOOST_CHECK(key2.getPublicKey() == id1Key2);
+
+  key1.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_EQUAL(key1.getCertificate(id1Key1Cert1.getName()), id1Key1Cert1);
+
+  auto otherCert = id1Key1Cert1;
+  SignatureInfo info;
+  info.setValidityPeriod(ValidityPeriod(time::system_clock::now(),
+                                        time::system_clock::now() + 1_s));
+  m_keyChain.sign(otherCert, SigningInfo().setSignatureInfo(info));
+
+  BOOST_CHECK_EQUAL(otherCert.getName(), id1Key1Cert1.getName());
+  BOOST_CHECK(otherCert.getContent() == id1Key1Cert1.getContent());
+  BOOST_CHECK_NE(otherCert, id1Key1Cert1);
+
+  key1.addCertificate(otherCert);
+
+  BOOST_CHECK_EQUAL(key1.getCertificate(id1Key1Cert1.getName()), otherCert);
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<pib::PibMemory>();
+
+  BOOST_CHECK_THROW(KeyImpl(id1Key1Name, pibImpl), Pib::Error);
+  KeyImpl key11(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+
+  BOOST_CHECK_THROW(KeyImpl(Name("/wrong"), pibImpl), std::invalid_argument);
+  BOOST_CHECK_THROW(KeyImpl(Name("/wrong"), id1Key1.data(), id1Key1.size(), pibImpl), std::invalid_argument);
+  Buffer wrongKey;
+  BOOST_CHECK_THROW(KeyImpl(id1Key2Name, wrongKey.data(), wrongKey.size(), pibImpl), std::invalid_argument);
+
+  key11.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_THROW(key11.addCertificate(id1Key2Cert1), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.removeCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.getCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.setDefaultCertificate(id1Key2Cert1), std::invalid_argument);
+  BOOST_CHECK_THROW(key11.setDefaultCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyImpl
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/identity-container.t.cpp b/tests/unit/security/pib/identity-container.t.cpp
new file mode 100644
index 0000000..e821970
--- /dev/null
+++ b/tests/unit/security/pib/identity-container.t.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/identity-container.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestIdentityContainer, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  // start with an empty container
+  IdentityContainer container(pibImpl);
+  BOOST_CHECK_EQUAL(container.size(), 0);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 0);
+
+  // add the first identity
+  Identity identity11 = container.add(id1);
+  BOOST_CHECK_EQUAL(identity11.getName(), id1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 1);
+  BOOST_CHECK(container.find(id1) != container.end());
+
+  // add the same identity again
+  Identity identity12 = container.add(id1);
+  BOOST_CHECK_EQUAL(identity12.getName(), id1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 1);
+  BOOST_CHECK(container.find(id1) != container.end());
+
+  // add the second identity
+  Identity identity21 = container.add(id2);
+  BOOST_CHECK_EQUAL(identity21.getName(), id2);
+  BOOST_CHECK_EQUAL(container.size(), 2);
+  BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 2);
+  BOOST_CHECK(container.find(id1) != container.end());
+  BOOST_CHECK(container.find(id2) != container.end());
+
+  // get identities
+  BOOST_REQUIRE_NO_THROW(container.get(id1));
+  BOOST_REQUIRE_NO_THROW(container.get(id2));
+  BOOST_CHECK_THROW(container.get(Name("/non-existing")), Pib::Error);
+
+  // check identity
+  Identity identity1 = container.get(id1);
+  Identity identity2 = container.get(id2);
+  BOOST_CHECK_EQUAL(identity1.getName(), id1);
+  BOOST_CHECK_EQUAL(identity2.getName(), id2);
+
+  // create another container from the same PibImpl
+  // cache should be empty
+  IdentityContainer container2(pibImpl);
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 0);
+
+  // get key, cache should be filled
+  BOOST_REQUIRE_NO_THROW(container2.get(id1));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 1);
+
+  BOOST_REQUIRE_NO_THROW(container2.get(id2));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 2);
+
+  // remove a key
+  container2.remove(id1);
+  BOOST_CHECK_EQUAL(container2.size(), 1);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 1);
+  BOOST_CHECK(container2.find(id1) == container2.end());
+  BOOST_CHECK(container2.find(id2) != container2.end());
+
+  // remove another key
+  container2.remove(id2);
+  BOOST_CHECK_EQUAL(container2.size(), 0);
+  BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 0);
+  BOOST_CHECK(container2.find(id2) == container2.end());
+
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  auto pibImpl = make_shared<PibMemory>();
+  IdentityContainer container(pibImpl);
+  container.add(id1);
+  container.add(id2);
+
+  std::set<Name> idNames;
+  idNames.insert(id1);
+  idNames.insert(id2);
+
+  IdentityContainer::const_iterator it = container.begin();
+  std::set<Name>::const_iterator testIt = idNames.begin();
+  BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+  it++;
+  testIt++;
+  BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+  ++it;
+  testIt++;
+  BOOST_CHECK(it == container.end());
+
+  size_t count = 0;
+  testIt = idNames.begin();
+  for (const auto& identity : container) {
+    BOOST_CHECK_EQUAL(identity.getName(), *testIt);
+    testIt++;
+    count++;
+  }
+  BOOST_CHECK_EQUAL(count, 2);
+
+  BOOST_CHECK(IdentityContainer::const_iterator() == IdentityContainer::const_iterator());
+  BOOST_CHECK(IdentityContainer::const_iterator() == container.end());
+  BOOST_CHECK(container.end() == IdentityContainer::const_iterator());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentityContainer
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/identity.t.cpp b/tests/unit/security/pib/identity.t.cpp
new file mode 100644
index 0000000..d10c7bf
--- /dev/null
+++ b/tests/unit/security/pib/identity.t.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/identity.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "security/pib/detail/identity-impl.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestIdentity, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(ValidityChecking)
+{
+  using security::pib::detail::IdentityImpl;
+
+  Identity id;
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(id), false);
+  BOOST_CHECK_EQUAL(!id, true);
+
+  if (id)
+    BOOST_CHECK(false);
+  else
+    BOOST_CHECK(true);
+
+  auto identityImpl = make_shared<IdentityImpl>(id1, make_shared<PibMemory>(), true);
+  id = Identity(identityImpl);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(id), true);
+  BOOST_CHECK_EQUAL(!id, false);
+
+  if (id)
+    BOOST_CHECK(true);
+  else
+    BOOST_CHECK(false);
+}
+
+/**
+ * pib::Identity is a wrapper of pib::detail::IdentityImpl.  Since the functionalities of
+ * IdentityImpl have already been tested in detail/identity-impl.t.cpp, we only test the shared
+ * property of pib::Identity in this test case.
+ */
+BOOST_AUTO_TEST_CASE(Share)
+{
+  using security::pib::detail::IdentityImpl;
+
+  auto identityImpl = make_shared<IdentityImpl>(id1, make_shared<pib::PibMemory>(), true);
+  Identity identity1(identityImpl);
+  Identity identity2(identityImpl);
+  BOOST_CHECK_EQUAL(identity1, identity2);
+  BOOST_CHECK_NE(identity1, Identity());
+  BOOST_CHECK_EQUAL(Identity(), Identity());
+
+  identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_NO_THROW(identity2.getKey(id1Key1Name));
+  identity2.removeKey(id1Key1Name);
+  BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+
+  identity1.setDefaultKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_NO_THROW(identity2.getDefaultKey());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentity
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/key-container.t.cpp b/tests/unit/security/pib/key-container.t.cpp
new file mode 100644
index 0000000..2afd3e2
--- /dev/null
+++ b/tests/unit/security/pib/key-container.t.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/key-container.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestKeyContainer, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  // start with an empty container
+  KeyContainer container(id1, pibImpl);
+  BOOST_CHECK_EQUAL(container.size(), 0);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 0);
+
+  // add the first key
+  Key key11 = container.add(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key11.getName(), id1Key1Name);
+  BOOST_CHECK(key11.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Name) != container.end());
+
+  // add the same key again
+  Key key12 = container.add(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  BOOST_CHECK_EQUAL(key12.getName(), id1Key1Name);
+  BOOST_CHECK(key12.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(container.size(), 1);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 1);
+  BOOST_CHECK(container.find(id1Key1Name) != container.end());
+
+  // add the second key
+  Key key21 = container.add(id1Key2.data(), id1Key2.size(), id1Key2Name);
+  BOOST_CHECK_EQUAL(key21.getName(), id1Key2Name);
+  BOOST_CHECK(key21.getPublicKey() == id1Key2);
+  BOOST_CHECK_EQUAL(container.size(), 2);
+  BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 2);
+  BOOST_CHECK(container.find(id1Key1Name) != container.end());
+  BOOST_CHECK(container.find(id1Key2Name) != container.end());
+
+  // get keys
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key1Name));
+  BOOST_REQUIRE_NO_THROW(container.get(id1Key2Name));
+  Name id1Key3Name = v2::constructKeyName(id1, name::Component("non-existing-id"));
+  BOOST_CHECK_THROW(container.get(id1Key3Name), Pib::Error);
+
+  // check key
+  Key key1 = container.get(id1Key1Name);
+  Key key2 = container.get(id1Key2Name);
+  BOOST_CHECK_EQUAL(key1.getName(), id1Key1Name);
+  BOOST_CHECK(key1.getPublicKey() == id1Key1);
+  BOOST_CHECK_EQUAL(key2.getName(), id1Key2Name);
+  BOOST_CHECK(key2.getPublicKey() == id1Key2);
+
+  // create another container from the same PibImpl
+  // cache should be empty
+  KeyContainer container2(id1, pibImpl);
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 0);
+
+  // get key, cache should be filled
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Name));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 1);
+
+  BOOST_REQUIRE_NO_THROW(container2.get(id1Key2Name));
+  BOOST_CHECK_EQUAL(container2.size(), 2);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 2);
+
+  // remove a key
+  container2.remove(id1Key1Name);
+  BOOST_CHECK_EQUAL(container2.size(), 1);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 1);
+  BOOST_CHECK(container2.find(id1Key1Name) == container2.end());
+  BOOST_CHECK(container2.find(id1Key2Name) != container2.end());
+
+  // remove another key
+  container2.remove(id1Key2Name);
+  BOOST_CHECK_EQUAL(container2.size(), 0);
+  BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 0);
+  BOOST_CHECK(container2.find(id1Key2Name) == container2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  auto pibImpl = make_shared<PibMemory>();
+
+  KeyContainer container(id1, pibImpl);
+
+  BOOST_CHECK_THROW(container.add(id2Key1.data(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(container.remove(id2Key1Name), std::invalid_argument);
+  BOOST_CHECK_THROW(container.get(id2Key1Name), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+  auto pibImpl = make_shared<PibMemory>();
+  KeyContainer container(id1, pibImpl);
+
+  container.add(id1Key1.data(), id1Key1.size(), id1Key1Name);
+  container.add(id1Key2.data(), id1Key2.size(), id1Key2Name);
+
+  std::set<Name> keyNames;
+  keyNames.insert(id1Key1Name);
+  keyNames.insert(id1Key2Name);
+
+  KeyContainer::const_iterator it = container.begin();
+  std::set<Name>::const_iterator testIt = keyNames.begin();
+  BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+  it++;
+  testIt++;
+  BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+  ++it;
+  testIt++;
+  BOOST_CHECK(it == container.end());
+
+  size_t count = 0;
+  testIt = keyNames.begin();
+  for (const auto& key : container) {
+    BOOST_CHECK_EQUAL(key.getIdentity(), id1);
+    BOOST_CHECK_EQUAL(key.getName(), *testIt);
+    testIt++;
+    count++;
+  }
+  BOOST_CHECK_EQUAL(count, 2);
+
+  BOOST_CHECK(KeyContainer::const_iterator() == KeyContainer::const_iterator());
+  BOOST_CHECK(KeyContainer::const_iterator() == container.end());
+  BOOST_CHECK(container.end() == KeyContainer::const_iterator());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyContainer
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/key.t.cpp b/tests/unit/security/pib/key.t.cpp
new file mode 100644
index 0000000..3c5d207
--- /dev/null
+++ b/tests/unit/security/pib/key.t.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/key.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "security/pib/detail/key-impl.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestKey, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(ValidityChecking)
+{
+  using security::pib::detail::KeyImpl;
+
+  Key key;
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(key), false);
+  BOOST_CHECK_EQUAL(!key, true);
+
+  if (key)
+    BOOST_CHECK(false);
+  else
+    BOOST_CHECK(true);
+
+  auto keyImpl = make_shared<KeyImpl>(id1Key1Name, id1Key1.data(), id1Key1.size(),
+                                      make_shared<pib::PibMemory>());
+  key = Key(keyImpl);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(key), true);
+  BOOST_CHECK_EQUAL(!key, false);
+
+  if (key)
+    BOOST_CHECK(true);
+  else
+    BOOST_CHECK(false);
+}
+
+/**
+ * pib::Key is a wrapper of pib::detail::KeyImpl.  Since the functionalities of KeyImpl
+ * have already been tested in detail/key-impl.t.cpp, we only test the shared property
+ * of pib::Key in this test case.
+ */
+
+BOOST_AUTO_TEST_CASE(Share)
+{
+  using security::pib::detail::KeyImpl;
+
+  auto keyImpl = make_shared<KeyImpl>(id1Key1Name, id1Key1.data(), id1Key1.size(),
+                                      make_shared<pib::PibMemory>());
+  Key key1(keyImpl);
+  Key key2(keyImpl);
+  BOOST_CHECK_EQUAL(key1, key2);
+  BOOST_CHECK_NE(key1, Key());
+  BOOST_CHECK_EQUAL(Key(), Key());
+
+  key1.addCertificate(id1Key1Cert1);
+  BOOST_CHECK_NO_THROW(key2.getCertificate(id1Key1Cert1.getName()));
+  key2.removeCertificate(id1Key1Cert1.getName());
+  BOOST_CHECK_THROW(key1.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+
+  key1.setDefaultCertificate(id1Key1Cert1);
+  BOOST_CHECK_NO_THROW(key2.getDefaultCertificate());
+}
+
+BOOST_AUTO_TEST_CASE(Helpers)
+{
+  BOOST_CHECK_EQUAL(v2::constructKeyName("/hello", name::Component("world")), "/hello/KEY/world");
+
+  BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello"), false);
+  BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello/KEY"), false);
+  BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello/KEY/world"), true);
+
+  BOOST_CHECK_EQUAL(v2::isValidKeyName("/KEY/hello"), true);
+  BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello/world/KEY/!"), true);
+
+  BOOST_CHECK_EQUAL(v2::extractIdentityFromKeyName("/KEY/hello"), "/");
+  BOOST_CHECK_EQUAL(v2::extractIdentityFromKeyName("/hello/KEY/world"), "/hello");
+  BOOST_CHECK_EQUAL(v2::extractIdentityFromKeyName("/hello/world/KEY/!"), "/hello/world");
+
+  BOOST_CHECK_THROW(v2::extractIdentityFromKeyName("/hello"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKey
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-data-fixture.cpp b/tests/unit/security/pib/pib-data-fixture.cpp
new file mode 100644
index 0000000..91e5b34
--- /dev/null
+++ b/tests/unit/security/pib/pib-data-fixture.cpp
@@ -0,0 +1,429 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "pib-data-fixture.hpp"
+#include "../../identity-management-time-fixture.hpp"
+
+// #include "security/pib/pib-memory.hpp"
+// #include "security/tpm/tpm.hpp"
+// #include "security/tpm/back-end-mem.hpp"
+// #include <fstream>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+// class TestCertDataGenerator
+// {
+// public:
+//   TestCertDataGenerator()
+//     : tpm("test", "test", make_unique<tpm::BackEndMem>())
+//   {
+//   }
+
+//   void
+//   printTestDataForId(const std::string& prefix, const Name& id)
+//   {
+//     for (int keyId : {1, 2}) {
+//       Name keyName = tpm.createKey(id, EcKeyParams(name::Component::fromNumber(keyId)));
+
+//       for (int certVersion : {1, 2}) {
+//         Name certName = keyName;
+//         certName
+//           .append("issuer")
+//           .appendVersion(certVersion);
+//         v2::Certificate cert;
+//         cert.setName(certName);
+//         cert.setFreshnessPeriod(1_h);
+//         cert.setContent(tpm.getPublicKey(keyName));
+
+//         // @TODO sign using the new KeyChain
+//         SignatureInfo info;
+//         info.setSignatureType(tlv::SignatureSha256WithEcdsa);
+//         info.setKeyLocator(KeyLocator(keyName));
+//         info.setValidityPeriod(ValidityPeriod(time::fromIsoString("20170102T000000"),
+//                                               time::fromIsoString("20180102T000000")));
+//         cert.setSignature(Signature(info, Block()));
+
+//         EncodingBuffer buf;
+//         cert.wireEncode(buf, true);
+
+//         cert.setSignatureValue(Block(tlv::SignatureValue,
+//                                      tpm.sign(buf.buf(), buf.size(), keyName, DigestAlgorithm::SHA256)));
+
+//         printBytes(prefix + "_KEY" + to_string(keyId) + "_CERT" + to_string(certVersion),
+//                    cert.wireEncode());
+//       }
+//     }
+//   }
+
+//   static void
+//   printBytes(const std::string& name, const Block& block)
+//   {
+//     printBytes(name, block.wire(), block.size());
+//   }
+
+//   static void
+//   printBytes(const std::string& name, const Buffer& buffer)
+//   {
+//     printBytes(name, buffer.buf(), buffer.size());
+//   }
+
+//   static void
+//   printBytes(const std::string& name, const uint8_t* buf, size_t size)
+//   {
+//     std::cout << "\nconst uint8_t " << name << "[] = {\n"
+//               << "  ";
+
+//     std::string hex = toHex(buf, size);
+
+//     for (size_t i = 0; i < hex.size(); i++) {
+//       if (i > 0 && i % 40 == 0)
+//         std::cout << "\n  ";
+
+//       std::cout << "0x" << hex[i];
+//       std::cout << hex[++i];
+
+//       if ((i + 1) != hex.size())
+//         std::cout << ", ";
+//     }
+//     std::cout << "\n"
+//               << "};" << std::endl;
+//   }
+
+// public:
+//   pib::PibMemory pib;
+//   Tpm tpm;
+// };
+
+// // The test data can be generated using this test case
+// BOOST_FIXTURE_TEST_CASE(GenerateTestCertData, TestCertDataGenerator)
+// {
+//   printTestDataForId("ID1", Name("/pib/interface/id/1"));
+//   printTestDataForId("ID2", Name("/pib/interface/id/2"));
+// }
+
+const uint8_t ID1_KEY1_CERT1[] = {
+  0x06, 0xFD, 0x02, 0x25, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xCB, 0x46, 0xF7, 0x16, 0x2E,
+  0x83, 0x3D, 0x5E, 0x4A, 0x80, 0x6A, 0x78, 0xB7, 0xA8, 0x7A, 0x15, 0x95, 0x2D, 0x23, 0xA8, 0x41, 0xF7, 0x62, 0xE4, 0x0E,
+  0x66, 0x36, 0xB3, 0xF3, 0x14, 0xD6, 0xB3, 0xAB, 0x19, 0x26, 0x9D, 0x5A, 0x8A, 0x51, 0xD4, 0x4E, 0xBA, 0xBE, 0x13, 0x96,
+  0xCA, 0x38, 0x52, 0x16, 0xE4, 0x3D, 0xB0, 0x88, 0xBA, 0xBB, 0x7B, 0x97, 0x00, 0xA5, 0x95, 0x97, 0x4E, 0xE8, 0xF6, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x46, 0x30, 0x44, 0x02, 0x20, 0x53, 0xC8, 0xAD, 0x88, 0xBA, 0x52, 0x29, 0x68, 0xFF, 0x74, 0xA8, 0x39, 0x7F,
+  0x2C, 0xE2, 0x8E, 0x04, 0xC1, 0x78, 0x36, 0x46, 0x89, 0x38, 0x58, 0x45, 0x22, 0x44, 0xA3, 0xC8, 0xC1, 0xFF, 0x72, 0x02,
+  0x20, 0x23, 0x9D, 0xE4, 0x92, 0x00, 0xF1, 0x43, 0x69, 0xF7, 0x32, 0xF6, 0xAA, 0x8C, 0xFD, 0x7F, 0x2B, 0xFB, 0xD2, 0x40,
+  0x6A, 0x1E, 0xA3, 0xE5, 0xF0, 0xF8, 0x2B, 0x92, 0x99, 0x6B, 0xDB, 0xE2, 0x6D
+};
+
+const uint8_t ID1_KEY1_CERT2[] = {
+  0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xCB, 0x46, 0xF7, 0x16, 0x2E,
+  0x83, 0x3D, 0x5E, 0x4A, 0x80, 0x6A, 0x78, 0xB7, 0xA8, 0x7A, 0x15, 0x95, 0x2D, 0x23, 0xA8, 0x41, 0xF7, 0x62, 0xE4, 0x0E,
+  0x66, 0x36, 0xB3, 0xF3, 0x14, 0xD6, 0xB3, 0xAB, 0x19, 0x26, 0x9D, 0x5A, 0x8A, 0x51, 0xD4, 0x4E, 0xBA, 0xBE, 0x13, 0x96,
+  0xCA, 0x38, 0x52, 0x16, 0xE4, 0x3D, 0xB0, 0x88, 0xBA, 0xBB, 0x7B, 0x97, 0x00, 0xA5, 0x95, 0x97, 0x4E, 0xE8, 0xF6, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xB2, 0x93, 0xCD, 0x3D, 0x01, 0x00, 0xB5, 0xF1, 0x75, 0x22, 0x68, 0x9F,
+  0xE4, 0x5E, 0x0A, 0x76, 0x34, 0xBC, 0x9D, 0xCF, 0x9A, 0x4C, 0x21, 0x3F, 0xA5, 0x12, 0x51, 0xF7, 0x3A, 0x5E, 0x37, 0x7D,
+  0x02, 0x20, 0x33, 0xA9, 0xA9, 0x8F, 0xD8, 0x2E, 0xED, 0x3C, 0xE5, 0x18, 0x94, 0x59, 0x28, 0xEA, 0x82, 0x38, 0x5B, 0x20,
+  0xE4, 0xBF, 0x15, 0xF4, 0x0D, 0x45, 0xAE, 0x8B, 0x63, 0x19, 0x79, 0x78, 0x50, 0x3A
+};
+
+const uint8_t ID1_KEY2_CERT1[] = {
+  0x06, 0xFD, 0x02, 0x25, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x34, 0xAA, 0x4B, 0x1A, 0x97,
+  0x4A, 0x6B, 0x6F, 0x3F, 0xB3, 0xC9, 0xD1, 0x39, 0x9F, 0x1E, 0x49, 0xB6, 0x6E, 0x19, 0x97, 0x13, 0x5E, 0xFA, 0xE6, 0xD3,
+  0xFE, 0xF3, 0xB0, 0xCA, 0x80, 0x09, 0x31, 0xCA, 0x50, 0x5C, 0xE6, 0x57, 0xBF, 0x13, 0x16, 0xCE, 0x3E, 0xF1, 0xD4, 0x23,
+  0xF8, 0x7F, 0x31, 0xFA, 0x13, 0x39, 0x09, 0xED, 0xC6, 0x74, 0x3D, 0xFD, 0x1A, 0x0B, 0xC7, 0xC1, 0x01, 0x15, 0x7F, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x46, 0x30, 0x44, 0x02, 0x20, 0x71, 0x3A, 0xB4, 0x19, 0x4B, 0xB3, 0x25, 0xA5, 0x03, 0x23, 0x8C, 0xC1, 0xB9,
+  0x68, 0xC1, 0x41, 0x4B, 0xED, 0x13, 0xCC, 0x87, 0x16, 0xB5, 0x13, 0x87, 0xA0, 0x54, 0xA2, 0x9F, 0xF0, 0xD7, 0x72, 0x02,
+  0x20, 0x4B, 0xEF, 0xB5, 0x6A, 0x8C, 0x40, 0x71, 0x17, 0xD2, 0x4F, 0xB6, 0x0F, 0xBE, 0x60, 0x1A, 0x46, 0x9B, 0x78, 0x15,
+  0x46, 0x09, 0xC2, 0x7A, 0x80, 0xD4, 0xE6, 0x71, 0x52, 0xD6, 0x83, 0x4B, 0x04
+};
+
+const uint8_t ID1_KEY2_CERT2[] = {
+  0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x34, 0xAA, 0x4B, 0x1A, 0x97,
+  0x4A, 0x6B, 0x6F, 0x3F, 0xB3, 0xC9, 0xD1, 0x39, 0x9F, 0x1E, 0x49, 0xB6, 0x6E, 0x19, 0x97, 0x13, 0x5E, 0xFA, 0xE6, 0xD3,
+  0xFE, 0xF3, 0xB0, 0xCA, 0x80, 0x09, 0x31, 0xCA, 0x50, 0x5C, 0xE6, 0x57, 0xBF, 0x13, 0x16, 0xCE, 0x3E, 0xF1, 0xD4, 0x23,
+  0xF8, 0x7F, 0x31, 0xFA, 0x13, 0x39, 0x09, 0xED, 0xC6, 0x74, 0x3D, 0xFD, 0x1A, 0x0B, 0xC7, 0xC1, 0x01, 0x15, 0x7F, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xD2, 0x90, 0x8C, 0xA3, 0x52, 0x2F, 0x79, 0xB3, 0xD7, 0x39, 0xE1, 0x6C,
+  0x7F, 0xA0, 0xDF, 0xD1, 0x3E, 0x0F, 0x70, 0xBE, 0xF5, 0xDB, 0x08, 0xDF, 0xE1, 0x0B, 0xDF, 0x79, 0x99, 0xFE, 0x5C, 0xDC,
+  0x02, 0x20, 0x3D, 0xD4, 0x7C, 0xD1, 0x83, 0xBE, 0x29, 0xBB, 0x73, 0xA3, 0x82, 0xE5, 0xE6, 0x83, 0xA1, 0xC1, 0xBC, 0xF6,
+  0x84, 0x42, 0x85, 0x00, 0x92, 0x4B, 0xA2, 0xA8, 0xCA, 0x10, 0x7B, 0x92, 0x89, 0xB0
+};
+
+const uint8_t ID2_KEY1_CERT1[] = {
+  0x06, 0xFD, 0x02, 0x25, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xAC, 0x62, 0xD7, 0x31, 0x3B,
+  0x19, 0x1F, 0x44, 0x76, 0x6E, 0x79, 0x03, 0xB9, 0xC8, 0x26, 0xC4, 0x1E, 0x38, 0x3A, 0x41, 0xFE, 0xB4, 0x72, 0xA2, 0x36,
+  0xBB, 0x82, 0x9C, 0xB9, 0x07, 0x62, 0x6F, 0x1C, 0x79, 0x12, 0xCA, 0x9C, 0x3D, 0xAA, 0x7A, 0x96, 0xFF, 0xAF, 0x5B, 0x6F,
+  0xE1, 0x72, 0x60, 0xB0, 0x7F, 0x44, 0x38, 0x05, 0x21, 0xCC, 0x49, 0x78, 0x89, 0xC1, 0xEF, 0xEE, 0x81, 0x8E, 0xF5, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x46, 0x30, 0x44, 0x02, 0x20, 0x16, 0xC0, 0xF4, 0xE3, 0x15, 0x43, 0x6E, 0x27, 0x33, 0x7C, 0x46, 0x4D, 0x35,
+  0xA7, 0x8B, 0x0C, 0xE3, 0x27, 0x63, 0x4B, 0xB2, 0xB6, 0x4F, 0x06, 0x90, 0x2A, 0xD8, 0x54, 0x92, 0xE8, 0xBA, 0xBE, 0x02,
+  0x20, 0x67, 0xA6, 0x55, 0x8D, 0x16, 0x0E, 0x1E, 0x9E, 0x10, 0x0E, 0xB9, 0x3C, 0xEF, 0xEE, 0xB5, 0xF7, 0x9C, 0xB3, 0x1D,
+  0x04, 0xF1, 0xD4, 0xB5, 0x9F, 0xD4, 0x13, 0xBB, 0xFF, 0xA7, 0x58, 0xAE, 0xCB
+};
+
+const uint8_t ID2_KEY1_CERT2[] = {
+  0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xAC, 0x62, 0xD7, 0x31, 0x3B,
+  0x19, 0x1F, 0x44, 0x76, 0x6E, 0x79, 0x03, 0xB9, 0xC8, 0x26, 0xC4, 0x1E, 0x38, 0x3A, 0x41, 0xFE, 0xB4, 0x72, 0xA2, 0x36,
+  0xBB, 0x82, 0x9C, 0xB9, 0x07, 0x62, 0x6F, 0x1C, 0x79, 0x12, 0xCA, 0x9C, 0x3D, 0xAA, 0x7A, 0x96, 0xFF, 0xAF, 0x5B, 0x6F,
+  0xE1, 0x72, 0x60, 0xB0, 0x7F, 0x44, 0x38, 0x05, 0x21, 0xCC, 0x49, 0x78, 0x89, 0xC1, 0xEF, 0xEE, 0x81, 0x8E, 0xF5, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xF2, 0x0D, 0x1D, 0x60, 0x0B, 0x2D, 0x97, 0x3A, 0x6B, 0xEE, 0xEC, 0x56,
+  0xD1, 0x64, 0xBF, 0xED, 0x68, 0xB7, 0x10, 0x0B, 0xDF, 0x81, 0x29, 0xCD, 0xB0, 0xBB, 0x87, 0x0D, 0xDA, 0x12, 0x52, 0xCC,
+  0x02, 0x20, 0x64, 0x33, 0x4E, 0x91, 0xAF, 0x81, 0xF4, 0xE7, 0xAD, 0x38, 0x8E, 0xBF, 0x79, 0xA7, 0x70, 0x1E, 0xD6, 0x71,
+  0x7E, 0xF5, 0xEB, 0x92, 0x56, 0x5F, 0xC7, 0x05, 0xDC, 0x27, 0xE5, 0x11, 0xC2, 0x43
+};
+
+const uint8_t ID2_KEY2_CERT1[] = {
+  0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x2C, 0xC3, 0xAF, 0xA8, 0x73,
+  0xF2, 0x61, 0xCF, 0x48, 0x04, 0x0F, 0x9D, 0xD3, 0xAF, 0x0B, 0xC6, 0x2F, 0x4D, 0xDA, 0x0E, 0x4C, 0x66, 0x1D, 0x03, 0x9D,
+  0xFE, 0x2C, 0x0B, 0xB6, 0x25, 0x60, 0xBC, 0xFA, 0xDA, 0xFE, 0x6F, 0x43, 0xFA, 0x95, 0x45, 0x57, 0x8A, 0x25, 0xEC, 0x0F,
+  0xF2, 0xB7, 0x43, 0x85, 0x0D, 0x0B, 0x8D, 0x97, 0x40, 0x83, 0x4C, 0x28, 0x1B, 0xD4, 0x2E, 0x99, 0x2C, 0x73, 0x7D, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x20, 0x56, 0x34, 0x49, 0xAC, 0x72, 0x4E, 0x58, 0x24, 0x6F, 0x14, 0xEE, 0xD3, 0x01,
+  0x5B, 0xD4, 0x0A, 0x26, 0x2B, 0x6A, 0xD1, 0xB3, 0x33, 0x69, 0x4D, 0x64, 0x0C, 0xAA, 0xAE, 0x63, 0x59, 0x6A, 0xFD, 0x02,
+  0x21, 0x00, 0xFD, 0xB9, 0x9E, 0x37, 0x70, 0x9C, 0xE2, 0x7A, 0x0A, 0xFD, 0x64, 0x99, 0x1B, 0xA3, 0x78, 0x83, 0x09, 0xC6,
+  0xA0, 0x6D, 0x7A, 0x55, 0x8F, 0x6C, 0x35, 0xAB, 0x63, 0x78, 0x9D, 0xF3, 0xDC, 0xBC
+};
+
+const uint8_t ID2_KEY2_CERT2[] = {
+  0x06, 0xFD, 0x02, 0x27, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+  0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+  0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+  0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+  0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+  0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+  0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+  0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+  0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+  0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+  0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+  0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+  0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+  0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x2C, 0xC3, 0xAF, 0xA8, 0x73,
+  0xF2, 0x61, 0xCF, 0x48, 0x04, 0x0F, 0x9D, 0xD3, 0xAF, 0x0B, 0xC6, 0x2F, 0x4D, 0xDA, 0x0E, 0x4C, 0x66, 0x1D, 0x03, 0x9D,
+  0xFE, 0x2C, 0x0B, 0xB6, 0x25, 0x60, 0xBC, 0xFA, 0xDA, 0xFE, 0x6F, 0x43, 0xFA, 0x95, 0x45, 0x57, 0x8A, 0x25, 0xEC, 0x0F,
+  0xF2, 0xB7, 0x43, 0x85, 0x0D, 0x0B, 0x8D, 0x97, 0x40, 0x83, 0x4C, 0x28, 0x1B, 0xD4, 0x2E, 0x99, 0x2C, 0x73, 0x7D, 0x16,
+  0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+  0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+  0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+  0x30, 0x17, 0x48, 0x30, 0x46, 0x02, 0x21, 0x00, 0xB3, 0xF5, 0x96, 0x19, 0xE7, 0xF9, 0x6B, 0xCF, 0x14, 0x64, 0xB1, 0x08,
+  0xFA, 0xFF, 0xB3, 0x52, 0x8B, 0x41, 0xCB, 0xE7, 0xE0, 0x3D, 0x14, 0x7B, 0xC2, 0xD0, 0xC8, 0x89, 0x88, 0xFA, 0x95, 0x73,
+  0x02, 0x21, 0x00, 0xEC, 0x8E, 0x0C, 0x8B, 0x8C, 0x18, 0x8D, 0x00, 0x7C, 0x12, 0x68, 0x57, 0x87, 0xB1, 0x99, 0x69, 0xDA,
+  0x46, 0xEF, 0x14, 0x2D, 0x04, 0x18, 0xBE, 0x1D, 0xAE, 0x79, 0x49, 0xFD, 0x22, 0x8E, 0xBB
+};
+
+PibDataFixture::PibDataFixture()
+  : id1Key1Cert1(Block(ID1_KEY1_CERT1, sizeof(ID1_KEY1_CERT1)))
+  , id1Key1Cert2(Block(ID1_KEY1_CERT2, sizeof(ID1_KEY1_CERT2)))
+  , id1Key2Cert1(Block(ID1_KEY2_CERT1, sizeof(ID1_KEY2_CERT1)))
+  , id1Key2Cert2(Block(ID1_KEY2_CERT2, sizeof(ID1_KEY2_CERT2)))
+
+  , id2Key1Cert1(Block(ID2_KEY1_CERT1, sizeof(ID2_KEY1_CERT1)))
+  , id2Key1Cert2(Block(ID2_KEY1_CERT2, sizeof(ID2_KEY1_CERT2)))
+  , id2Key2Cert1(Block(ID2_KEY2_CERT1, sizeof(ID2_KEY2_CERT1)))
+  , id2Key2Cert2(Block(ID2_KEY2_CERT2, sizeof(ID2_KEY2_CERT2)))
+
+  , id1(id1Key1Cert1.getIdentity())
+  , id2(id2Key1Cert1.getIdentity())
+
+  , id1Key1Name(id1Key1Cert1.getKeyName())
+  , id1Key2Name(id1Key2Cert1.getKeyName())
+
+  , id2Key1Name(id2Key1Cert1.getKeyName())
+  , id2Key2Name(id2Key2Cert1.getKeyName())
+
+  , id1Key1(id1Key1Cert1.getPublicKey())
+  , id1Key2(id1Key2Cert1.getPublicKey())
+  , id2Key1(id2Key1Cert1.getPublicKey())
+  , id2Key2(id2Key2Cert1.getPublicKey())
+{
+  BOOST_ASSERT(id1Key1Cert1.getPublicKey() == id1Key1Cert2.getPublicKey());
+  BOOST_ASSERT(id1Key2Cert1.getPublicKey() == id1Key2Cert2.getPublicKey());
+  BOOST_ASSERT(id2Key1Cert1.getPublicKey() == id2Key1Cert2.getPublicKey());
+  BOOST_ASSERT(id2Key2Cert1.getPublicKey() == id2Key2Cert2.getPublicKey());
+
+  BOOST_ASSERT(id1Key1Cert1.getPublicKey() == id1Key1);
+  BOOST_ASSERT(id1Key1Cert2.getPublicKey() == id1Key1);
+  BOOST_ASSERT(id1Key2Cert1.getPublicKey() == id1Key2);
+  BOOST_ASSERT(id1Key2Cert2.getPublicKey() == id1Key2);
+
+  BOOST_ASSERT(id2Key1Cert1.getPublicKey() == id2Key1);
+  BOOST_ASSERT(id2Key1Cert2.getPublicKey() == id2Key1);
+  BOOST_ASSERT(id2Key2Cert1.getPublicKey() == id2Key2);
+  BOOST_ASSERT(id2Key2Cert2.getPublicKey() == id2Key2);
+
+  BOOST_ASSERT(id1Key1Cert2.getIdentity() == id1);
+  BOOST_ASSERT(id1Key2Cert1.getIdentity() == id1);
+  BOOST_ASSERT(id1Key2Cert2.getIdentity() == id1);
+
+  BOOST_ASSERT(id2Key1Cert2.getIdentity() == id2);
+  BOOST_ASSERT(id2Key2Cert1.getIdentity() == id2);
+  BOOST_ASSERT(id2Key2Cert2.getIdentity() == id2);
+
+  BOOST_ASSERT(id1Key1Cert2.getKeyName() == id1Key1Name);
+  BOOST_ASSERT(id1Key2Cert2.getKeyName() == id1Key2Name);
+
+  BOOST_ASSERT(id2Key1Cert2.getKeyName() == id2Key1Name);
+  BOOST_ASSERT(id2Key2Cert2.getKeyName() == id2Key2Name);
+}
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-data-fixture.hpp b/tests/unit/security/pib/pib-data-fixture.hpp
new file mode 100644
index 0000000..2328702
--- /dev/null
+++ b/tests/unit/security/pib/pib-data-fixture.hpp
@@ -0,0 +1,66 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_PIB_DATA_FIXTURE_HPP
+#define NDN_TESTS_SECURITY_PIB_DATA_FIXTURE_HPP
+
+#include "security/v2/certificate.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+class PibDataFixture
+{
+public:
+  PibDataFixture();
+
+public:
+  v2::Certificate id1Key1Cert1;
+  v2::Certificate id1Key1Cert2;
+  v2::Certificate id1Key2Cert1;
+  v2::Certificate id1Key2Cert2;
+  v2::Certificate id2Key1Cert1;
+  v2::Certificate id2Key1Cert2;
+  v2::Certificate id2Key2Cert1;
+  v2::Certificate id2Key2Cert2;
+
+  Name id1;
+  Name id2;
+
+  Name id1Key1Name;
+  Name id1Key2Name;
+  Name id2Key1Name;
+  Name id2Key2Name;
+
+  Buffer id1Key1;
+  Buffer id1Key2;
+  Buffer id2Key1;
+  Buffer id2Key2;
+};
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_PIB_DATA_FIXTURE_HPP
diff --git a/tests/unit/security/pib/pib-impl.t.cpp b/tests/unit/security/pib/pib-impl.t.cpp
new file mode 100644
index 0000000..6094b0e
--- /dev/null
+++ b/tests/unit/security/pib/pib-impl.t.cpp
@@ -0,0 +1,350 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/pib-memory.hpp"
+#include "security/pib/pib-sqlite3.hpp"
+#include "security/pib/pib.hpp"
+#include "security/security-common.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+#include <boost/filesystem.hpp>
+#include <boost/mpl/list.hpp>
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(TestPibImpl)
+
+using pib::Pib;
+
+class PibMemoryFixture : public PibDataFixture
+{
+public:
+  PibMemory pib;
+};
+
+class PibSqlite3Fixture : public PibDataFixture
+{
+public:
+  PibSqlite3Fixture()
+    : tmpPath(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "DbTest")
+    , pib(tmpPath.c_str())
+  {
+  }
+
+  ~PibSqlite3Fixture()
+  {
+    boost::filesystem::remove_all(tmpPath);
+  }
+
+public:
+  boost::filesystem::path tmpPath;
+  PibSqlite3 pib;
+};
+
+typedef boost::mpl::list<PibMemoryFixture,
+                         PibSqlite3Fixture> PibImpls;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(TpmLocator, T, PibImpls, T)
+{
+  // Basic getting and setting
+  BOOST_CHECK_NO_THROW(this->pib.getTpmLocator());
+
+  BOOST_CHECK_NO_THROW(this->pib.setTpmLocator("tpmLocator"));
+  BOOST_CHECK_EQUAL(this->pib.getTpmLocator(), "tpmLocator");
+
+  // Add cert, and do not change TPM locator
+  this->pib.addCertificate(this->id1Key1Cert1);
+  BOOST_CHECK(this->pib.hasIdentity(this->id1));
+  BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+  BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+
+  // Set TPM locator to the same value, nothing should change
+  this->pib.setTpmLocator("tpmLocator");
+  BOOST_CHECK(this->pib.hasIdentity(this->id1));
+  BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+  BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+
+  // Change TPM locator (contents of PIB should not change)
+  this->pib.setTpmLocator("newTpmLocator");
+  BOOST_CHECK(this->pib.hasIdentity(this->id1));
+  BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+  BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(IdentityManagement, T, PibImpls, T)
+{
+  // no default setting, throw Error
+  BOOST_CHECK_THROW(this->pib.getDefaultIdentity(), Pib::Error);
+
+  // check id1, which should not exist
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), false);
+
+  // add id1, should be default
+  this->pib.addIdentity(this->id1);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
+  BOOST_CHECK_NO_THROW(this->pib.getDefaultIdentity());
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+  // add id2, should not be default
+  this->pib.addIdentity(this->id2);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id2), true);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+  // set id2 explicitly as default
+  this->pib.setDefaultIdentity(this->id2);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id2);
+
+  // remove id2, should not have default identity
+  this->pib.removeIdentity(this->id2);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id2), false);
+  BOOST_CHECK_THROW(this->pib.getDefaultIdentity(), Pib::Error);
+
+  // add id2 again, should be default
+  this->pib.addIdentity(this->id2);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id2);
+
+  // get all identities, should contain id1 and id2
+  std::set<Name> idNames = this->pib.getIdentities();
+  BOOST_CHECK_EQUAL(idNames.size(), 2);
+  BOOST_CHECK_EQUAL(idNames.count(this->id1), 1);
+  BOOST_CHECK_EQUAL(idNames.count(this->id2), 1);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ClearIdentities, T, PibImpls, T)
+{
+  this->pib.setTpmLocator("tpmLocator");
+
+  // Add id, key, and cert
+  this->pib.addCertificate(this->id1Key1Cert1);
+  BOOST_CHECK(this->pib.hasIdentity(this->id1));
+  BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+  BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+
+  // Clear identities
+  this->pib.clearIdentities();
+  BOOST_CHECK_EQUAL(this->pib.getIdentities().size(), 0);
+  BOOST_CHECK_EQUAL(this->pib.getKeysOfIdentity(this->id1).size(), 0);
+  BOOST_CHECK_EQUAL(this->pib.getCertificatesOfKey(this->id1Key1Name).size(), 0);
+  BOOST_CHECK_EQUAL(this->pib.getTpmLocator(), "tpmLocator");
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(KeyManagement, T, PibImpls, T)
+{
+  // no default setting, throw Error
+  BOOST_CHECK_THROW(this->pib.getDefaultKeyOfIdentity(this->id1), Pib::Error);
+
+  // check id1Key1, should not exist, neither should id1.
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), false);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), false);
+
+  // add id1Key1, should be default, id1 should be added implicitly
+  this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key1.data(), this->id1Key1.size());
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
+  const Buffer& keyBits = this->pib.getKeyBits(this->id1Key1Name);
+  BOOST_CHECK(keyBits == this->id1Key1);
+  BOOST_CHECK_NO_THROW(this->pib.getDefaultKeyOfIdentity(this->id1));
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key1Name);
+
+  // add id1Key2, should not be default
+  this->pib.addKey(this->id1, this->id1Key2Name, this->id1Key2.data(), this->id1Key2.size());
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key2Name), true);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key1Name);
+
+  // set id1Key2 explicitly as default
+  this->pib.setDefaultKeyOfIdentity(this->id1, this->id1Key2Name);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key2Name);
+
+  // set a non-existing key as default, throw Error
+  BOOST_CHECK_THROW(this->pib.setDefaultKeyOfIdentity(this->id1, Name("/non-existing")),
+                    Pib::Error);
+
+  // remove id1Key2, should not have default key
+  this->pib.removeKey(this->id1Key2Name);
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key2Name), false);
+  BOOST_CHECK_THROW(this->pib.getKeyBits(this->id1Key2Name), Pib::Error);
+  BOOST_CHECK_THROW(this->pib.getDefaultKeyOfIdentity(this->id1), Pib::Error);
+
+  // add id1Key2 back, should be default
+  this->pib.addKey(this->id1, this->id1Key2Name, this->id1Key2.data(), this->id1Key2.size());
+  BOOST_CHECK_NO_THROW(this->pib.getKeyBits(this->id1Key2Name));
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key2Name);
+
+  // get all the keys: id1Key1 and id1Key2
+  std::set<Name> keyNames = this->pib.getKeysOfIdentity(this->id1);
+  BOOST_CHECK_EQUAL(keyNames.size(), 2);
+  BOOST_CHECK_EQUAL(keyNames.count(this->id1Key1Name), 1);
+  BOOST_CHECK_EQUAL(keyNames.count(this->id1Key2Name), 1);
+
+  // remove id1, should remove all the keys
+  this->pib.removeIdentity(this->id1);
+  keyNames = this->pib.getKeysOfIdentity(this->id1);
+  BOOST_CHECK_EQUAL(keyNames.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(CertificateManagement, T, PibImpls, T)
+{
+  // no default setting, throw Error
+  BOOST_CHECK_THROW(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), Pib::Error);
+
+  // check id1Key1Cert1, should not exist, neither should id1 and id1Key1
+  BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), false);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), false);
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), false);
+
+  // add id1Key1Cert1, should be default, id1 and id1Key1 should be added implicitly
+  this->pib.addCertificate(this->id1Key1Cert1);
+  BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), true);
+  BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
+  BOOST_CHECK_EQUAL(this->pib.getCertificate(this->id1Key1Cert1.getName()).wireEncode(),
+                    this->id1Key1Cert1.wireEncode());
+  BOOST_CHECK_NO_THROW(this->pib.getDefaultCertificateOfKey(this->id1Key1Name));
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert1);
+
+  // add id1Key1Cert2, should not be default
+  this->pib.addCertificate(this->id1Key1Cert2);
+  BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert2.getName()), true);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert1);
+
+  // set id1Key1Cert2 explicitly as default
+  this->pib.setDefaultCertificateOfKey(this->id1Key1Name, this->id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert2);
+
+  // set a non-existing cert as default, throw Error
+  BOOST_CHECK_THROW(this->pib.setDefaultCertificateOfKey(this->id1Key1Name, Name("/non-existing")),
+                    Pib::Error);
+
+  // remove id1Key1Cert2, should not have default cert
+  this->pib.removeCertificate(this->id1Key1Cert2.getName());
+  BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert2.getName()), false);
+  BOOST_CHECK_THROW(this->pib.getCertificate(this->id1Key1Cert2.getName()), Pib::Error);
+  BOOST_CHECK_THROW(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), Pib::Error);
+
+  // add id1Key1Cert2, should be default
+  this->pib.addCertificate(this->id1Key1Cert2);
+  BOOST_CHECK_NO_THROW(this->pib.getCertificate(this->id1Key1Cert1.getName()));
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert2);
+
+  // get all certificates: id1Key1Cert1 and id1Key1Cert2
+  std::set<Name> certNames = this->pib.getCertificatesOfKey(this->id1Key1Name);
+  BOOST_CHECK_EQUAL(certNames.size(), 2);
+  BOOST_CHECK_EQUAL(certNames.count(this->id1Key1Cert1.getName()), 1);
+  BOOST_CHECK_EQUAL(certNames.count(this->id1Key1Cert2.getName()), 1);
+
+  // remove id1Key1, should remove all the certs
+  this->pib.removeKey(this->id1Key1Name);
+  certNames = this->pib.getCertificatesOfKey(this->id1Key1Name);
+  BOOST_CHECK_EQUAL(certNames.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(DefaultsManagement, T, PibImpls, T)
+{
+  this->pib.addIdentity(this->id1);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+  this->pib.addIdentity(this->id2);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+  this->pib.removeIdentity(this->id1);
+  BOOST_CHECK_THROW(this->pib.getDefaultIdentity(), Pib::Error);
+
+  this->pib.addKey(this->id2, this->id2Key1Name, this->id2Key1.data(), this->id2Key1.size());
+  BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id2);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id2), this->id2Key1Name);
+
+  this->pib.addKey(this->id2, this->id2Key2Name, this->id2Key2.data(), this->id2Key2.size());
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id2), this->id2Key1Name);
+
+  this->pib.removeKey(this->id2Key1Name);
+  BOOST_CHECK_THROW(this->pib.getDefaultKeyOfIdentity(this->id2), Pib::Error);
+
+  this->pib.addCertificate(this->id2Key2Cert1);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id2), this->id2Key2Name);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id2Key2Name).getName(), this->id2Key2Cert1.getName());
+
+  this->pib.addCertificate(this->id2Key2Cert2);
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id2Key2Name).getName(), this->id2Key2Cert1.getName());
+
+  this->pib.removeCertificate(this->id2Key2Cert2.getName());
+  BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id2Key2Name).getName(), this->id2Key2Cert1.getName());
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Overwrite, T, PibImpls, T)
+{
+  // check id1Key1, should not exist
+  this->pib.removeIdentity(this->id1);
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), false);
+
+  // add id1Key1
+  this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key1.data(), this->id1Key1.size());
+  BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
+  const Buffer& keyBits = this->pib.getKeyBits(this->id1Key1Name);
+  BOOST_CHECK(keyBits == this->id1Key1);
+
+  // check overwrite, add a key with the same name.
+  this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key2.data(), this->id1Key2.size());
+  const Buffer& keyBits2 = this->pib.getKeyBits(this->id1Key1Name);
+  BOOST_CHECK(keyBits2 == this->id1Key2);
+
+  // check id1Key1Cert1, should not exist
+  this->pib.removeIdentity(this->id1);
+  BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), false);
+
+  // add id1Key1Cert1
+  this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key1.data(), this->id1Key1.size());
+  this->pib.addCertificate(this->id1Key1Cert1);
+  BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), true);
+
+  auto cert = this->pib.getCertificate(this->id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(cert.wireEncode(), this->id1Key1Cert1.wireEncode());
+
+  // Create a fake cert with the same name
+  auto cert2 = this->id1Key2Cert1;
+  cert2.setName(this->id1Key1Cert1.getName());
+  cert2.setSignature(this->id1Key2Cert1.getSignature());
+  this->pib.addCertificate(cert2);
+
+  auto cert3 = this->pib.getCertificate(this->id1Key1Cert1.getName());
+  BOOST_CHECK_EQUAL(cert3.wireEncode(), cert2.wireEncode());
+
+  // both key and certificate are overwritten
+  Buffer keyBits3 = this->pib.getKeyBits(this->id1Key1Name);
+  BOOST_CHECK(keyBits3 == this->id1Key2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPibImpl
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-memory.t.cpp b/tests/unit/security/pib/pib-memory.t.cpp
new file mode 100644
index 0000000..72b0db8
--- /dev/null
+++ b/tests/unit/security/pib/pib-memory.t.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(TestPibMemory)
+
+// Functionality is tested as part of pib-impl.t.cpp
+
+BOOST_AUTO_TEST_SUITE_END() // TestPibMemory
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-sqlite3.t.cpp b/tests/unit/security/pib/pib-sqlite3.t.cpp
new file mode 100644
index 0000000..b46cb50
--- /dev/null
+++ b/tests/unit/security/pib/pib-sqlite3.t.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/pib-sqlite3.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(TestPibSqlite3)
+
+// Functionality is tested as part of pib-impl.t.cpp
+
+BOOST_AUTO_TEST_SUITE_END() // TestPibSqlite3
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib.t.cpp b/tests/unit/security/pib/pib.t.cpp
new file mode 100644
index 0000000..0ce5afc
--- /dev/null
+++ b/tests/unit/security/pib/pib.t.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestPib, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(ValidityChecking)
+{
+  Pib pib("pib-memory", "", make_shared<PibMemory>());
+
+  Identity id = pib.addIdentity(id1);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(id), true);
+  BOOST_CHECK_EQUAL(!id, false);
+
+  if (id)
+    BOOST_CHECK(true);
+  else
+    BOOST_CHECK(false);
+
+  // key
+  Key key = id.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(key), true);
+  BOOST_CHECK_EQUAL(!key, false);
+
+  if (key)
+    BOOST_CHECK(true);
+  else
+    BOOST_CHECK(false);
+}
+
+BOOST_AUTO_TEST_CASE(TpmLocator)
+{
+  Pib pib("pib-memory", "", make_shared<PibMemory>());
+
+  BOOST_CHECK_EQUAL(pib.getPibLocator(), "pib-memory:");
+  BOOST_CHECK_THROW(pib.getTpmLocator(), Pib::Error);
+
+  pib.setTpmLocator("test-tpm-locator");
+  BOOST_CHECK_NO_THROW(pib.getTpmLocator());
+
+  BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+  pib.addIdentity(id1);
+  BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+
+  pib.setTpmLocator("another-tpm-locator");
+  BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+
+  pib.addIdentity(id1);
+  BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+  pib.reset();
+  BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+  BOOST_CHECK_THROW(pib.getTpmLocator(), Pib::Error);
+}
+
+BOOST_AUTO_TEST_CASE(IdentityOperations)
+{
+  Pib pib("pib-memory", "", make_shared<PibMemory>());
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+
+  // get non-existing identity, throw Pib::Error
+  BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+  // get default identity when it is not set yet, throw Pib::Error
+  BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+
+  // add identity
+  pib.addIdentity(id1);
+  BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+
+  // new key becomes default key when there was no default key
+  BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+  BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id1);
+
+  // remove identity
+  pib.removeIdentity(id1);
+  BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+  BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+
+  // set default identity
+  BOOST_REQUIRE_NO_THROW(pib.setDefaultIdentity(id1));
+  BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+  BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id1);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+  BOOST_REQUIRE_NO_THROW(pib.setDefaultIdentity(id2));
+  BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+  BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id2);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 2);
+
+  // remove default identity
+  pib.removeIdentity(id2);
+  BOOST_CHECK_THROW(pib.getIdentity(id2), Pib::Error);
+  BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+  pib.removeIdentity(id1);
+  BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+  BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPib
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/safe-bag.t.cpp b/tests/unit/security/safe-bag.t.cpp
new file mode 100644
index 0000000..0423c69
--- /dev/null
+++ b/tests/unit/security/safe-bag.t.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ */
+
+#include "security/safe-bag.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestSafeBag)
+
+const uint8_t CERT[] = {
+  0x06, 0xc8, // Data
+      0x07, 0x14, // Name
+          0x08, 0x05,
+              0x6c, 0x6f, 0x63, 0x61, 0x6c,
+          0x08, 0x03,
+              0x6e, 0x64, 0x6e,
+          0x08, 0x06,
+              0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+      0x14, 0x07, // MetaInfo
+          0x18, 0x01, // ContentType
+              0x02,
+          0x19, 0x02, // FreshnessPeriod
+              0x27, 0x10,
+      0x15, 0x08, // Content
+          0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x21,
+      0x16, 0x1b, // SignatureInfo
+          0x1b, 0x01, // SignatureType
+              0x01,
+          0x1c, 0x16, // KeyLocator
+              0x07, 0x14, // Name
+                  0x08, 0x04,
+                      0x74, 0x65, 0x73, 0x74,
+                  0x08, 0x03,
+                      0x6b, 0x65, 0x79,
+                  0x08, 0x07,
+                      0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72,
+      0x17, 0x80, // SignatureValue
+          0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+          0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+          0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+          0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+          0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+          0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+          0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+          0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+          0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+          0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+const uint8_t ENCRYPTED_KEY_BAG[] = {
+  0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe
+};
+
+const uint8_t SAFE_BAG[] = {
+  0x80, 0xd4, // SafeBag
+      0x06, 0xc8, // Data
+          0x07, 0x14, // Name
+              0x08, 0x05,
+                  0x6c, 0x6f, 0x63, 0x61, 0x6c,
+              0x08, 0x03,
+                  0x6e, 0x64, 0x6e,
+              0x08, 0x06,
+                  0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+          0x14, 0x07, // MetaInfo
+              0x18, 0x01, // ContentType
+                  0x02,
+              0x19, 0x02, // FreshnessPeriod
+                  0x27, 0x10,
+          0x15, 0x08, // Content
+              0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x21,
+          0x16, 0x1b, // SignatureInfo
+              0x1b, 0x01, // SignatureType
+                  0x01,
+              0x1c, 0x16, // KeyLocator
+                  0x07, 0x14, // Name
+                      0x08, 0x04,
+                          0x74, 0x65, 0x73, 0x74,
+                      0x08, 0x03,
+                          0x6b, 0x65, 0x79,
+                      0x08, 0x07,
+                          0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72,
+          0x17, 0x80, // SignatureValue
+              0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+              0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+              0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+              0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+              0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+              0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+              0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+              0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+              0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+              0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1,
+      0x81, 0x08, // EncryptedKeyBag
+          0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe
+};
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+  Block dataBlock(CERT, sizeof(CERT));
+  Data data(dataBlock);
+  SafeBag safeBag1(data, ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+
+  Block safeBagBlock(SAFE_BAG, sizeof(SAFE_BAG));
+  SafeBag safeBag2(safeBagBlock);
+
+  Buffer buffer(ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+  SafeBag safeBag3(data, buffer);
+
+  BOOST_CHECK(safeBag1.getCertificate() == data);
+  BOOST_CHECK(safeBag1.getEncryptedKeyBag() == buffer);
+  BOOST_CHECK(safeBag2.getCertificate() == data);
+  BOOST_CHECK(safeBag2.getEncryptedKeyBag() == buffer);
+  BOOST_CHECK(safeBag3.getCertificate() == data);
+  BOOST_CHECK(safeBag3.getEncryptedKeyBag() == buffer);
+}
+
+BOOST_AUTO_TEST_CASE(EncoderAndDecoder)
+{
+  Block dataBlock(CERT, sizeof(CERT));
+  Data data(dataBlock);
+  SafeBag safeBag(data, ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+
+  // wire encode
+  Block wireBlock = safeBag.wireEncode();
+  Block block(SAFE_BAG, sizeof(SAFE_BAG));
+
+  // check safe bag block
+  BOOST_CHECK_EQUAL(wireBlock, block);
+
+  // wire decode
+  SafeBag safeBag2;
+  safeBag2.wireDecode(wireBlock);
+
+  // check equal
+  Buffer buffer1 = safeBag2.getEncryptedKeyBag();
+  Buffer buffer2(ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+  BOOST_CHECK(buffer1 == buffer2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSafeBag
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signature-sha256-with-ecdsa.t.cpp b/tests/unit/security/signature-sha256-with-ecdsa.t.cpp
new file mode 100644
index 0000000..2f90302
--- /dev/null
+++ b/tests/unit/security/signature-sha256-with-ecdsa.t.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/signature-sha256-with-ecdsa.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/scheduler.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+class SignatureSha256EcdsaTimeFixture : public IdentityManagementTimeFixture
+{
+public:
+  SignatureSha256EcdsaTimeFixture()
+    : scheduler(io)
+  {
+  }
+
+public:
+  Scheduler scheduler;
+};
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestSignatureSha256WithEcdsa, SignatureSha256EcdsaTimeFixture)
+
+const uint8_t sigInfo[] = {
+  0x16, 0x1b, // SignatureInfo
+    0x1b, 0x01, // SignatureType
+      0x03,
+    0x1c, 0x16, // KeyLocator
+      0x07, 0x14, // Name: /test/key/locator
+        0x08, 0x04,
+          0x74, 0x65, 0x73, 0x74,
+        0x08, 0x03,
+          0x6b, 0x65, 0x79,
+        0x08, 0x07,
+          0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+};
+
+const uint8_t sigValue[] = {
+  0x17, 0x40, // SignatureValue
+    0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+    0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+    0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+    0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+    0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b
+};
+
+
+BOOST_AUTO_TEST_CASE(Decoding)
+{
+  Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+  Block sigValueBlock(sigValue, sizeof(sigValue));
+
+  Signature sig(sigInfoBlock, sigValueBlock);
+  BOOST_CHECK_NO_THROW(SignatureSha256WithEcdsa{sig});
+  BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+}
+
+BOOST_AUTO_TEST_CASE(Encoding)
+{
+  Name name("/test/key/locator");
+  KeyLocator keyLocator(name);
+
+  SignatureSha256WithEcdsa sig(keyLocator);
+
+  BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+
+  const Block& encodeSigInfoBlock = sig.getInfo();
+
+  Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(),
+                                sigInfoBlock.wire() + sigInfoBlock.size(),
+                                encodeSigInfoBlock.wire(),
+                                encodeSigInfoBlock.wire() + encodeSigInfoBlock.size());
+
+  sig.setKeyLocator(Name("/test/another/key/locator"));
+
+  const Block& encodeSigInfoBlock2 = sig.getInfo();
+  BOOST_CHECK_NE(sigInfoBlock, encodeSigInfoBlock2);
+}
+
+BOOST_AUTO_TEST_CASE(DataSignature)
+{
+  Identity identity = addIdentity("/SecurityTestSignatureSha256WithEcdsa/DataSignature", EcKeyParams());
+
+  Data testData("/SecurityTestSignatureSha256WithEcdsa/DataSignature/Data1");
+  char content[5] = "1234";
+  testData.setContent(reinterpret_cast<uint8_t*>(content), 5);
+  BOOST_CHECK_NO_THROW(m_keyChain.sign(testData, security::SigningInfo(identity)));
+  Block dataBlock(testData.wireEncode().wire(), testData.wireEncode().size());
+
+  Data testData2;
+  testData2.wireDecode(dataBlock);
+  BOOST_CHECK(verifySignature(testData2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_CASE(InterestSignature)
+{
+  Identity identity = addIdentity("/SecurityTestSignatureSha256WithEcdsa/InterestSignature", EcKeyParams());
+
+  auto interest = makeInterest("/SecurityTestSignatureSha256WithEcdsa/InterestSignature/Interest1");
+  auto interest11 = makeInterest("/SecurityTestSignatureSha256WithEcdsa/InterestSignature/Interest1");
+
+  scheduler.scheduleEvent(100_ms, [&] {
+      m_keyChain.sign(*interest, security::SigningInfo(identity));
+    });
+
+  advanceClocks(100_ms);
+  scheduler.scheduleEvent(100_ms, [&] {
+      m_keyChain.sign(*interest11, security::SigningInfo(identity));
+    });
+
+  advanceClocks(100_ms);
+
+  Block interestBlock(interest->wireEncode().wire(), interest->wireEncode().size());
+
+  Interest interest2;
+  interest2.wireDecode(interestBlock);
+  BOOST_CHECK(verifySignature(interest2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignatureSha256WithEcdsa
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signature-sha256-with-rsa.t.cpp b/tests/unit/security/signature-sha256-with-rsa.t.cpp
new file mode 100644
index 0000000..7de3774
--- /dev/null
+++ b/tests/unit/security/signature-sha256-with-rsa.t.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/signature-sha256-with-rsa.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/scheduler.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+class SignatureSha256RsaTimeFixture : public IdentityManagementTimeFixture
+{
+public:
+  SignatureSha256RsaTimeFixture()
+    : scheduler(io)
+  {
+  }
+
+public:
+  Scheduler scheduler;
+};
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestSignatureSha256WithRsa, SignatureSha256RsaTimeFixture)
+
+const uint8_t sigInfo[] = {
+  0x16, 0x1b, // SignatureInfo
+    0x1b, 0x01, // SignatureType
+      0x01,
+    0x1c, 0x16, // KeyLocator
+      0x07, 0x14, // Name
+        0x08, 0x04,
+          0x74, 0x65, 0x73, 0x74,
+        0x08, 0x03,
+          0x6b, 0x65, 0x79,
+        0x08, 0x07,
+          0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+};
+
+const uint8_t sigValue[] = {
+0x17, 0x80, // SignatureValue
+  0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+  0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+  0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+  0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+  0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+  0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+  0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+  0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+  0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+  0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+
+BOOST_AUTO_TEST_CASE(Decoding)
+{
+  Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+  Block sigValueBlock(sigValue, sizeof(sigValue));
+
+  Signature sig(sigInfoBlock, sigValueBlock);
+  BOOST_CHECK_NO_THROW(SignatureSha256WithRsa{sig});
+  BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+}
+
+BOOST_AUTO_TEST_CASE(Encoding)
+{
+  Name name("/test/key/locator");
+  KeyLocator keyLocator(name);
+
+  SignatureSha256WithRsa sig(keyLocator);
+
+  BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+
+  const Block& encodeSigInfoBlock = sig.getInfo();
+
+  Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(),
+                                sigInfoBlock.wire() + sigInfoBlock.size(),
+                                encodeSigInfoBlock.wire(),
+                                encodeSigInfoBlock.wire() + encodeSigInfoBlock.size());
+
+  sig.setKeyLocator(Name("/test/another/key/locator"));
+
+  const Block& encodeSigInfoBlock2 = sig.getInfo();
+  BOOST_CHECK_NE(sigInfoBlock, encodeSigInfoBlock2);
+}
+
+BOOST_AUTO_TEST_CASE(DataSignature)
+{
+  Identity identity = addIdentity("/SecurityTestSignatureSha256WithRsa/DataSignature", RsaKeyParams());
+
+  Data testData("/SecurityTestSignatureSha256WithRsa/DataSignature/Data1");
+  char content[5] = "1234";
+  testData.setContent(reinterpret_cast<uint8_t*>(content), 5);
+  BOOST_CHECK_NO_THROW(m_keyChain.sign(testData, security::SigningInfo(identity)));
+  Block dataBlock(testData.wireEncode().wire(), testData.wireEncode().size());
+
+  Data testData2;
+  testData2.wireDecode(dataBlock);
+  BOOST_CHECK(verifySignature(testData2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_CASE(InterestSignature)
+{
+  Identity identity = addIdentity("/SecurityTestSignatureSha256WithRsa/InterestSignature", RsaKeyParams());
+
+  auto interest = makeInterest("/SecurityTestSignatureSha256WithRsa/InterestSignature/Interest1");
+  auto interest11 = makeInterest("/SecurityTestSignatureSha256WithRsa/InterestSignature/Interest1");
+
+  scheduler.scheduleEvent(100_ms, [&] {
+      m_keyChain.sign(*interest, security::SigningInfo(identity));
+    });
+
+  advanceClocks(100_ms);
+  scheduler.scheduleEvent(100_ms, [&] {
+      m_keyChain.sign(*interest11, security::SigningInfo(identity));
+    });
+
+  advanceClocks(100_ms);
+
+  Block interestBlock(interest->wireEncode().wire(), interest->wireEncode().size());
+
+  Interest interest2;
+  interest2.wireDecode(interestBlock);
+  BOOST_CHECK(verifySignature(interest2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignatureSha256WithRsa
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signing-helpers.t.cpp b/tests/unit/security/signing-helpers.t.cpp
new file mode 100644
index 0000000..af2c568
--- /dev/null
+++ b/tests/unit/security/signing-helpers.t.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestSigningHelpers)
+
+// update of this test case deferred until the new IdentityManagementFixture is available
+
+BOOST_AUTO_TEST_CASE(Identity)
+{
+  Name identity("/identity");
+  SigningInfo info = signingByIdentity(identity);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+  BOOST_CHECK_EQUAL(info.getSignerName(), identity);
+}
+
+BOOST_AUTO_TEST_CASE(Key)
+{
+  Name key("/key");
+  SigningInfo info = signingByKey(key);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+  BOOST_CHECK_EQUAL(info.getSignerName(), key);
+}
+
+BOOST_AUTO_TEST_CASE(Certificate)
+{
+  Name cert("/cert");
+  SigningInfo info = signingByCertificate(cert);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+  BOOST_CHECK_EQUAL(info.getSignerName(), cert);
+}
+
+BOOST_AUTO_TEST_CASE(Sha256)
+{
+  SigningInfo info = signingWithSha256();
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+  BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSigningHelpers
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signing-info.t.cpp b/tests/unit/security/signing-info.t.cpp
new file mode 100644
index 0000000..fc6f627
--- /dev/null
+++ b/tests/unit/security/signing-info.t.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/signing-info.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestSigningInfo)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  Name id("/my-identity");
+  Name key("/my-key");
+  Name cert("/my-cert");
+
+  SigningInfo info;
+
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
+  BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
+  BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  const SignatureInfo& sigInfo = info.getSignatureInfo();
+  BOOST_CHECK_EQUAL(sigInfo.getSignatureType(), -1);
+  BOOST_CHECK_EQUAL(sigInfo.hasKeyLocator(), false);
+
+  info.setSigningIdentity(id);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+  BOOST_CHECK_EQUAL(info.getSignerName(), id);
+  BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoId(SigningInfo::SIGNER_TYPE_ID, id);
+  BOOST_CHECK_EQUAL(infoId.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+  BOOST_CHECK_EQUAL(infoId.getSignerName(), id);
+  BOOST_CHECK_EQUAL(infoId.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  info.setSigningKeyName(key);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+  BOOST_CHECK_EQUAL(info.getSignerName(), key);
+  BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoKey(SigningInfo::SIGNER_TYPE_KEY, key);
+  BOOST_CHECK_EQUAL(infoKey.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+  BOOST_CHECK_EQUAL(infoKey.getSignerName(), key);
+  BOOST_CHECK_EQUAL(infoKey.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  info.setSigningCertName(cert);
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+  BOOST_CHECK_EQUAL(info.getSignerName(), cert);
+  BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoCert(SigningInfo::SIGNER_TYPE_CERT, cert);
+  BOOST_CHECK_EQUAL(infoCert.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+  BOOST_CHECK_EQUAL(infoCert.getSignerName(), cert);
+  BOOST_CHECK_EQUAL(infoCert.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  info.setSha256Signing();
+  BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+  BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
+  BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoSha(SigningInfo::SIGNER_TYPE_SHA256);
+  BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+  BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
+  BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_CASE(CustomSignatureInfo)
+{
+  SigningInfo info1;
+  BOOST_CHECK_EQUAL(info1.getSignatureInfo(), SignatureInfo());
+
+  SignatureInfo si;
+  si.setKeyLocator(Name("ndn:/test/key/locator"));
+  info1.setSignatureInfo(si);
+
+  BOOST_CHECK_EQUAL(info1.getSignatureInfo(), si);
+
+  SigningInfo info2(SigningInfo::SIGNER_TYPE_NULL, SigningInfo::getEmptyName(), si);
+  BOOST_CHECK_EQUAL(info2.getSignatureInfo(), si);
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+  SigningInfo infoDefault("");
+  BOOST_CHECK_EQUAL(infoDefault.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
+  BOOST_CHECK_EQUAL(infoDefault.getSignerName(), SigningInfo::getEmptyName());
+  BOOST_CHECK_EQUAL(infoDefault.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoId("id:/my-identity");
+  BOOST_CHECK_EQUAL(infoId.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+  BOOST_CHECK_EQUAL(infoId.getSignerName(), "/my-identity");
+  BOOST_CHECK_EQUAL(infoId.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoKey("key:/my-key");
+  BOOST_CHECK_EQUAL(infoKey.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+  BOOST_CHECK_EQUAL(infoKey.getSignerName(), "/my-key");
+  BOOST_CHECK_EQUAL(infoKey.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+  SigningInfo infoCert("cert:/my-cert");
+  BOOST_CHECK_EQUAL(infoCert.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+  BOOST_CHECK_EQUAL(infoCert.getSignerName(), "/my-cert");
+  BOOST_CHECK_EQUAL(infoCert.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());
+  BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+  // We can't use lexical_cast due to Boost Bug 6298.
+  std::stringstream ss;
+  ss << SigningInfo();
+  BOOST_CHECK_EQUAL(ss.str(), "");
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_ID, "/my-identity")), "id:/my-identity");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_KEY, "/my-key")), "key:/my-key");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_CERT, "/my-cert")), "cert:/my-cert");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+                    SigningInfo(SigningInfo::SIGNER_TYPE_SHA256)),
+                    "id:/localhost/identity/digest-sha256");
+}
+
+BOOST_AUTO_TEST_CASE(Chaining)
+{
+  SigningInfo info = SigningInfo()
+    .setSigningIdentity("/identity")
+    .setSigningKeyName("/key/name")
+    .setSigningCertName("/cert/name")
+    .setPibIdentity(Identity())
+    .setPibKey(Key())
+    .setSha256Signing()
+    .setDigestAlgorithm(DigestAlgorithm::SHA256)
+    .setSignatureInfo(SignatureInfo());
+
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info), "id:/localhost/identity/digest-sha256");
+}
+
+BOOST_AUTO_TEST_CASE(OperatorEquals)
+{
+  // Check name equality
+  SigningInfo info1("id:/my-id");
+  SigningInfo info2("id:/my-id");
+  BOOST_CHECK_EQUAL(info1, info2);
+  // Change name, check inequality
+  info2 = SigningInfo("id:/not-same-id");
+  BOOST_CHECK_NE(info1, info2);
+
+  // Check name, digest algo equality
+  info1 = SigningInfo("id:/my-id");
+  info2 = SigningInfo("id:/my-id");
+  info1.setDigestAlgorithm(DigestAlgorithm::SHA256);
+  info2.setDigestAlgorithm(DigestAlgorithm::SHA256);
+  BOOST_CHECK_EQUAL(info1, info2);
+  // Change digest algo, check inequality
+  info2.setDigestAlgorithm(DigestAlgorithm::NONE);
+  BOOST_CHECK_NE(info1, info2);
+
+  // Check name, digest algo, signature info equality
+  info1 = SigningInfo("id:/my-id");
+  info2 = SigningInfo("id:/my-id");
+  info1.setDigestAlgorithm(DigestAlgorithm::SHA256);
+  info2.setDigestAlgorithm(DigestAlgorithm::SHA256);
+  SignatureInfo sigInfo1(tlv::DigestSha256);
+  info1.setSignatureInfo(sigInfo1);
+  info2.setSignatureInfo(sigInfo1);
+  BOOST_CHECK_EQUAL(info1, info2);
+  // Change signature info, check inequality
+  SignatureInfo sigInfo2(tlv::SignatureSha256WithRsa);
+  info2.setSignatureInfo(sigInfo2);
+  BOOST_CHECK_NE(info1, info2);
+}
+
+BOOST_AUTO_TEST_CASE(OperatorEqualsDifferentTypes)
+{
+  SigningInfo info1("key:/my-id/KEY/1");
+  SigningInfo info2("key:/my-id/KEY/1");
+  // Check equality for key type
+  BOOST_CHECK_EQUAL(info1, info2);
+  info2 = SigningInfo("id:/my-id");
+  // Change signature type, check inequality
+  BOOST_CHECK_NE(info1, info2);
+  info2 = SigningInfo("key:/not-same-id/KEY/1");
+  // Change key name, check inequality
+  BOOST_CHECK_NE(info1, info2);
+
+  info1 = SigningInfo("cert:/my-id/KEY/1/self/%FD01");
+  info2 = SigningInfo("cert:/my-id/KEY/1/self/%FD01");
+  // Check equality for cert type
+  BOOST_CHECK_EQUAL(info1, info2);
+  info2 = SigningInfo("cert:/not-my-id/KEY/1/other/%FD01");
+  // Change cert name, check inequality
+  BOOST_CHECK_NE(info1, info2);
+  info2 = SigningInfo("id:/my-id");
+  // Change signature type, check inequality
+  BOOST_CHECK_NE(info1, info2);
+
+  info1 = SigningInfo(SigningInfo::SIGNER_TYPE_NULL);
+  info2 = SigningInfo(SigningInfo::SIGNER_TYPE_NULL);
+  // Check equality for null type
+  BOOST_CHECK_EQUAL(info1, info2);
+  info2 = SigningInfo("id:/my-id");
+  // Change signature type, check inequality
+  BOOST_CHECK_NE(info1, info2);
+
+  info1 = SigningInfo(SigningInfo::SIGNER_TYPE_SHA256);
+  info2 = SigningInfo(SigningInfo::SIGNER_TYPE_SHA256);
+  // Check equality for SHA256 digest type
+  BOOST_CHECK_EQUAL(info1, info2);
+  info2 = SigningInfo("id:/my-id");
+  // Change signature type, check inequality
+  BOOST_CHECK_NE(info1, info2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSigningInfo
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/tmp-home/.ndn/client.conf b/tests/unit/security/tmp-home/.ndn/client.conf
new file mode 100644
index 0000000..b832cfc
--- /dev/null
+++ b/tests/unit/security/tmp-home/.ndn/client.conf
@@ -0,0 +1,2 @@
+pib=pib-sqlite3:/tmp/test/ndn-cxx/keychain
+tpm=tpm-file:/tmp/test/ndn-cxx/keychain
\ No newline at end of file
diff --git a/tests/unit/security/tpm/back-end-wrapper-file.hpp b/tests/unit/security/tpm/back-end-wrapper-file.hpp
new file mode 100644
index 0000000..dabd6d3
--- /dev/null
+++ b/tests/unit/security/tpm/back-end-wrapper-file.hpp
@@ -0,0 +1,72 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_FILE_HPP
+#define NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_FILE_HPP
+
+#include "security/tpm/back-end-file.hpp"
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+/**
+ * @brief A wrapper of tpm::BackEndFile for unit test template.
+ */
+class BackEndWrapperFile
+{
+public:
+  BackEndWrapperFile()
+    : m_tmpPath(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "TpmFileTest")
+    , m_impl(make_unique<BackEndFile>(m_tmpPath.string()))
+  {
+  }
+
+  ~BackEndWrapperFile()
+  {
+    boost::filesystem::remove_all(m_tmpPath);
+  }
+
+  BackEnd&
+  getTpm()
+  {
+    return *m_impl;
+  }
+
+  std::string
+  getScheme() const
+  {
+    return "tpm-file";
+  }
+
+private:
+  const boost::filesystem::path m_tmpPath;
+  const unique_ptr<BackEnd> m_impl;
+};
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_FILE_HPP
diff --git a/tests/unit/security/tpm/back-end-wrapper-mem.hpp b/tests/unit/security/tpm/back-end-wrapper-mem.hpp
new file mode 100644
index 0000000..ddf629b
--- /dev/null
+++ b/tests/unit/security/tpm/back-end-wrapper-mem.hpp
@@ -0,0 +1,64 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_MEM_HPP
+#define NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_MEM_HPP
+
+#include "security/tpm/back-end-mem.hpp"
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+/**
+ * @brief A wrapper of tpm::BackEndMem for unit test template.
+ */
+class BackEndWrapperMem
+{
+public:
+  BackEndWrapperMem()
+    : m_impl(make_unique<BackEndMem>())
+  {
+  }
+
+  BackEnd&
+  getTpm()
+  {
+    return *m_impl;
+  }
+
+  std::string
+  getScheme() const
+  {
+    return "tpm-memory";
+  }
+
+private:
+  const unique_ptr<BackEnd> m_impl;
+};
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_MEM_HPP
diff --git a/tests/unit/security/tpm/back-end-wrapper-osx.hpp b/tests/unit/security/tpm/back-end-wrapper-osx.hpp
new file mode 100644
index 0000000..6d7d35b
--- /dev/null
+++ b/tests/unit/security/tpm/back-end-wrapper-osx.hpp
@@ -0,0 +1,88 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_OSX_HPP
+#define NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_OSX_HPP
+
+#include "security/tpm/back-end-osx.hpp"
+#include "security/tpm/key-handle-osx.hpp"
+
+#include <cstdlib>
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+/**
+ * @brief A wrapper of tpm::BackEndOsx for unit test template.
+ */
+class BackEndWrapperOsx
+{
+public:
+  BackEndWrapperOsx()
+  {
+    std::string oldHOME;
+    if (std::getenv("OLD_HOME"))
+      oldHOME = std::getenv("OLD_HOME");
+
+    if (std::getenv("HOME"))
+      m_HOME = std::getenv("HOME");
+
+    if (!oldHOME.empty())
+      setenv("HOME", oldHOME.data(), 1);
+    else
+      unsetenv("HOME");
+
+    m_impl = make_unique<BackEndOsx>();
+  }
+
+  ~BackEndWrapperOsx()
+  {
+    if (!m_HOME.empty())
+      setenv("HOME", m_HOME.data(), 1);
+    else
+      unsetenv("HOME");
+  }
+
+  BackEnd&
+  getTpm()
+  {
+    return *m_impl;
+  }
+
+  std::string
+  getScheme() const
+  {
+    return "tpm-osxkeychain";
+  }
+
+private:
+  std::string m_HOME;
+  unique_ptr<BackEnd> m_impl;
+};
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_OSX_HPP
diff --git a/tests/unit/security/tpm/back-end.t.cpp b/tests/unit/security/tpm/back-end.t.cpp
new file mode 100644
index 0000000..5512ebd
--- /dev/null
+++ b/tests/unit/security/tpm/back-end.t.cpp
@@ -0,0 +1,279 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/tpm/back-end.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/pib/key.hpp"
+#include "security/tpm/key-handle.hpp"
+#include "security/tpm/tpm.hpp"
+#include "security/transform/bool-sink.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/public-key.hpp"
+#include "security/transform/verifier-filter.hpp"
+
+#include "back-end-wrapper-file.hpp"
+#include "back-end-wrapper-mem.hpp"
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+#include "back-end-wrapper-osx.hpp"
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
+
+#include "boost-test.hpp"
+
+#include <boost/mpl/vector.hpp>
+#include <set>
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Tpm)
+BOOST_AUTO_TEST_SUITE(TestBackEnd)
+
+using tpm::Tpm;
+
+using TestBackEnds = boost::mpl::vector<
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+  BackEndWrapperOsx,
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
+  BackEndWrapperMem,
+  BackEndWrapperFile>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(KeyManagement, T, TestBackEnds)
+{
+  T wrapper;
+  BackEnd& tpm = wrapper.getTpm();
+
+  Name identity("/Test/KeyName");
+  name::Component keyId("1");
+  Name keyName = v2::constructKeyName(identity, keyId);
+
+  // key should not exist
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+  BOOST_CHECK(tpm.getKeyHandle(keyName) == nullptr);
+
+  // create key, should exist
+  BOOST_CHECK(tpm.createKey(identity, RsaKeyParams(keyId)) != nullptr);
+  BOOST_CHECK(tpm.hasKey(keyName));
+  BOOST_CHECK(tpm.getKeyHandle(keyName) != nullptr);
+
+  // create a key with the same name, should throw error
+  BOOST_CHECK_THROW(tpm.createKey(identity, RsaKeyParams(keyId)), Tpm::Error);
+
+  // delete key, should not exist
+  tpm.deleteKey(keyName);
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+  BOOST_CHECK(tpm.getKeyHandle(keyName) == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(RsaSigning, T, TestBackEnds)
+{
+  T wrapper;
+  BackEnd& tpm = wrapper.getTpm();
+
+  // create an RSA key
+  Name identity("/Test/KeyName");
+  unique_ptr<KeyHandle> key = tpm.createKey(identity, RsaKeyParams());
+  Name keyName = key->getKeyName();
+
+  const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+  Block sigBlock(tlv::SignatureValue, key->sign(DigestAlgorithm::SHA256, content, sizeof(content)));
+
+  transform::PublicKey pubKey;
+  ConstBufferPtr pubKeyBits = key->derivePublicKey();
+  pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
+
+  bool result;
+  {
+    using namespace transform;
+    bufferSource(content, sizeof(content)) >>
+      verifierFilter(DigestAlgorithm::SHA256, pubKey, sigBlock.value(), sigBlock.value_size()) >>
+      boolSink(result);
+  }
+  BOOST_CHECK_EQUAL(result, true);
+
+  tpm.deleteKey(keyName);
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(RsaDecryption, T, TestBackEnds)
+{
+  T wrapper;
+  BackEnd& tpm = wrapper.getTpm();
+
+  // create an RSA key
+  Name identity("/Test/KeyName");
+  unique_ptr<KeyHandle> key = tpm.createKey(identity, RsaKeyParams());
+  Name keyName = key->getKeyName();
+
+  const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+
+  transform::PublicKey pubKey;
+  ConstBufferPtr pubKeyBits = key->derivePublicKey();
+  pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
+
+  ConstBufferPtr cipherText = pubKey.encrypt(content, sizeof(content));
+  ConstBufferPtr plainText = key->decrypt(cipherText->data(), cipherText->size());
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(content, content + sizeof(content),
+                                plainText->begin(), plainText->end());
+
+  tpm.deleteKey(keyName);
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(EcdsaSigning, T, TestBackEnds)
+{
+  T wrapper;
+  BackEnd& tpm = wrapper.getTpm();
+
+  // create an EC key
+  Name identity("/Test/Ec/KeyName");
+  unique_ptr<KeyHandle> key = tpm.createKey(identity, EcKeyParams());
+  Name ecKeyName = key->getKeyName();
+
+  const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+  Block sigBlock(tlv::SignatureValue, key->sign(DigestAlgorithm::SHA256, content, sizeof(content)));
+
+  transform::PublicKey pubKey;
+  ConstBufferPtr pubKeyBits = key->derivePublicKey();
+  pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
+
+  bool result;
+  {
+    using namespace transform;
+    bufferSource(content, sizeof(content)) >> verifierFilter(DigestAlgorithm::SHA256, pubKey,
+                                                             sigBlock.value(), sigBlock.value_size())
+                                           >> boolSink(result);
+  }
+  BOOST_CHECK_EQUAL(result, true);
+
+  tpm.deleteKey(ecKeyName);
+  BOOST_CHECK_EQUAL(tpm.hasKey(ecKeyName), false);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ImportExport, T, TestBackEnds)
+{
+  const std::string privKeyPkcs1 =
+    "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
+    "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
+    "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
+    "c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9gZwIL5PuE9BiO6I39cL9z7EK1SfZh\n"
+    "OWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA9rH58ynaAix0tcR/nBMRLUX+e3rU\n"
+    "RHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0ywIDAQABAoIBADQkckOIl4IZMUTn\n"
+    "W8LFv6xOdkJwMKC8G6bsPRFbyY+HvC2TLt7epSvfS+f4AcYWaOPcDu2E49vt2sNr\n"
+    "cASly8hgwiRRAB3dHH9vcsboiTo8bi2RFvMqvjv9w3tK2yMxVDtmZamzrrnaV3YV\n"
+    "Q+5nyKo2F/PMDjQ4eUAKDOzjhBuKHsZBTFnA1MFNI+UKj5X4Yp64DFmKlxTX/U2b\n"
+    "wzVywo5hzx2Uhw51jmoLls4YUvMJXD0wW5ZtYRuPogXvXb/of9ef/20/wU11WFKg\n"
+    "Xb4gfR8zUXaXS1sXcnVm3+24vIs9dApUwykuoyjOqxWqcHRec2QT2FxVGkFEraze\n"
+    "CPa4rMECgYEA5Y8CywomIcTgerFGFCeMHJr8nQGqY2V/owFb3k9maczPnC9p4a9R\n"
+    "c5szLxA9FMYFxurQZMBWSEG2JS1HR2mnjigx8UKjYML/A+rvvjZOMe4M6Sy2ggh4\n"
+    "SkLZKpWTzjTe07ByM/j5v/SjNZhWAG7sw4/LmPGRQkwJv+KZhGojuOkCgYEA2cOF\n"
+    "T6cJRv6kvzTz9S0COZOVm+euJh/BXp7oAsAmbNfOpckPMzqHXy8/wpdKl6AAcB57\n"
+    "OuztlNfV1D7qvbz7JuRlYwQ0cEfBgbZPcz1p18HHDXhwn57ZPb8G33Yh9Omg0HNA\n"
+    "Imb4LsVuSqxA6NwSj7cpRekgTedrhLFPJ+Ydb5MCgYEAsM3Q7OjILcIg0t6uht9e\n"
+    "vrlwTsz1mtCV2co2I6crzdj9HeI2vqf1KAElDt6G7PUHhglcr/yjd8uEqmWRPKNX\n"
+    "ddnnfVZB10jYeP/93pac6z/Zmc3iU4yKeUe7U10ZFf0KkiiYDQd59CpLef/2XScS\n"
+    "HB0oRofnxRQjfjLc4muNT+ECgYEAlcDk06MOOTly+F8lCc1bA1dgAmgwFd2usDBd\n"
+    "Y07a3e0HGnGLN3Kfl7C5i0tZq64HvxLnMd2vgLVxQlXGPpdQrC1TH+XLXg+qnlZO\n"
+    "ivSH7i0/gx75bHvj75eH1XK65V8pDVDEoSPottllAIs21CxLw3N1ObOZWJm2EfmR\n"
+    "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
+    "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
+    "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
+  const std::string password("password");
+  const std::string wrongPassword("wrong");
+
+  T wrapper;
+  BackEnd& tpm = wrapper.getTpm();
+
+  Name keyName("/Test/KeyName/KEY/1");
+  tpm.deleteKey(keyName);
+  BOOST_REQUIRE_EQUAL(tpm.hasKey(keyName), false);
+
+  transform::PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privKeyPkcs1.data()), privKeyPkcs1.size());
+  OBufferStream os;
+  sKey.savePkcs8(os, password.data(), password.size());
+  auto pkcs8 = os.buf();
+
+  // import with wrong password
+  BOOST_CHECK_THROW(tpm.importKey(keyName, pkcs8->data(), pkcs8->size(), wrongPassword.data(), wrongPassword.size()),
+                    BackEnd::Error);
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+
+  // import with correct password
+  tpm.importKey(keyName, pkcs8->data(), pkcs8->size(), password.data(), password.size());
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), true);
+
+  // import already present key
+  BOOST_CHECK_THROW(tpm.importKey(keyName, pkcs8->data(), pkcs8->size(), password.data(), password.size()),
+                    BackEnd::Error);
+
+  // test derivePublicKey with the imported key
+  auto keyHdl = tpm.getKeyHandle(keyName);
+  auto pubKey = keyHdl->derivePublicKey();
+  BOOST_CHECK(pubKey != nullptr);
+
+  // export
+  auto exportedKey = tpm.exportKey(keyName, password.data(), password.size());
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), true);
+
+  transform::PrivateKey sKey2;
+  sKey2.loadPkcs8(exportedKey->data(), exportedKey->size(), password.data(), password.size());
+  OBufferStream os2;
+  sKey.savePkcs1Base64(os2);
+  auto pkcs1 = os2.buf();
+
+  // verify that the exported key is identical to the key that was imported
+  BOOST_CHECK_EQUAL_COLLECTIONS(privKeyPkcs1.begin(), privKeyPkcs1.end(),
+                                pkcs1->begin(), pkcs1->end());
+
+  // export nonexistent key
+  tpm.deleteKey(keyName);
+  BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+  BOOST_CHECK_THROW(tpm.exportKey(keyName, password.data(), password.size()), BackEnd::Error);
+}
+
+BOOST_AUTO_TEST_CASE(RandomKeyId)
+{
+  BackEndWrapperMem wrapper;
+  BackEnd& tpm = wrapper.getTpm();
+  Name identity("/Test/KeyName");
+
+  std::set<Name> keyNames;
+  for (int i = 0; i < 100; i++) {
+    auto key = tpm.createKey(identity, RsaKeyParams());
+    Name keyName = key->getKeyName();
+    BOOST_CHECK(keyNames.insert(keyName).second);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBackEnd
+BOOST_AUTO_TEST_SUITE_END() // Tpm
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform.t.cpp b/tests/unit/security/transform.t.cpp
new file mode 100644
index 0000000..1d55464
--- /dev/null
+++ b/tests/unit/security/transform.t.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestTransform)
+
+BOOST_AUTO_TEST_CASE(SymbolVisibility)
+{
+  transform::BufferSource* bufferSource = nullptr;
+  BOOST_CHECK(bufferSource == nullptr);
+
+  transform::StreamSource* streamSource = nullptr;
+  BOOST_CHECK(streamSource == nullptr);
+
+  transform::StepSource* stepSource = nullptr;
+  BOOST_CHECK(stepSource == nullptr);
+
+  transform::BoolSink* boolSink = nullptr;
+  BOOST_CHECK(boolSink == nullptr);
+
+  transform::StreamSink* streamSink = nullptr;
+  BOOST_CHECK(streamSink == nullptr);
+
+  transform::HexEncode* hexEncode = nullptr;
+  BOOST_CHECK(hexEncode == nullptr);
+
+  transform::StripSpace* stripSpace = nullptr;
+  BOOST_CHECK(stripSpace == nullptr);
+
+  transform::HexDecode* hexDecode = nullptr;
+  BOOST_CHECK(hexDecode == nullptr);
+
+  transform::Base64Encode* base64Encode = nullptr;
+  BOOST_CHECK(base64Encode == nullptr);
+
+  transform::Base64Decode* base64Decode = nullptr;
+  BOOST_CHECK(base64Decode == nullptr);
+
+  transform::DigestFilter* digestFilter = nullptr;
+  BOOST_CHECK(digestFilter == nullptr);
+
+  transform::HmacFilter* hmacFilter = nullptr;
+  BOOST_CHECK(hmacFilter == nullptr);
+
+  transform::BlockCipher* blockCipher = nullptr;
+  BOOST_CHECK(blockCipher == nullptr);
+
+  transform::SignerFilter* signerFilter = nullptr;
+  BOOST_CHECK(signerFilter == nullptr);
+
+  transform::VerifierFilter* verifierFilter = nullptr;
+  BOOST_CHECK(verifierFilter == nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTransform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/base64-decode.t.cpp b/tests/unit/security/transform/base64-decode.t.cpp
new file mode 100644
index 0000000..ecaa17c
--- /dev/null
+++ b/tests/unit/security/transform/base64-decode.t.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/base64-decode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBase64Decode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  std::string in =
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=\n";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  OBufferStream os;
+  bufferSource(in) >> base64Decode() >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(NoNewLine)
+{
+  std::string in =
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  OBufferStream os;
+  bufferSource(in) >> base64Decode(false) >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+  std::string in =
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  const uint8_t* input = reinterpret_cast<const uint8_t*>(in.data());
+  OBufferStream os;
+  StepSource source;
+  source >> base64Decode() >> streamSink(os);
+  source.write(input, 65);       // complete line with "\n"
+  source.write(input + 65, 64);  // complete line without "\n"
+  source.write(input + 129, 1);  // single "\n"
+  source.write(input + 130, 35); // front of a line
+  source.write(input + 165, 30); // end of a line with "\n"
+  source.write(input + 195, 25); // front of a line
+  source.write(input + 220, 20); // middle of a line
+  source.write(input + 240, 19); // end of a line without "\n"
+  source.write(input + 259, 101); // "\n" plus one and half line
+  source.write(input + 360, 65); // end of a line plus front of another line
+  source.write(input + 425, 95); // remaining
+  source.end();
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+  OBufferStream os;
+  StepSource source;
+  source >> base64Decode() >> streamSink(os);
+  source.end();
+  BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBase64Decode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/base64-encode.t.cpp b/tests/unit/security/transform/base64-encode.t.cpp
new file mode 100644
index 0000000..d52ac0b
--- /dev/null
+++ b/tests/unit/security/transform/base64-encode.t.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/base64-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBase64Encode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  uint8_t in[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::string out =
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=\n";
+
+  OBufferStream os;
+  bufferSource(in, sizeof(in)) >> base64Encode() >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(NoNewLine)
+{
+  uint8_t in[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::string out =
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=";
+
+  OBufferStream os;
+  BufferSource(in, sizeof(in)) >> base64Encode(false) >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+  uint8_t in[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::string out =
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+    "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n";
+
+  OBufferStream os;
+  StepSource source;
+  source >> base64Encode() >> streamSink(os);
+  source.write(in, 64); // complete chunk
+  source.write(in + 64, 32); // first half of a chunk
+  source.write(in + 96, 32); // second half of a chunk
+  source.write(in + 128, 24); // front of a chunk
+  source.write(in + 152, 20); // middle of a chunk
+  source.write(in + 172, 20); // end of a chunk
+  source.write(in + 192, 63); // odd number of bytes
+  source.write(in + 255, 85); // one and half chunk
+  source.write(in + 340, 44); // remaining part
+  source.end();
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+  OBufferStream os;
+  StepSource source;
+  source >> base64Encode() >> streamSink(os);
+  source.end();
+  BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBase64Encode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/block-cipher.t.cpp b/tests/unit/security/transform/block-cipher.t.cpp
new file mode 100644
index 0000000..379093d
--- /dev/null
+++ b/tests/unit/security/transform/block-cipher.t.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/block-cipher.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBlockCipher)
+
+BOOST_AUTO_TEST_CASE(AesCbc)
+{
+  const uint8_t key[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+  const uint8_t iv[] = {
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+  };
+  const uint8_t plainText[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+  //
+  // You can use the following shell one-liner to calculate the ciphertext:
+  //   echo ${plaintext} | xxd -p -r | openssl enc -aes-128-cbc -K ${key} -iv ${iv} | xxd -i
+  //
+  const uint8_t cipherText[] = {
+    0x07, 0x4d, 0x32, 0x68, 0xc3, 0x40, 0x64, 0x43,
+    0x1e, 0x66, 0x4c, 0x25, 0x66, 0x42, 0x0f, 0x59,
+    0x0a, 0x51, 0x19, 0x07, 0x67, 0x5c, 0x0e, 0xfa,
+    0xa6, 0x8c, 0xbb, 0xaf, 0xfd, 0xea, 0x47, 0xd4,
+    0xc7, 0x2c, 0x12, 0x34, 0x79, 0xde, 0xec, 0xc8,
+    0x75, 0x33, 0x8f, 0x6b, 0xd6, 0x55, 0xf3, 0xfa
+  };
+
+  // encrypt
+  OBufferStream os;
+  bufferSource(plainText, sizeof(plainText)) >>
+    blockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::ENCRYPT,
+                key, sizeof(key), iv, sizeof(iv)) >> streamSink(os);
+
+  auto buf = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(cipherText, cipherText + sizeof(cipherText),
+                                buf->begin(), buf->end());
+
+  // decrypt
+  OBufferStream os2;
+  bufferSource(cipherText, sizeof(cipherText)) >>
+    blockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::DECRYPT,
+                key, sizeof(key), iv, sizeof(iv)) >> streamSink(os2);
+
+  auto buf2 = os2.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
+                                buf2->begin(), buf2->end());
+
+  // invalid key length
+  const uint8_t badKey[] = {0x00, 0x01, 0x02, 0x03};
+  BOOST_CHECK_THROW(BlockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::ENCRYPT,
+                                badKey, sizeof(badKey), iv, sizeof(iv)), Error);
+
+  // wrong iv length
+  const uint8_t badIv[] = {0x00, 0x01, 0x02, 0x03};
+  BOOST_CHECK_THROW(BlockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::ENCRYPT,
+                                key, sizeof(key), badIv, sizeof(badIv)), Error);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidAlgorithm)
+{
+  BOOST_CHECK_THROW(BlockCipher(BlockCipherAlgorithm::NONE, CipherOperator::ENCRYPT,
+                                nullptr, 0, nullptr, 0), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBlockCipher
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/bool-sink.t.cpp b/tests/unit/security/transform/bool-sink.t.cpp
new file mode 100644
index 0000000..2ce633f
--- /dev/null
+++ b/tests/unit/security/transform/bool-sink.t.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/bool-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBoolSink)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  uint8_t in1[] = {0x00, 0x01};
+  bool value1 = true;
+  BoolSink sink1(value1);
+  BOOST_CHECK_EQUAL(sink1.write(in1, 1), 1);
+  BOOST_CHECK_EQUAL(sink1.write(in1 + 1, 1), 1);
+  sink1.end();
+  BOOST_CHECK_EQUAL(value1, false);
+  BOOST_CHECK_THROW(sink1.write(in1 + 1, 1), transform::Error);
+
+  uint8_t in2[] = {0x01, 0x00};
+  bool value2 = false;
+  BoolSink sink2(value2);
+  BOOST_CHECK_EQUAL(sink2.write(in2, 1), 1);
+  BOOST_CHECK_EQUAL(sink2.write(in2 + 1, 1), 1);
+  sink2.end();
+  BOOST_CHECK_EQUAL(value2, true);
+  BOOST_CHECK_THROW(sink2.write(in2 + 1, 1), transform::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBoolSink
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/buffer-source.t.cpp b/tests/unit/security/transform/buffer-source.t.cpp
new file mode 100644
index 0000000..cb7e7f4
--- /dev/null
+++ b/tests/unit/security/transform/buffer-source.t.cpp
@@ -0,0 +1,89 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBufferSource)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  uint8_t in[16] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::ostringstream os1;
+  bufferSource(in, sizeof(in)) >> streamSink(os1);
+  std::string out1 = os1.str();
+  BOOST_CHECK_EQUAL_COLLECTIONS(in, in + sizeof(in), out1.begin(), out1.end());
+
+  std::string in2 =
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567";
+
+  std::ostringstream os2;
+  bufferSource(in2) >> streamSink(os2);
+  std::string out2 = os2.str();
+  BOOST_CHECK_EQUAL_COLLECTIONS(in2.begin(), in2.end(), out2.begin(), out2.end());
+
+  Buffer in3(in, sizeof(in));
+  std::ostringstream os3;
+  bufferSource(in3) >> streamSink(os3);
+  std::string out3 = os3.str();
+  BOOST_CHECK_EQUAL_COLLECTIONS(in3.begin(), in3.end(), out3.begin(), out3.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBufferSource
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/digest-filter.t.cpp b/tests/unit/security/transform/digest-filter.t.cpp
new file mode 100644
index 0000000..5c0aaca
--- /dev/null
+++ b/tests/unit/security/transform/digest-filter.t.cpp
@@ -0,0 +1,222 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/digest-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/detail/openssl.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestDigestFilter)
+
+static const uint8_t in[] = {
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+static const uint8_t out[] = {
+  0x3e, 0x14, 0xfd, 0x66, 0x9a, 0x79, 0x80, 0x65,
+  0xc4, 0x0d, 0x61, 0xf8, 0x6a, 0xc7, 0x98, 0x29,
+  0xc0, 0x6b, 0x90, 0x8f, 0xbb, 0x19, 0xa0, 0x85,
+  0xf7, 0xfa, 0x7b, 0x2d, 0xd6, 0x8c, 0xd5, 0xa3
+};
+
+BOOST_AUTO_TEST_CASE(BufferInput)
+{
+  OBufferStream os;
+  bufferSource(in, sizeof(in)) >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepInput)
+{
+  StepSource source;
+  OBufferStream os;
+  source >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+  source.write(in, 32);
+  source.write(in + 32, 1);
+  source.write(in + 33, 2);
+  source.write(in + 35, 3);
+  source.write(in + 38, 26);
+  source.write(in + 64, 64);
+  source.end();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmNone)
+{
+  BOOST_CHECK_THROW(DigestFilter{DigestAlgorithm::NONE}, Error);
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha224)
+{
+  const uint8_t out[] = {
+    0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47, 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4,
+    0x15, 0xa2, 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4, 0x2f,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA224) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha256)
+{
+  const uint8_t out[] = {
+    0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+    0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha384)
+{
+  const uint8_t out[] = {
+    0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a,
+    0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda,
+    0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA384) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha512)
+{
+  const uint8_t out[] = {
+    0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07,
+    0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce,
+    0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f,
+    0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA512) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_BLAKE2)
+BOOST_AUTO_TEST_CASE(AlgorithmBlake2b_512)
+{
+  const uint8_t out[] = {
+    0x78, 0x6a, 0x02, 0xf7, 0x42, 0x01, 0x59, 0x03, 0xc6, 0xc6, 0xfd, 0x85, 0x25, 0x52, 0xd2, 0x72,
+    0x91, 0x2f, 0x47, 0x40, 0xe1, 0x58, 0x47, 0x61, 0x8a, 0x86, 0xe2, 0x17, 0xf7, 0x1f, 0x54, 0x19,
+    0xd2, 0x5e, 0x10, 0x31, 0xaf, 0xee, 0x58, 0x53, 0x13, 0x89, 0x64, 0x44, 0x93, 0x4e, 0xb0, 0x4b,
+    0x90, 0x3a, 0x68, 0x5b, 0x14, 0x48, 0xb7, 0x55, 0xd5, 0x6f, 0x70, 0x1a, 0xfe, 0x9b, 0xe2, 0xce,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::BLAKE2B_512) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmBlake2s_256)
+{
+  const uint8_t out[] = {
+    0x69, 0x21, 0x7a, 0x30, 0x79, 0x90, 0x80, 0x94, 0xe1, 0x11, 0x21, 0xd0, 0x42, 0x35, 0x4a, 0x7c,
+    0x1f, 0x55, 0xb6, 0x48, 0x2c, 0xa1, 0xa5, 0x1e, 0x1b, 0x25, 0x0d, 0xfd, 0x1e, 0xd0, 0xee, 0xf9,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::BLAKE2S_256) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_BLAKE2)
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101001L
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_224)
+{
+  const uint8_t out[] = {
+    0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e, 0xb1, 0xab,
+    0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a, 0x6b, 0xc7,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_224) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_256)
+{
+  const uint8_t out[] = {
+    0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61, 0xd6, 0x62,
+    0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b, 0x80, 0xf8, 0x43, 0x4a,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_256) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_384)
+{
+  const uint8_t out[] = {
+    0x0c, 0x63, 0xa7, 0x5b, 0x84, 0x5e, 0x4f, 0x7d, 0x01, 0x10, 0x7d, 0x85, 0x2e, 0x4c, 0x24, 0x85,
+    0xc5, 0x1a, 0x50, 0xaa, 0xaa, 0x94, 0xfc, 0x61, 0x99, 0x5e, 0x71, 0xbb, 0xee, 0x98, 0x3a, 0x2a,
+    0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb, 0x47, 0xfb, 0x6b, 0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_384) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_512)
+{
+  const uint8_t out[] = {
+    0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5, 0xc8, 0xb5, 0x67, 0xdc, 0x18, 0x5a, 0x75, 0x6e,
+    0x97, 0xc9, 0x82, 0x16, 0x4f, 0xe2, 0x58, 0x59, 0xe0, 0xd1, 0xdc, 0xc1, 0x47, 0x5c, 0x80, 0xa6,
+    0x15, 0xb2, 0x12, 0x3a, 0xf1, 0xf5, 0xf9, 0x4c, 0x11, 0xe3, 0xe9, 0x40, 0x2c, 0x3a, 0xc5, 0x58,
+    0xf5, 0x00, 0x19, 0x9d, 0x95, 0xb6, 0xd3, 0xe3, 0x01, 0x75, 0x85, 0x86, 0x28, 0x1d, 0xcd, 0x26,
+  };
+  OBufferStream os;
+  bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_512) >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x10101001L
+
+BOOST_AUTO_TEST_SUITE_END() // TestDigestFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/hex-decode.t.cpp b/tests/unit/security/transform/hex-decode.t.cpp
new file mode 100644
index 0000000..3c05ab4
--- /dev/null
+++ b/tests/unit/security/transform/hex-decode.t.cpp
@@ -0,0 +1,294 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/hex-decode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestHexDecode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  std::string in =
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  OBufferStream os;
+  bufferSource(in) >> hexDecode() >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(UpperCase)
+{
+  std::string in =
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  OBufferStream os;
+  bufferSource(in) >> hexDecode() >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(MixCase)
+{
+  std::string in =
+    "000102030405060708090a0b0c0d0e0f"
+    "101112131415161718191a1b1c1d1e1f"
+    "202122232425262728292a2b2c2d2e2f"
+    "303132333435363738393a3b3c3d3e3f"
+    "404142434445464748494a4b4c4d4e4f"
+    "505152535455565758595a5b5c5d5e5f"
+    "606162636465666768696a6b6c6d6e6f"
+    "707172737475767778797a7b7c7d7e7f"
+    "808182838485868788898a8b8c8d8e8f"
+    "909192939495969798999a9b9c9d9e9f"
+    "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
+    "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+    "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+    "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+    "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+    "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
+    "000102030405060708090A0B0C0D0E0F"
+    "101112131415161718191A1B1C1D1E1F"
+    "202122232425262728292A2B2C2D2E2F"
+    "303132333435363738393A3B3C3D3E3F"
+    "404142434445464748494A4B4C4D4E4F"
+    "505152535455565758595A5B5C5D5E5F"
+    "606162636465666768696A6B6C6D6E6F"
+    "707172737475767778797A7B7C7D7E7F"
+    "808182838485868788898A8B8C8D8E8F"
+    "909192939495969798999A9B9C9D9E9F"
+    "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
+    "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
+    "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
+    "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
+    "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
+    "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
+    "aaabacadaeafaAaBaCaD"
+    "babbbcbdbebfbAbBbCbD"
+    "cacbcccdcecfcAcBcCcD"
+    "dadbdcdddedfdAdBdCdD"
+    "eaebecedeeefeAeBeCeD"
+    "fafbfcfdfefffAfBfCfD"
+    "AaAbAcAdAeAfAAABACAD"
+    "BaBbBcBdBeBfBABBBCBD"
+    "CaCbCcCdCeCfCACBCCCD"
+    "DaDbDcDdDeDfDADBDCDD";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+    0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAA, 0xAB, 0xAC, 0xAD,
+    0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xBA, 0xBB, 0xBC, 0xBD,
+    0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xCA, 0xCB, 0xCC, 0xCD,
+    0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xDA, 0xDB, 0xDC, 0xDD,
+    0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xEA, 0xEB, 0xEC, 0xED,
+    0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFA, 0xFB, 0xFC, 0xFD,
+    0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAA, 0xAB, 0xAC, 0xAD,
+    0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xBA, 0xBB, 0xBC, 0xBD,
+    0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xCA, 0xCB, 0xCC, 0xCD,
+    0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xDA, 0xDB, 0xDC, 0xDD
+  };
+
+  OBufferStream os;
+  bufferSource(in) >> hexDecode() >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+  std::string in =
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+  uint8_t out[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  const uint8_t* input = reinterpret_cast<const uint8_t*>(in.data());
+  OBufferStream os;
+  StepSource source;
+  source >> hexDecode() >> streamSink(os);
+  source.write(input, 128);       // complete chunk
+  source.write(input + 128, 64);  // first half of a chunk
+  source.write(input + 192, 64);  // second half of a chunk
+  source.write(input + 256, 127); // odd number of byets
+  source.write(input + 383, 192); // one and half chunk
+  source.write(input + 575, 193); // remaining part
+  source.end();
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(OddByte)
+{
+  std::string in1 = "0001020304050";
+
+  OBufferStream os1;
+  BOOST_REQUIRE_THROW(bufferSource(in1) >> hexDecode() >> streamSink(os1), transform::Error);
+
+  std::string in2 = "0001020304xy";
+
+  OBufferStream os2;
+  BOOST_REQUIRE_THROW(bufferSource(in2) >> hexDecode() >> streamSink(os2), transform::Error);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+  OBufferStream os;
+  StepSource source;
+  source >> hexDecode() >> streamSink(os);
+  source.end();
+  BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestHexDecode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/hex-encode.t.cpp b/tests/unit/security/transform/hex-encode.t.cpp
new file mode 100644
index 0000000..26f6321
--- /dev/null
+++ b/tests/unit/security/transform/hex-encode.t.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/hex-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestHexEncode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  uint8_t in[128] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::string out =
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+  OBufferStream os;
+  bufferSource(in, sizeof(in)) >> hexEncode() >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(UpperCase)
+{
+  uint8_t in[128] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::string out =
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+    "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F";
+
+  OBufferStream os;
+  bufferSource(in, sizeof(in)) >> hexEncode(true) >> streamSink(os);
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+  uint8_t in[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+
+  std::string out =
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+    "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+  OBufferStream os;
+  StepSource source;
+  source >> hexEncode() >> streamSink(os);
+  source.write(in, 64);       // complete chunk
+  source.write(in + 64, 32);  // first half of a chunk
+  source.write(in + 96, 32);  // second half of a chunk
+  source.write(in + 128, 20); // front of a chunk
+  source.write(in + 148, 20); // middle of a chunk
+  source.write(in + 168, 24); // end of a chunk
+  source.write(in + 192, 63); // odd number of bytes
+  source.write(in + 255, 85); // one and half chunk
+  source.write(in + 340, 44); // remaining part
+  source.end();
+
+  ConstBufferPtr buf1 = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+  OBufferStream os;
+  StepSource source;
+  source >> hexEncode() >> streamSink(os);
+  source.end();
+  BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestHexEncode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/hmac-filter.t.cpp b/tests/unit/security/transform/hmac-filter.t.cpp
new file mode 100644
index 0000000..1ed5e25
--- /dev/null
+++ b/tests/unit/security/transform/hmac-filter.t.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/hmac-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestHmacFilter)
+
+static const uint8_t key[] = {
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+static const uint8_t data[] = {
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+static const uint8_t digest[] = {
+  0x9f, 0x3a, 0xa2, 0x88, 0x26, 0xb3, 0x74, 0x85,
+  0xca, 0x05, 0x01, 0x4d, 0x71, 0x42, 0xb3, 0xea,
+  0x3f, 0xfb, 0xda, 0x5a, 0x35, 0xbf, 0xd2, 0x0f,
+  0x2f, 0x9c, 0x8f, 0xcc, 0x6d, 0x30, 0x48, 0x54
+};
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  OBufferStream os;
+  bufferSource(data, sizeof(data)) >> hmacFilter(DigestAlgorithm::SHA256, key, sizeof(key)) >> streamSink(os);
+
+  ConstBufferPtr buf = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(digest, digest + sizeof(digest), buf->begin(), buf->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+  OBufferStream os;
+  StepSource source;
+  source >> hmacFilter(DigestAlgorithm::SHA256, key, sizeof(key)) >> streamSink(os);
+  source.write(data, 1);
+  source.write(data + 1, 2);
+  source.write(data + 3, 3);
+  source.write(data + 6, 4);
+  source.write(data + 10, 5);
+  source.write(data + 15, 1);
+  source.end();
+
+  ConstBufferPtr buf = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(digest, digest + sizeof(digest), buf->begin(), buf->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+  const uint8_t digest[] = {
+    0x07, 0xEF, 0xF8, 0xB3, 0x26, 0xB7, 0x79, 0x8C,
+    0x9C, 0xCF, 0xCB, 0xDB, 0xE5, 0x79, 0x48, 0x9A,
+    0xC7, 0x85, 0xA7, 0x99, 0x5A, 0x04, 0x61, 0x8B,
+    0x1A, 0x28, 0x13, 0xC2, 0x67, 0x44, 0x77, 0x7D
+  };
+
+  OBufferStream os;
+  StepSource source;
+  source >> hmacFilter(DigestAlgorithm::SHA256, key, sizeof(key)) >> streamSink(os);
+  source.end();
+
+  ConstBufferPtr buf = os.buf();
+  BOOST_CHECK_EQUAL_COLLECTIONS(digest, digest + sizeof(digest), buf->begin(), buf->end());
+}
+
+BOOST_AUTO_TEST_CASE(InvalidAlgorithm)
+{
+  BOOST_CHECK_THROW(HmacFilter(DigestAlgorithm::NONE, key, sizeof(key)), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestHmacFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/private-key.t.cpp b/tests/unit/security/transform/private-key.t.cpp
new file mode 100644
index 0000000..ee956f9
--- /dev/null
+++ b/tests/unit/security/transform/private-key.t.cpp
@@ -0,0 +1,401 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/private-key.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/key-params.hpp"
+#include "security/transform.hpp"
+
+#include "boost-test.hpp"
+#include <boost/mpl/vector.hpp>
+
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestPrivateKey)
+
+struct RsaKeyTestData
+{
+  const std::string privateKeyPkcs1 =
+      "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
+      "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
+      "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
+      "c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9gZwIL5PuE9BiO6I39cL9z7EK1SfZh\n"
+      "OWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA9rH58ynaAix0tcR/nBMRLUX+e3rU\n"
+      "RHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0ywIDAQABAoIBADQkckOIl4IZMUTn\n"
+      "W8LFv6xOdkJwMKC8G6bsPRFbyY+HvC2TLt7epSvfS+f4AcYWaOPcDu2E49vt2sNr\n"
+      "cASly8hgwiRRAB3dHH9vcsboiTo8bi2RFvMqvjv9w3tK2yMxVDtmZamzrrnaV3YV\n"
+      "Q+5nyKo2F/PMDjQ4eUAKDOzjhBuKHsZBTFnA1MFNI+UKj5X4Yp64DFmKlxTX/U2b\n"
+      "wzVywo5hzx2Uhw51jmoLls4YUvMJXD0wW5ZtYRuPogXvXb/of9ef/20/wU11WFKg\n"
+      "Xb4gfR8zUXaXS1sXcnVm3+24vIs9dApUwykuoyjOqxWqcHRec2QT2FxVGkFEraze\n"
+      "CPa4rMECgYEA5Y8CywomIcTgerFGFCeMHJr8nQGqY2V/owFb3k9maczPnC9p4a9R\n"
+      "c5szLxA9FMYFxurQZMBWSEG2JS1HR2mnjigx8UKjYML/A+rvvjZOMe4M6Sy2ggh4\n"
+      "SkLZKpWTzjTe07ByM/j5v/SjNZhWAG7sw4/LmPGRQkwJv+KZhGojuOkCgYEA2cOF\n"
+      "T6cJRv6kvzTz9S0COZOVm+euJh/BXp7oAsAmbNfOpckPMzqHXy8/wpdKl6AAcB57\n"
+      "OuztlNfV1D7qvbz7JuRlYwQ0cEfBgbZPcz1p18HHDXhwn57ZPb8G33Yh9Omg0HNA\n"
+      "Imb4LsVuSqxA6NwSj7cpRekgTedrhLFPJ+Ydb5MCgYEAsM3Q7OjILcIg0t6uht9e\n"
+      "vrlwTsz1mtCV2co2I6crzdj9HeI2vqf1KAElDt6G7PUHhglcr/yjd8uEqmWRPKNX\n"
+      "ddnnfVZB10jYeP/93pac6z/Zmc3iU4yKeUe7U10ZFf0KkiiYDQd59CpLef/2XScS\n"
+      "HB0oRofnxRQjfjLc4muNT+ECgYEAlcDk06MOOTly+F8lCc1bA1dgAmgwFd2usDBd\n"
+      "Y07a3e0HGnGLN3Kfl7C5i0tZq64HvxLnMd2vgLVxQlXGPpdQrC1TH+XLXg+qnlZO\n"
+      "ivSH7i0/gx75bHvj75eH1XK65V8pDVDEoSPottllAIs21CxLw3N1ObOZWJm2EfmR\n"
+      "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
+      "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
+      "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
+  const std::string privateKeyPkcs8 =
+      "MIIFCzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIOKYJXvB6p8kCAggA\n"
+      "MBEGBSsOAwIHBAiQgMK8kQXTyASCBMjeNiKYYw5/yHgs9BfSGrpqvV0LkkgMQNUW\n"
+      "R4ZY8fuNjZynd+PxDuw2pyrv1Yv3jc+tupwUehZEzYOnGd53wQAuLO+Z0TBgRFN7\n"
+      "Lhk+AxlT7hu0xaB3ZpJ/uvWpgEJHsq/aB/GYgyzXcQo2AiqzERVpMCWJVmE1L977\n"
+      "CHwJmLm5mxclVLYp1UK5lkIBFu/M4nPavmNmYNUU1LOrXRo56TlJ2kUp8gQyQI1P\n"
+      "VPxi4chmlsr/OnQ2d1eZN+euFm0CS+yP+LFgI9ZqdyH1w+J43SXdHDzauVcZp7oa\n"
+      "Kw24OrhHfolLAnQIECXEJYeT7tZmhC4O9V6B18PFVyxWnEU4eFNpFE8kYSmm8Um2\n"
+      "buvDKI71q43hm23moYT9uIM1f4M8UkoOliJGrlf4xgEcmDuokEX01PdOq1gc4nvG\n"
+      "0DCwDI9cOsyn8cxhk9UVtFgzuG/seuznxIv1F5H0hzYOyloStXxRisJES0kgByBt\n"
+      "FFTfyoFKRrmCjRIygwVKUSkSDR0DlQS5ZLvQyIswnSQFwxAHqfvoSL4dB9UAIAQ+\n"
+      "ALVF1maaHgptbL6Ifqf0GFCv0hdNCVNDNCdy8R+S6nEYE+YdYSIdT1L88KD5PjU3\n"
+      "YY/CMnxhTncMaT4acPO1UUYuSGRZ/JL6E0ihoqIU+bqUgLSHNzhPySPfN9uqN61Y\n"
+      "HFBtxeEPWKU0f/JPkRBMmZdMI1/OVmA3QHSRBydI+CQN8no2gZRFoVbHTkG8IMpE\n"
+      "1fiDJpwFkpzIv/JPiTSE7DeBH5NJk1bgu7TcuZfa4unyAqss0UuLnXzS06TppkUj\n"
+      "QGft0g8VPW56eli6B4xrSzzuvAdbrxsVfxdmtHPyYxLb3/UG1g4x/H/yULhx7x9P\n"
+      "iI6cw6JUE+8bwJV2ZIlHXXHO+wUp/gCFJ6MHo9wkR1QvnHP2ClJAzBm9OvYnUx2Y\n"
+      "SX0HxEowW8BkhxOF184LEmxeua0yyZUqCdrYmErp7x9EY/LhD1zBwH8OGRa0qzmR\n"
+      "VKxAPKihkb9OgxcUKbvKePx3k2cQ7fbCUspGPm4Kn1zwMgRAZ4fz/o8Lnwc8MSY3\n"
+      "lPWnmLTFu420SRH2g9N0o/r195hiZ5cc+KfF4pwZWKbEbKFk/UfXA9vmOi7BBtDJ\n"
+      "RWshOINhzMU6Ij3KuaEpHni1HoHjw0SQ97ow2x/aB8k2QC28tbsa49lD2KKJku6b\n"
+      "2Or89adwFKqMgS2IXfXMXs/iG5EFLYN6r8e40Dn5f1vJfRLJl03XByIfT2n92pw3\n"
+      "fP7muOIKLUsEKjOrmn94NwMlfeW13oQHEH2KjPOWFS/tyJHDdVU+of4COH5yg59a\n"
+      "TZqFkOTGeliE1O+6sfF9fRuVxFUF3D8Hpr0JIjdc6+3RgIlGsXc8BwiSjDSI2XW+\n"
+      "vo75/2zPU9t8OeXEIJk2CQGyqLwUJ6dyi/yDRrvZAgjrUvbpcxydnBAHrLbLUGXJ\n"
+      "aEHH2tjEtnTqVyTchr1yHoupcFOCkA0dAA66XqwcssQxJiMGrWTpCbgd9mrTXQaZ\n"
+      "U7afFN1jpO78tgBQUUpImXdHLLsqdN5tefqjileZGZ9x3/C6TNAfDwYJdsicNNn5\n"
+      "y+JVsbltfLWlJxb9teb3dtQiFlJ7ofprLJnJVqI/Js8lozY+KaxV2vtbZkcD4dM=\n";
+  const std::string publicKeyPkcs8 =
+      "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+      "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+      "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+      "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+      "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+      "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+      "ywIDAQAB\n";
+};
+
+struct EcKeyTestData
+{
+  const std::string privateKeyPkcs1 =
+      "MIIBaAIBAQQgRxwcbzK9RV6AHYFsDcykI86o3M/a1KlJn0z8PcLMBZOggfowgfcC\n"
+      "AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////\n"
+      "MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr\n"
+      "vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE\n"
+      "axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W\n"
+      "K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8\n"
+      "YyVRAgEBoUQDQgAEaG4WJuDAt0QkEM4t29KDUdzkQlMPGrqWzkWhgt9OGnwc6O7A\n"
+      "ZLPSrDyhwyrKS7XLRXml5DisQ93RvByll32y8A==\n";
+  const std::string privateKeyPkcs8 =
+      "MIIBwzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIVHkBzLGtDvICAggA\n"
+      "MBEGBSsOAwIHBAhk6g9eI3toNwSCAYDd+LWPDBTrKV7vUyxTvDbpUd0eXfh73DKA\n"
+      "MHkdHuVmhpmpBbsF9XvaFuL8J/1xi1Yl2XGw8j3WyrprD2YEhl/+zKjNbdTDJmNO\n"
+      "SlomuwWb5AVCJ9reT94zIXKCnexUcyBFS7ep+P4dwuef0VjzprjfmnAZHrP+u594\n"
+      "ELHpKwi0ZpQLtcJjjud13bn43vbXb+aU7jmPV5lU2XP8TxaQJiYIibNEh1Y3TZGr\n"
+      "akJormYvhaYbiZkKLHQ9AvQMEjhoIW5WCB3q+tKZUKTzcQpjNnf9FOTeKN3jk3Kd\n"
+      "2OmibPZcbMJdgCD/nRVn1cBo7Hjn3IMjgtszQHtEUphOQiAkOJUnKmy9MTYqtcNN\n"
+      "6cuFItbu4QvbVwailgdUjOYwIJCmIxExlPV0ohS24pFGsO03Yn7W8rBB9VWENYmG\n"
+      "HkZIbGsHv7O9Wy7fv+FJgZkjeti0807IsNXSJl8LUK0ZIhAR7OU8uONWMsbHdQnk\n"
+      "q1HB1ZKa52ugACl7g/DF9b7CoSAjFeE=\n";
+  const std::string publicKeyPkcs8 =
+      "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+      "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+      "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+      "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+      "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+      "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+      "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+};
+
+using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
+
+static void
+checkPkcs8Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
+{
+  PrivateKey sKey;
+  sKey.loadPkcs8(encoding->data(), encoding->size(), password.c_str(), password.size());
+  OBufferStream os;
+  sKey.savePkcs1(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(pkcs1->begin(), pkcs1->end(),
+                                os.buf()->begin(), os.buf()->end());
+}
+
+static void
+checkPkcs8Base64Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
+{
+  OBufferStream os;
+  bufferSource(*encoding) >> base64Decode() >> streamSink(os);
+  checkPkcs8Encoding(os.buf(), password, pkcs1);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(SaveLoad, T, KeyTestDataSets)
+{
+  T dataSet;
+
+  const uint8_t* sKeyPkcs1Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str());
+  size_t sKeyPkcs1Base64Len = dataSet.privateKeyPkcs1.size();
+  OBufferStream os;
+  bufferSource(sKeyPkcs1Base64, sKeyPkcs1Base64Len) >> base64Decode() >> streamSink(os);
+  ConstBufferPtr sKeyPkcs1Buf = os.buf();
+  const uint8_t* sKeyPkcs1 = sKeyPkcs1Buf->data();
+  size_t sKeyPkcs1Len = sKeyPkcs1Buf->size();
+
+  // load key in base64-encoded pkcs1 format
+  PrivateKey sKey;
+  BOOST_CHECK_NO_THROW(sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len));
+
+  std::stringstream ss2(dataSet.privateKeyPkcs1);
+  PrivateKey sKey2;
+  BOOST_CHECK_NO_THROW(sKey2.loadPkcs1Base64(ss2));
+
+  // load key in pkcs1 format
+  PrivateKey sKey3;
+  BOOST_CHECK_NO_THROW(sKey3.loadPkcs1(sKeyPkcs1, sKeyPkcs1Len));
+
+  std::stringstream ss4;
+  ss4.write(reinterpret_cast<const char*>(sKeyPkcs1), sKeyPkcs1Len);
+  PrivateKey sKey4;
+  BOOST_CHECK_NO_THROW(sKey4.loadPkcs1(ss4));
+
+  // save key in base64-encoded pkcs1 format
+  OBufferStream os2;
+  BOOST_REQUIRE_NO_THROW(sKey.savePkcs1Base64(os2));
+  BOOST_CHECK_EQUAL_COLLECTIONS(sKeyPkcs1Base64, sKeyPkcs1Base64 + sKeyPkcs1Base64Len,
+                                os2.buf()->begin(), os2.buf()->end());
+
+  // save key in pkcs1 format
+  OBufferStream os3;
+  BOOST_REQUIRE_NO_THROW(sKey.savePkcs1(os3));
+  BOOST_CHECK_EQUAL_COLLECTIONS(sKeyPkcs1, sKeyPkcs1 + sKeyPkcs1Len,
+                                os3.buf()->begin(), os3.buf()->end());
+
+  const uint8_t* sKeyPkcs8Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs8.c_str());
+  size_t sKeyPkcs8Base64Len = dataSet.privateKeyPkcs8.size();
+  OBufferStream os4;
+  bufferSource(sKeyPkcs8Base64, sKeyPkcs8Base64Len) >> base64Decode() >> streamSink(os4);
+  const uint8_t* sKeyPkcs8 = os4.buf()->data();
+  size_t sKeyPkcs8Len = os4.buf()->size();
+
+  std::string password("password");
+  std::string wrongpw("wrongpw");
+  auto pwCallback = [&password] (char* buf, size_t size, int) -> int {
+    BOOST_REQUIRE_LE(password.size(), size);
+    std::copy(password.begin(), password.end(), buf);
+    return static_cast<int>(password.size());
+  };
+
+  // load key in base64-encoded pkcs8 format
+  PrivateKey sKey5;
+  BOOST_CHECK_NO_THROW(sKey5.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
+                                             password.c_str(), password.size()));
+
+  PrivateKey sKey6;
+  BOOST_CHECK_NO_THROW(sKey6.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, pwCallback));
+
+  std::stringstream ss7(dataSet.privateKeyPkcs8);
+  PrivateKey sKey7;
+  BOOST_CHECK_NO_THROW(sKey7.loadPkcs8Base64(ss7, password.c_str(), password.size()));
+
+  std::stringstream ss8(dataSet.privateKeyPkcs8);
+  PrivateKey sKey8;
+  BOOST_CHECK_NO_THROW(sKey8.loadPkcs8Base64(ss8, pwCallback));
+
+  // load key in pkcs8 format
+  PrivateKey sKey9;
+  BOOST_CHECK_NO_THROW(sKey9.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, password.c_str(), password.size()));
+
+  PrivateKey sKey10;
+  BOOST_CHECK_NO_THROW(sKey10.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, pwCallback));
+
+  std::stringstream ss11;
+  ss11.write(reinterpret_cast<const char*>(sKeyPkcs8), sKeyPkcs8Len);
+  PrivateKey sKey11;
+  BOOST_CHECK_NO_THROW(sKey11.loadPkcs8(ss11, password.c_str(), password.size()));
+
+  std::stringstream ss12;
+  ss12.write(reinterpret_cast<const char*>(sKeyPkcs8), sKeyPkcs8Len);
+  PrivateKey sKey12;
+  BOOST_CHECK_NO_THROW(sKey12.loadPkcs8(ss12, pwCallback));
+
+  // load key using wrong password, Error is expected
+  PrivateKey sKey13;
+  BOOST_CHECK_THROW(sKey13.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, wrongpw.c_str(), wrongpw.size()),
+                    PrivateKey::Error);
+
+  // save key in base64-encoded pkcs8 format
+  OBufferStream os14;
+  BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os14, password.c_str(), password.size()));
+  checkPkcs8Base64Encoding(os14.buf(), password, sKeyPkcs1Buf);
+
+  OBufferStream os15;
+  BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os15, pwCallback));
+  checkPkcs8Base64Encoding(os15.buf(), password, sKeyPkcs1Buf);
+
+  // save key in pkcs8 format
+  OBufferStream os16;
+  BOOST_REQUIRE_NO_THROW(sKey.savePkcs8(os16, password.c_str(), password.size()));
+  checkPkcs8Encoding(os16.buf(), password, sKeyPkcs1Buf);
+
+  OBufferStream os17;
+  BOOST_REQUIRE_NO_THROW(sKey.savePkcs8(os17, pwCallback));
+  checkPkcs8Encoding(os17.buf(), password, sKeyPkcs1Buf);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(DerivePublicKey, T, KeyTestDataSets)
+{
+  T dataSet;
+
+  const uint8_t* sKeyPkcs1Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str());
+  size_t sKeyPkcs1Base64Len = dataSet.privateKeyPkcs1.size();
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len);
+
+  // derive public key and compare
+  ConstBufferPtr pKeyBits = sKey.derivePublicKey();
+  OBufferStream os;
+  bufferSource(dataSet.publicKeyPkcs8) >> base64Decode() >> streamSink(os);
+  BOOST_CHECK_EQUAL_COLLECTIONS(pKeyBits->begin(), pKeyBits->end(),
+                                os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(RsaDecryption)
+{
+  RsaKeyTestData dataSet;
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+                       dataSet.privateKeyPkcs1.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::RSA);
+
+  const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+  const std::string cipherTextBase64 =
+    "i2XNpZ2JbLa4JmBTdDrGmsd4/0C+p+BSCpW3MuPBNe5uChQ0eRO1dvjTnEqwSECY\n"
+    "38en9JZwcyb0It/TSFNXHlq+Z1ZpffnjIJxQR9HcgwvwQJh6WRH0vu38tvGkGuNv\n"
+    "60Rdn85hqSy1CikmXCeWXL9yCqeqcP21R94G/T3FuA+c1FtFko8KOzCwvrTXMO6n\n"
+    "5PNsqlLXabSGr+jz4EwOsSCgPkiDf9U6tXoSPRA2/YvqFQdaiUXIVlomESvaqqZ8\n"
+    "FxPs2BON0lobM8gT+xdzbRKofp+rNjNK+5uWyeOnXJwzCszh17cdJl2BH1dZwaVD\n"
+    "PmTiSdeDQXZ94U5boDQ4Aw==\n";
+  OBufferStream os;
+  bufferSource(cipherTextBase64) >> base64Decode() >> streamSink(os);
+
+  auto decrypted = sKey.decrypt(os.buf()->data(), os.buf()->size());
+  BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
+                                decrypted->begin(), decrypted->end());
+}
+
+BOOST_AUTO_TEST_CASE(RsaEncryptDecrypt)
+{
+  RsaKeyTestData dataSet;
+
+  PublicKey pKey;
+  pKey.loadPkcs8Base64(reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str()),
+                       dataSet.publicKeyPkcs8.size());
+  BOOST_CHECK_EQUAL(pKey.getKeyType(), KeyType::RSA);
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+                       dataSet.privateKeyPkcs1.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::RSA);
+
+  const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+  auto cipherText = pKey.encrypt(plainText, sizeof(plainText));
+  auto decrypted = sKey.decrypt(cipherText->data(), cipherText->size());
+  BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
+                                decrypted->begin(), decrypted->end());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedEcDecryption)
+{
+  EcKeyTestData dataSet;
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+                       dataSet.privateKeyPkcs1.size());
+  BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::EC);
+
+  OBufferStream os;
+  bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
+
+  BOOST_CHECK_THROW(sKey.decrypt(os.buf()->data(), os.buf()->size()), PrivateKey::Error);
+}
+
+using KeyParams = boost::mpl::vector<RsaKeyParams, EcKeyParams>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(GenerateKey, T, KeyParams)
+{
+  unique_ptr<PrivateKey> sKey = generatePrivateKey(T());
+  PublicKey pKey;
+  ConstBufferPtr pKeyBits = sKey->derivePublicKey();
+  pKey.loadPkcs8(pKeyBits->data(), pKeyBits->size());
+
+  const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+  OBufferStream os;
+  BOOST_REQUIRE_NO_THROW(bufferSource(data, sizeof(data)) >>
+                         signerFilter(DigestAlgorithm::SHA256, *sKey) >>
+                         streamSink(os));
+
+  ConstBufferPtr sig = os.buf();
+  bool result = false;
+  BOOST_REQUIRE_NO_THROW(bufferSource(data, sizeof(data)) >>
+                         verifierFilter(DigestAlgorithm::SHA256, pKey, sig->data(), sig->size()) >>
+                         boolSink(result));
+  BOOST_CHECK(result);
+
+  unique_ptr<PrivateKey> sKey2 = generatePrivateKey(T());
+
+  OBufferStream os1;
+  sKey->savePkcs1(os1);
+  ConstBufferPtr key1Pkcs1 = os1.buf();
+
+  OBufferStream os2;
+  sKey2->savePkcs1(os2);
+  ConstBufferPtr key2Pkcs1 = os2.buf();
+
+  BOOST_CHECK(*key1Pkcs1 != *key2Pkcs1);
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedKeyType)
+{
+  BOOST_CHECK_THROW(generatePrivateKey(AesKeyParams()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPrivateKey
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/public-key.t.cpp b/tests/unit/security/transform/public-key.t.cpp
new file mode 100644
index 0000000..f9d3304
--- /dev/null
+++ b/tests/unit/security/transform/public-key.t.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/public-key.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform.hpp"
+
+#include "boost-test.hpp"
+#include <boost/mpl/vector.hpp>
+
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestPublicKey)
+
+struct RsaKeyTestData
+{
+  const std::string publicKeyPkcs8 =
+      "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+      "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+      "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+      "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+      "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+      "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+      "ywIDAQAB\n";
+};
+
+struct EcKeyTestData
+{
+  const std::string publicKeyPkcs8 =
+      "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+      "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+      "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+      "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+      "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+      "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+      "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+};
+
+using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(SaveLoad, T, KeyTestDataSets)
+{
+  T dataSet;
+
+  const uint8_t* pKeyPkcs8Base64 = reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str());
+  size_t pKeyPkcs8Base64Len = dataSet.publicKeyPkcs8.size();
+  OBufferStream os;
+  bufferSource(pKeyPkcs8Base64, pKeyPkcs8Base64Len) >> base64Decode() >> streamSink(os);
+  ConstBufferPtr pKeyPkcs8Buf = os.buf();
+  const uint8_t* pKeyPkcs8 = pKeyPkcs8Buf->data();
+  size_t pKeyPkcs8Len = pKeyPkcs8Buf->size();
+
+  PublicKey pKey1;
+  BOOST_CHECK_NO_THROW(pKey1.loadPkcs8Base64(pKeyPkcs8Base64, pKeyPkcs8Base64Len));
+
+  std::stringstream ss2(dataSet.publicKeyPkcs8);
+  PublicKey pKey2;
+  BOOST_CHECK_NO_THROW(pKey2.loadPkcs8Base64(ss2));
+
+  PublicKey pKey3;
+  BOOST_CHECK_NO_THROW(pKey3.loadPkcs8(pKeyPkcs8, pKeyPkcs8Len));
+
+  std::stringstream ss4;
+  ss4.write(reinterpret_cast<const char*>(pKeyPkcs8), pKeyPkcs8Len);
+  PublicKey pKey4;
+  BOOST_CHECK_NO_THROW(pKey4.loadPkcs8(ss4));
+
+  OBufferStream os5;
+  BOOST_REQUIRE_NO_THROW(pKey1.savePkcs8Base64(os5));
+  BOOST_CHECK_EQUAL_COLLECTIONS(pKeyPkcs8Base64, pKeyPkcs8Base64 + pKeyPkcs8Base64Len,
+                                os5.buf()->begin(), os5.buf()->end());
+
+  OBufferStream os6;
+  BOOST_REQUIRE_NO_THROW(pKey1.savePkcs8(os6));
+  BOOST_CHECK_EQUAL_COLLECTIONS(pKeyPkcs8, pKeyPkcs8 + pKeyPkcs8Len,
+                                os6.buf()->begin(), os6.buf()->end());
+}
+
+// NOTE: We cannot test RSA encryption by comparing the computed ciphertext to
+//       a known-good one, because OAEP padding is randomized and would produce
+//       different results every time. An encrypt/decrypt round-trip test is
+//       performed in private-key.t.cpp
+
+BOOST_AUTO_TEST_CASE(UnsupportedEcEncryption)
+{
+  EcKeyTestData dataSet;
+
+  PublicKey pKey;
+  pKey.loadPkcs8Base64(reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str()),
+                       dataSet.publicKeyPkcs8.size());
+  BOOST_CHECK_EQUAL(pKey.getKeyType(), KeyType::EC);
+
+  OBufferStream os;
+  bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
+
+  BOOST_CHECK_THROW(pKey.encrypt(os.buf()->data(), os.buf()->size()), PublicKey::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPublicKey
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/signer-filter.t.cpp b/tests/unit/security/transform/signer-filter.t.cpp
new file mode 100644
index 0000000..51a98f0
--- /dev/null
+++ b/tests/unit/security/transform/signer-filter.t.cpp
@@ -0,0 +1,146 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/signer-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/base64-decode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "security/verification-helpers.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestSignerFilter)
+
+BOOST_AUTO_TEST_CASE(Rsa)
+{
+  const std::string publicKeyPkcs8 =
+    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+    "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+    "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+    "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+    "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+    "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+    "ywIDAQAB\n";
+  const std::string privateKeyPkcs1 =
+    "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
+    "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
+    "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
+    "c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9gZwIL5PuE9BiO6I39cL9z7EK1SfZh\n"
+    "OWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA9rH58ynaAix0tcR/nBMRLUX+e3rU\n"
+    "RHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0ywIDAQABAoIBADQkckOIl4IZMUTn\n"
+    "W8LFv6xOdkJwMKC8G6bsPRFbyY+HvC2TLt7epSvfS+f4AcYWaOPcDu2E49vt2sNr\n"
+    "cASly8hgwiRRAB3dHH9vcsboiTo8bi2RFvMqvjv9w3tK2yMxVDtmZamzrrnaV3YV\n"
+    "Q+5nyKo2F/PMDjQ4eUAKDOzjhBuKHsZBTFnA1MFNI+UKj5X4Yp64DFmKlxTX/U2b\n"
+    "wzVywo5hzx2Uhw51jmoLls4YUvMJXD0wW5ZtYRuPogXvXb/of9ef/20/wU11WFKg\n"
+    "Xb4gfR8zUXaXS1sXcnVm3+24vIs9dApUwykuoyjOqxWqcHRec2QT2FxVGkFEraze\n"
+    "CPa4rMECgYEA5Y8CywomIcTgerFGFCeMHJr8nQGqY2V/owFb3k9maczPnC9p4a9R\n"
+    "c5szLxA9FMYFxurQZMBWSEG2JS1HR2mnjigx8UKjYML/A+rvvjZOMe4M6Sy2ggh4\n"
+    "SkLZKpWTzjTe07ByM/j5v/SjNZhWAG7sw4/LmPGRQkwJv+KZhGojuOkCgYEA2cOF\n"
+    "T6cJRv6kvzTz9S0COZOVm+euJh/BXp7oAsAmbNfOpckPMzqHXy8/wpdKl6AAcB57\n"
+    "OuztlNfV1D7qvbz7JuRlYwQ0cEfBgbZPcz1p18HHDXhwn57ZPb8G33Yh9Omg0HNA\n"
+    "Imb4LsVuSqxA6NwSj7cpRekgTedrhLFPJ+Ydb5MCgYEAsM3Q7OjILcIg0t6uht9e\n"
+    "vrlwTsz1mtCV2co2I6crzdj9HeI2vqf1KAElDt6G7PUHhglcr/yjd8uEqmWRPKNX\n"
+    "ddnnfVZB10jYeP/93pac6z/Zmc3iU4yKeUe7U10ZFf0KkiiYDQd59CpLef/2XScS\n"
+    "HB0oRofnxRQjfjLc4muNT+ECgYEAlcDk06MOOTly+F8lCc1bA1dgAmgwFd2usDBd\n"
+    "Y07a3e0HGnGLN3Kfl7C5i0tZq64HvxLnMd2vgLVxQlXGPpdQrC1TH+XLXg+qnlZO\n"
+    "ivSH7i0/gx75bHvj75eH1XK65V8pDVDEoSPottllAIs21CxLw3N1ObOZWJm2EfmR\n"
+    "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
+    "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
+    "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
+  const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+  OBufferStream os1;
+  bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+  auto pubKey = os1.buf();
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+  BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::NONE, sKey), Error);
+
+  OBufferStream os2;
+  bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+  auto sig = os2.buf();
+
+  BOOST_CHECK(verifySignature(data, sizeof(data), sig->data(), sig->size(), pubKey->data(), pubKey->size()));
+}
+
+BOOST_AUTO_TEST_CASE(Ecdsa)
+{
+  const std::string privateKeyPkcs1 =
+    "MIIBaAIBAQQgRxwcbzK9RV6AHYFsDcykI86o3M/a1KlJn0z8PcLMBZOggfowgfcC\n"
+    "AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////\n"
+    "MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr\n"
+    "vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE\n"
+    "axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W\n"
+    "K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8\n"
+    "YyVRAgEBoUQDQgAEaG4WJuDAt0QkEM4t29KDUdzkQlMPGrqWzkWhgt9OGnwc6O7A\n"
+    "ZLPSrDyhwyrKS7XLRXml5DisQ93RvByll32y8A==\n";
+  const std::string publicKeyPkcs8 =
+    "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+    "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+    "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+    "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+    "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+    "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+    "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+  const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+  OBufferStream os1;
+  bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+  auto pubKey = os1.buf();
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+  BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::NONE, sKey), Error);
+
+  OBufferStream os2;
+  bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+  auto sig = os2.buf();
+
+  BOOST_CHECK(verifySignature(data, sizeof(data), sig->data(), sig->size(), pubKey->data(), pubKey->size()));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidKey)
+{
+  PrivateKey sKey;
+  BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::SHA256, sKey), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignerFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/step-source.t.cpp b/tests/unit/security/transform/step-source.t.cpp
new file mode 100644
index 0000000..43c6655
--- /dev/null
+++ b/tests/unit/security/transform/step-source.t.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStepSource)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  const std::string input =
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567";
+  const uint8_t* buf = reinterpret_cast<const uint8_t*>(input.data());
+
+  std::ostringstream os;
+  StepSource ss;
+  ss >> streamSink(os);
+  BOOST_CHECK_EQUAL(ss.write(buf, 320), 320);
+  BOOST_CHECK_EQUAL(ss.write(buf + 320, 320), 320);
+  BOOST_CHECK_EQUAL(ss.write(buf + 640, 320), 320);
+  BOOST_CHECK_EQUAL(ss.write(buf + 960, 320), 320);
+  ss.end();
+  BOOST_CHECK_THROW(ss.write(buf + 960, 320), transform::Error);
+  BOOST_CHECK_EQUAL(os.str(), input);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+  std::ostringstream os;
+  StepSource ss;
+  ss >> streamSink(os);
+  ss.end();
+  BOOST_CHECK_EQUAL(os.str(), "");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStepSource
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/stream-sink.t.cpp b/tests/unit/security/transform/stream-sink.t.cpp
new file mode 100644
index 0000000..753ea35
--- /dev/null
+++ b/tests/unit/security/transform/stream-sink.t.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStreamSink)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  uint8_t in[] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x20, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+  };
+  std::ostringstream os;
+  StreamSink sink(os);
+  BOOST_CHECK_EQUAL(sink.write(in, 4), 4);
+  BOOST_CHECK_EQUAL(sink.write(in + 4, 4), 4);
+  BOOST_CHECK_EQUAL(sink.write(in + 8, 4), 4);
+  BOOST_CHECK_EQUAL(sink.write(in + 12, 4), 4);
+  sink.end();
+  std::string out = os.str();
+  BOOST_CHECK_EQUAL_COLLECTIONS(in, in + sizeof(in), out.begin(), out.end());
+  BOOST_CHECK_THROW(sink.write(in + 8, 8), transform::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStreamSink
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/stream-source.t.cpp b/tests/unit/security/transform/stream-source.t.cpp
new file mode 100644
index 0000000..ae385b1
--- /dev/null
+++ b/tests/unit/security/transform/stream-source.t.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/stream-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+#include <boost/mpl/integral_c.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStreamSource)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  std::string input =
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "01234567012345670123456701234567 1234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567"
+    "0123456701234567012345670123456701234567012345670123456701234567";
+
+  std::stringstream is(input);
+  std::stringstream os;
+  streamSource(is) >> streamSink(os);
+  std::string output = os.str();
+
+  BOOST_CHECK_EQUAL(input, output);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyStream)
+{
+  std::stringstream is;
+  std::stringstream os;
+  streamSource(is) >> streamSink(os);
+
+  BOOST_CHECK_EQUAL(os.str(), "");
+}
+
+typedef boost::mpl::vector<
+  boost::mpl::integral_c<std::ios_base::iostate, std::ios_base::badbit>,
+  boost::mpl::integral_c<std::ios_base::iostate, std::ios_base::failbit>
+> BadBits;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(BadStream, BadBit, BadBits)
+{
+  std::stringstream is;
+  is.setstate(BadBit::value);
+  std::stringstream os;
+  BOOST_CHECK_THROW(streamSource(is) >> streamSink(os), transform::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStreamSource
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/strip-space.t.cpp b/tests/unit/security/transform/strip-space.t.cpp
new file mode 100644
index 0000000..db8fa84
--- /dev/null
+++ b/tests/unit/security/transform/strip-space.t.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/strip-space.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStripSpace)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  const char* input = R"STR(
+    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+    incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
+    exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+    irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
+    deserunt mollit anim id est laborum.
+  )STR";
+  size_t inputLen = strlen(input);
+
+  OBufferStream os;
+  StepSource source;
+  source >> stripSpace() >> streamSink(os);
+
+  for (size_t offset = 0; offset < inputLen; offset += 40) {
+    source.write(reinterpret_cast<const uint8_t*>(input + offset),
+                 std::min<size_t>(40, inputLen - offset));
+  }
+  source.end();
+
+  std::string expected(
+    "Loremipsumdolorsitamet,consecteturadipiscingelit,seddoeiusmodtemporincididuntutl"
+    "aboreetdoloremagnaaliqua.Utenimadminimveniam,quisnostrudexercitationullamcolabor"
+    "isnisiutaliquipexeacommodoconsequat.Duisauteiruredolorinreprehenderitinvoluptate"
+    "velitessecillumdoloreeufugiatnullapariatur.Excepteursintoccaecatcupidatatnonproi"
+    "dent,suntinculpaquiofficiadeseruntmollitanimidestlaborum.");
+  ConstBufferPtr buf = os.buf();
+  BOOST_CHECK_EQUAL(std::string(buf->get<char>(), buf->size()), expected);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStripSpace
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/verifier-filter.t.cpp b/tests/unit/security/transform/verifier-filter.t.cpp
new file mode 100644
index 0000000..1b74690
--- /dev/null
+++ b/tests/unit/security/transform/verifier-filter.t.cpp
@@ -0,0 +1,165 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/transform/verifier-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/base64-decode.hpp"
+#include "security/transform/bool-sink.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/public-key.hpp"
+#include "security/transform/signer-filter.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestVerifierFilter)
+
+BOOST_AUTO_TEST_CASE(Rsa)
+{
+  const std::string publicKeyPkcs8 =
+    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+    "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+    "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+    "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+    "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+    "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+    "ywIDAQAB\n";
+  const std::string privateKeyPkcs1 =
+    "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDRYzX9aEDHIu0\n"
+    "SqyIAmBYNla7ORil5hV155lypFnPN9GAFBOw2jNLm3gefBNmHDBdsfuTdA3SRFNX\n"
+    "zbpehvCzeKKCiGcz3JMr4oqJrI+EgCb/7kcZPpKAUmFiGqjX+6lZ256iOKXpE/j8\n"
+    "CBq8eGinGUBVuTob1V1WeVBxDlm/Q5waLvdz4SdgP9iBRFgZKeBSL9ieyHvv2nZT\n"
+    "r3+172BnAgvk+4T0GI7ojf1wv3PsQrVJ9mE5a8N7+oftiEP8EfBxaK3wWNHDDWCX\n"
+    "BFVMmwD2sfnzKdoCLHS1xH+cExEtRf57etREeDpRtKMlt1v2qYozV9MYcpTMv/mk\n"
+    "wbq4FTTLAgMBAAECggEANCRyQ4iXghkxROdbwsW/rE52QnAwoLwbpuw9EVvJj4e8\n"
+    "LZMu3t6lK99L5/gBxhZo49wO7YTj2+3aw2twBKXLyGDCJFEAHd0cf29yxuiJOjxu\n"
+    "LZEW8yq+O/3De0rbIzFUO2ZlqbOuudpXdhVD7mfIqjYX88wONDh5QAoM7OOEG4oe\n"
+    "xkFMWcDUwU0j5QqPlfhinrgMWYqXFNf9TZvDNXLCjmHPHZSHDnWOaguWzhhS8wlc\n"
+    "PTBblm1hG4+iBe9dv+h/15//bT/BTXVYUqBdviB9HzNRdpdLWxdydWbf7bi8iz10\n"
+    "ClTDKS6jKM6rFapwdF5zZBPYXFUaQUStrN4I9riswQKBgQDljwLLCiYhxOB6sUYU\n"
+    "J4wcmvydAapjZX+jAVveT2ZpzM+cL2nhr1FzmzMvED0UxgXG6tBkwFZIQbYlLUdH\n"
+    "aaeOKDHxQqNgwv8D6u++Nk4x7gzpLLaCCHhKQtkqlZPONN7TsHIz+Pm/9KM1mFYA\n"
+    "buzDj8uY8ZFCTAm/4pmEaiO46QKBgQDZw4VPpwlG/qS/NPP1LQI5k5Wb564mH8Fe\n"
+    "nugCwCZs186lyQ8zOodfLz/Cl0qXoABwHns67O2U19XUPuq9vPsm5GVjBDRwR8GB\n"
+    "tk9zPWnXwccNeHCfntk9vwbfdiH06aDQc0AiZvguxW5KrEDo3BKPtylF6SBN52uE\n"
+    "sU8n5h1vkwKBgQCwzdDs6MgtwiDS3q6G316+uXBOzPWa0JXZyjYjpyvN2P0d4ja+\n"
+    "p/UoASUO3obs9QeGCVyv/KN3y4SqZZE8o1d12ed9VkHXSNh4//3elpzrP9mZzeJT\n"
+    "jIp5R7tTXRkV/QqSKJgNB3n0Kkt5//ZdJxIcHShGh+fFFCN+Mtzia41P4QKBgQCV\n"
+    "wOTTow45OXL4XyUJzVsDV2ACaDAV3a6wMF1jTtrd7QcacYs3cp+XsLmLS1mrrge/\n"
+    "Eucx3a+AtXFCVcY+l1CsLVMf5cteD6qeVk6K9IfuLT+DHvlse+Pvl4fVcrrlXykN\n"
+    "UMShI+i22WUAizbULEvDc3U5s5lYmbYR+ZFy4cgKawKBgC0UnWJ2oygfERLeaVGl\n"
+    "/YnHJC50/dIKbZakaapXOFFgiep5q1jmxR2U8seb+nvtFPsTLFAdOXCfwUk+4z/h\n"
+    "kfWtB3+8H5jyoC1gkJ7EMyxu8tb4mz5U6+SPB4QLSetwvfWP2YXS/PkTq19G7iGE\n"
+    "novjJ9azSBJ6OyR5UH/DxBji\n";
+  const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+  OBufferStream os1;
+  bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+  auto pubKey = os1.buf();
+
+  PublicKey pKey;
+  pKey.loadPkcs8(pubKey->data(), pubKey->size());
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+  OBufferStream os2;
+  bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+  auto sig = os2.buf();
+
+  BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::NONE, pKey, sig->data(), sig->size()), Error);
+
+  bool result = false;
+  bufferSource(data, sizeof(data)) >>
+    verifierFilter(DigestAlgorithm::SHA256, pKey, sig->data(), sig->size()) >>
+    boolSink(result);
+
+  BOOST_CHECK_EQUAL(result, true);
+}
+
+BOOST_AUTO_TEST_CASE(Ecdsa)
+{
+  const std::string privateKeyPkcs1 =
+    "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n"
+    "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n"
+    "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n"
+    "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n"
+    "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n"
+    "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgRxwcbzK9RV6A\n"
+    "HYFsDcykI86o3M/a1KlJn0z8PcLMBZOhRANCAARobhYm4MC3RCQQzi3b0oNR3ORC\n"
+    "Uw8aupbORaGC304afBzo7sBks9KsPKHDKspLtctFeaXkOKxD3dG8HKWXfbLw\n";
+  const std::string publicKeyPkcs8 =
+    "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+    "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+    "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+    "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+    "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+    "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+    "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+  const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+  OBufferStream os1;
+  bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+  auto pubKey = os1.buf();
+
+  PublicKey pKey;
+  pKey.loadPkcs8(pubKey->data(), pubKey->size());
+
+  PrivateKey sKey;
+  sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+  OBufferStream os2;
+  bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+  auto sig = os2.buf();
+
+  BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::NONE, pKey, sig->data(), sig->size()), Error);
+
+  bool result = false;
+  bufferSource(data, sizeof(data)) >>
+    verifierFilter(DigestAlgorithm::SHA256, pKey, sig->data(), sig->size()) >>
+    boolSink(result);
+
+  BOOST_CHECK_EQUAL(result, true);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidKey)
+{
+  PublicKey pKey;
+  BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::SHA256, pKey, nullptr, 0), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestVerifierFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/additional-description.t.cpp b/tests/unit/security/v2/additional-description.t.cpp
new file mode 100644
index 0000000..7ceee8c
--- /dev/null
+++ b/tests/unit/security/v2/additional-description.t.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/additional-description.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestAdditionalDescription)
+
+const uint8_t description[] = {
+  0xfd, 0x01, 0x02, 0x28,
+    0xfd, 0x02, 0x00, 0x10, // DescriptionEntry
+      0xfd, 0x02, 0x01, 0x04, // DescriptionKey
+        0x6b, 0x65, 0x79, 0x31, // "key1"
+      0xfd, 0x02, 0x02, 0x04, // DescriptionValue
+        0x76, 0x61, 0x6c, 0x31, // "val1"
+    0xfd, 0x02, 0x00, 0x10, // DescriptionEntry
+      0xfd, 0x02, 0x01, 0x04, // DescriptionKey
+        0x6b, 0x65, 0x79, 0x32, // "key2"
+      0xfd, 0x02, 0x02, 0x04, // DescriptionValue
+        0x76, 0x61, 0x6c, 0x32, // "val2"
+};
+
+const std::string text = "((key1:val1), (key2:val2))";
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  AdditionalDescription aDescription;
+
+  aDescription.set("key2", "val2");
+  aDescription.set("key1", "val1");
+
+  BOOST_REQUIRE_NO_THROW(aDescription.get("key1"));
+  BOOST_REQUIRE_NO_THROW(aDescription.get("key2"));
+  BOOST_REQUIRE_THROW(aDescription.get("key3"), AdditionalDescription::Error);
+
+  BOOST_CHECK_EQUAL(aDescription.has("key1"), true);
+  BOOST_CHECK_EQUAL(aDescription.has("key2"), true);
+  BOOST_CHECK_EQUAL(aDescription.has("key3"), false);
+
+  auto val1 = aDescription.get("key1");
+  auto val2 = aDescription.get("key2");
+
+  BOOST_CHECK_EQUAL(val1, "val1");
+  BOOST_CHECK_EQUAL(val2, "val2");
+
+  auto it = aDescription.begin();
+  BOOST_CHECK_EQUAL(it->second, "val1");
+  it++;
+  BOOST_CHECK_EQUAL(it->second, "val2");
+  it++;
+  BOOST_CHECK(it == aDescription.end());
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(aDescription.wireEncode().wire(),
+                                aDescription.wireEncode().wire() + aDescription.wireEncode().size(),
+                                description,
+                                description + sizeof(description));
+
+  BOOST_REQUIRE_NO_THROW(AdditionalDescription(Block(description, sizeof(description))));
+  AdditionalDescription aDescription2(Block(description, sizeof(description)));
+
+  BOOST_CHECK_EQUAL(aDescription2, aDescription);
+
+  AdditionalDescription aDescription3;
+  aDescription3.set("key3", "val3");
+  aDescription3.set("key2", "val2");
+
+  BOOST_CHECK_NE(aDescription2, aDescription3);
+
+  std::ostringstream os;
+  os << aDescription;
+  BOOST_CHECK_EQUAL(os.str(), text);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestAdditionalDescription
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp b/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp
new file mode 100644
index 0000000..d2e0565
--- /dev/null
+++ b/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-bundle-fetcher.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "util/regex/regex-pattern-list-matcher.hpp"
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateBundleFetcher)
+
+class CertificateBundleFetcherWrapper : public CertificateBundleFetcher
+{
+public:
+  CertificateBundleFetcherWrapper(Face& face)
+    : CertificateBundleFetcher(make_unique<CertificateFetcherFromNetwork>(face), face)
+  {
+  }
+};
+
+class Bundle
+{
+};
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateBundleFetcherFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+                                                                            CertificateBundleFetcherWrapper>
+{
+public:
+  CertificateBundleFetcherFixture()
+    : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+  {
+    subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+    cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+    m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+    bundleRegexMatcher = make_shared<RegexPatternListMatcher>("<>*<_BUNDLE><>*", nullptr);
+    processInterest = [this] (const Interest& interest) {
+      // check if the interest is for Bundle or individual certificates
+      if (bundleRegexMatcher->match(interest.getName(), 0, interest.getName().size())) {
+        makeResponse(interest);
+      }
+      else {
+        auto cert = cache.find(interest);
+        if (cert == nullptr) {
+          return;
+        }
+        face.receive(*cert);
+      }
+    };
+  }
+
+  void
+  makeResponse(const Interest& interest);
+
+public:
+  Data data;
+  Identity subSubIdentity;
+  shared_ptr<RegexPatternListMatcher> bundleRegexMatcher;
+};
+
+template<>
+void
+CertificateBundleFetcherFixture<Bundle>::makeResponse(const Interest& interest)
+{
+  Block certList = Block(tlv::Content);
+  Name bundleName(interest.getName());
+
+  if (!bundleName.get(-1).isSegment() || bundleName.get(-1).toSegment() == 0) {
+    Block subSubCert = subSubIdentity.getDefaultKey().getDefaultCertificate().wireEncode();
+    certList.push_back(subSubCert);
+
+    if (!bundleName.get(-1).isSegment()) {
+      bundleName
+        .appendVersion()
+        .appendSegment(0);
+    }
+  }
+  else {
+    Block subCert = subIdentity.getDefaultKey().getDefaultCertificate().wireEncode();
+    Block anchor = identity.getDefaultKey().getDefaultCertificate().wireEncode();
+    certList.push_back(subCert);
+    certList.push_back(anchor);
+  }
+
+  shared_ptr<Data> certBundle = make_shared<Data>();
+  certBundle->setName(bundleName);
+  certBundle->setFreshnessPeriod(100_s);
+  certBundle->setContent(certList);
+  certBundle->setFinalBlock(name::Component::fromSegment(1));
+
+  m_keyChain.sign(*certBundle, signingWithSha256());
+
+  face.receive(*certBundle);
+}
+
+template<>
+void
+CertificateBundleFetcherFixture<Timeout>::makeResponse(const Interest& interest)
+{
+  this->advanceClocks(200_s);
+}
+
+template<>
+void
+CertificateBundleFetcherFixture<Nack>::makeResponse(const Interest& interest)
+{
+  lp::Nack nack(interest);
+  nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+  face.receive(nack);
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessWithBundle, CertificateBundleFetcherFixture<Bundle>)
+{
+  VALIDATE_SUCCESS(this->data, "Should get accepted, as interest brings the bundle segments");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2); // produced bundle has 2 segments
+
+  for (const auto& sentInterest : this->face.sentInterests) {
+    BOOST_CHECK(this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+  }
+}
+
+using SuccessWithoutBundle = boost::mpl::vector<Nack, Timeout>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateSuccessWithoutBundle, T, SuccessWithoutBundle, CertificateBundleFetcherFixture<T>)
+{
+  VALIDATE_SUCCESS(this->data, "Should get accepted, as interest brings the certs");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4); // since interest for Bundle fails, each cert is retrieved
+
+  bool toggle = true;
+  for (const auto& sentInterest : this->face.sentInterests) {
+    if (toggle) {
+      // every alternate interest is going to be that of a bundle
+      BOOST_CHECK(this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+    }
+    else {
+      BOOST_CHECK(!this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+    }
+    toggle = !toggle;
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateBundleFetcher
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-cache.t.cpp b/tests/unit/security/v2/certificate-cache.t.cpp
new file mode 100644
index 0000000..4ae3505
--- /dev/null
+++ b/tests/unit/security/v2/certificate-cache.t.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-cache.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class CertificateCacheFixture : public ndn::tests::IdentityManagementTimeFixture
+{
+public:
+  CertificateCacheFixture()
+    : certCache(10_s)
+  {
+    identity = addIdentity("/TestCertificateCache/");
+    cert = identity.getDefaultKey().getDefaultCertificate();
+  }
+
+public:
+  CertificateCache certCache;
+  Identity identity;
+  Certificate cert;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestCertificateCache, CertificateCacheFixture)
+
+BOOST_AUTO_TEST_CASE(RemovalTime)
+{
+  // Cache lifetime is capped to 10 seconds during cache construction
+
+  BOOST_CHECK_NO_THROW(certCache.insert(cert));
+  BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+  advanceClocks(11_s, 1);
+  BOOST_CHECK(certCache.find(cert.getName()) == nullptr);
+
+  BOOST_CHECK_NO_THROW(certCache.insert(cert));
+  BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+  advanceClocks(5_s);
+  BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+  advanceClocks(15_s);
+  BOOST_CHECK(certCache.find(cert.getName()) == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(FindByInterest)
+{
+  BOOST_CHECK_NO_THROW(certCache.insert(cert));
+
+  // Find by interest
+  BOOST_CHECK(certCache.find(Interest(cert.getIdentity())) != nullptr);
+  BOOST_CHECK(certCache.find(Interest(cert.getKeyName())) != nullptr);
+  BOOST_CHECK(certCache.find(Interest(Name(cert.getName()).appendVersion())) == nullptr);
+
+  advanceClocks(12_s);
+  BOOST_CHECK(certCache.find(Interest(cert.getIdentity())) == nullptr);
+
+  Certificate cert3 = addCertificate(identity.getDefaultKey(), "3");
+  Certificate cert4 = addCertificate(identity.getDefaultKey(), "4");
+  Certificate cert5 = addCertificate(identity.getDefaultKey(), "5");
+
+  certCache.insert(cert3);
+  certCache.insert(cert4);
+  certCache.insert(cert5);
+
+  Interest interest4(cert3.getKeyName());
+  interest4.setExclude(Exclude().excludeOne(cert3.getName().at(Certificate::ISSUER_ID_OFFSET)));
+  BOOST_CHECK(certCache.find(interest4) != nullptr);
+  BOOST_CHECK_NE(certCache.find(interest4)->getName(), cert3.getName());
+
+  // TODO cover more cases with different interests
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateCache
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp b/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp
new file mode 100644
index 0000000..68fe6b1
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp
@@ -0,0 +1,205 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-fetcher-direct-fetch.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "lp/nack.hpp"
+#include "lp/tags.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/range/adaptor/strided.hpp>
+#include <boost/range/adaptor/sliced.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateFetcherDirectFetch)
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateFetcherDirectFetchFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+                                                                                 CertificateFetcherDirectFetch>
+{
+public:
+  CertificateFetcherDirectFetchFixture()
+    : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+    , interest("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest")
+    , interestNoTag("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest2")
+  {
+    Identity subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+    cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+    m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+    m_keyChain.sign(interest, signingByIdentity(subSubIdentity));
+    m_keyChain.sign(interestNoTag, signingByIdentity(subSubIdentity));
+
+    data.setTag(make_shared<lp::IncomingFaceIdTag>(123));
+    interest.setTag(make_shared<lp::IncomingFaceIdTag>(123));
+
+    processInterest = [this] (const Interest& interest) {
+      auto nextHopFaceIdTag = interest.template getTag<lp::NextHopFaceIdTag>();
+      if (nextHopFaceIdTag == nullptr) {
+        makeResponse(interest); // respond only to the "infrastructure" interest
+      }
+    };
+  }
+
+  void
+  makeResponse(const Interest& interest);
+
+public:
+  Data data;
+  Interest interest;
+  Interest interestNoTag;
+};
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Cert>::makeResponse(const Interest& interest)
+{
+  auto cert = cache.find(interest);
+  if (cert == nullptr) {
+    return;
+  }
+  face.receive(*cert);
+}
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Timeout>::makeResponse(const Interest& interest)
+{
+  // do nothing
+}
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Nack>::makeResponse(const Interest& interest)
+{
+  lp::Nack nack(interest);
+  nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+  face.receive(nack);
+}
+
+using Failures = boost::mpl::vector<Timeout, Nack>;
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessData, CertificateFetcherDirectFetchFixture<Cert>)
+{
+  VALIDATE_SUCCESS(this->data, "Should get accepted, normal and/or direct interests bring certs");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+  // odd interests
+  for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+  }
+
+  // even interests
+  for (const auto& sentInterest : this->face.sentInterests |
+                                    boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+                                    boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+  }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailureData, T, Failures, CertificateFetcherDirectFetchFixture<T>)
+{
+  VALIDATE_FAILURE(this->data, "Should fail, as all interests either NACKed or timeout");
+  // Direct fetcher sends two interests each time - to network and face
+  // 3 retries on nack or timeout (2 * (1 + 3) = 4)
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 8);
+
+  // odd interests
+  for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+  }
+
+  // even interests
+  for (const auto& sentInterest : this->face.sentInterests |
+                                    boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+                                    boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+  }
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessInterest, CertificateFetcherDirectFetchFixture<Cert>)
+{
+  VALIDATE_SUCCESS(this->interest, "Should get accepted, normal and/or direct interests bring certs");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+  // odd interests
+  for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+  }
+
+  // even interests
+  for (const auto& sentInterest : this->face.sentInterests |
+                                    boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+                                    boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+  }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailureInterest, T, Failures, CertificateFetcherDirectFetchFixture<T>)
+{
+  VALIDATE_FAILURE(this->interest, "Should fail, as all interests either NACKed or timeout");
+  // Direct fetcher sends two interests each time - to network and face
+  // 3 retries on nack or timeout (2 * (1 + 3) = 4)
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 8);
+
+  // odd interests
+  for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+  }
+
+  // even interests
+  for (const auto& sentInterest : this->face.sentInterests |
+                                    boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+                                    boost::adaptors::strided(2)) {
+    BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherDirectFetch
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp b/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp
new file mode 100644
index 0000000..5a6b395
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-fetcher-from-network.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateFetcherFromNetwork)
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateFetcherFromNetworkFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+                                                                                 CertificateFetcherFromNetwork>
+{
+public:
+  CertificateFetcherFromNetworkFixture()
+    : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+    , interest("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest")
+  {
+    Identity subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+    cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+    m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+    m_keyChain.sign(interest, signingByIdentity(subSubIdentity));
+
+    processInterest = bind(&CertificateFetcherFromNetworkFixture<Response>::makeResponse, this, _1);
+  }
+
+  void
+  makeResponse(const Interest& interest);
+
+public:
+  Data data;
+  Interest interest;
+};
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Cert>::makeResponse(const Interest& interest)
+{
+  auto cert = cache.find(interest);
+  if (cert == nullptr) {
+    return;
+  }
+  face.receive(*cert);
+}
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Timeout>::makeResponse(const Interest& interest)
+{
+  // do nothing
+}
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Nack>::makeResponse(const Interest& interest)
+{
+  lp::Nack nack(interest);
+  nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+  face.receive(nack);
+}
+
+using Failures = boost::mpl::vector<Timeout, Nack>;
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccess, CertificateFetcherFromNetworkFixture<Cert>)
+{
+  VALIDATE_SUCCESS(this->data, "Should get accepted, as normal interests bring cert");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2);
+  this->face.sentInterests.clear();
+
+  this->advanceClocks(1_h, 2); // expire validator caches
+
+  VALIDATE_SUCCESS(this->interest, "Should get accepted, as interests bring certs");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailure, T, Failures, CertificateFetcherFromNetworkFixture<T>)
+{
+  VALIDATE_FAILURE(this->data, "Should fail, as interests don't bring data");
+  // first interest + 3 retries
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+  this->face.sentInterests.clear();
+
+  this->advanceClocks(1_h, 2); // expire validator caches
+
+  VALIDATE_FAILURE(this->interest, "Should fail, as interests don't bring data");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherFromNetwork
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-offline.t.cpp b/tests/unit/security/v2/certificate-fetcher-offline.t.cpp
new file mode 100644
index 0000000..102a308
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-offline.t.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/certificate-fetcher-offline.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class CertificateFetcherOfflineWrapper : public CertificateFetcherOffline
+{
+public:
+  CertificateFetcherOfflineWrapper(Face&)
+  {
+  }
+};
+
+using CertificateFetcherOfflineFixture = HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+                                                                      CertificateFetcherOfflineWrapper>;
+
+BOOST_FIXTURE_TEST_SUITE(TestCertificateFetcherOffline, CertificateFetcherOfflineFixture)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+  Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Packet");
+
+  Packet packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(subIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, as no cert should be requested");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 0);
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(identity));
+  VALIDATE_SUCCESS(packet, "Should succeed, as signed by trust anchor");
+  BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherOffline
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate.t.cpp b/tests/unit/security/v2/certificate.t.cpp
new file mode 100644
index 0000000..e677aed
--- /dev/null
+++ b/tests/unit/security/v2/certificate.t.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ */
+
+#include "security/v2/certificate.hpp"
+
+#include "boost-test.hpp"
+#include "../../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestCertificate, UnitTestTimeFixture)
+
+const uint8_t PUBLIC_KEY[] = {
+  0x30, 0x81, 0x9d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x03, 0x81, 0x8b, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9e,
+  0x06, 0x3e, 0x47, 0x85, 0xb2, 0x34, 0x37, 0xaa, 0x85, 0x47, 0xac, 0x03, 0x24, 0x83, 0xb5,
+  0x9c, 0xa8, 0x05, 0x3a, 0x24, 0x1e, 0xeb, 0x89, 0x01, 0xbb, 0xe9, 0x9b, 0xb2, 0xc3, 0x22,
+  0xac, 0x68, 0xe3, 0xf0, 0x6c, 0x02, 0xce, 0x68, 0xa6, 0xc4, 0xd0, 0xa7, 0x06, 0x90, 0x9c,
+  0xaa, 0x1b, 0x08, 0x1d, 0x8b, 0x43, 0x9a, 0x33, 0x67, 0x44, 0x6d, 0x21, 0xa3, 0x1b, 0x88,
+  0x9a, 0x97, 0x5e, 0x59, 0xc4, 0x15, 0x0b, 0xd9, 0x2c, 0xbd, 0x51, 0x07, 0x61, 0x82, 0xad,
+  0xc1, 0xb8, 0xd7, 0xbf, 0x9b, 0xcf, 0x7d, 0x24, 0xc2, 0x63, 0xf3, 0x97, 0x17, 0xeb, 0xfe,
+  0x62, 0x25, 0xba, 0x5b, 0x4d, 0x8a, 0xc2, 0x7a, 0xbd, 0x43, 0x8a, 0x8f, 0xb8, 0xf2, 0xf1,
+  0xc5, 0x6a, 0x30, 0xd3, 0x50, 0x8c, 0xc8, 0x9a, 0xdf, 0xef, 0xed, 0x35, 0xe7, 0x7a, 0x62,
+  0xea, 0x76, 0x7c, 0xbb, 0x08, 0x26, 0xc7, 0x02, 0x01, 0x11
+};
+
+const uint8_t SIG_INFO[] = {
+  0x16, 0x55, 0x1B, 0x01, 0x01, 0x1C, 0x26, 0x07, 0x24, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x05,
+  0x73, 0x69, 0x74, 0x65, 0x31, 0x08, 0x11, 0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34,
+  0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39, 0x34, 0x08, 0x03, 0x4B, 0x45, 0x59, 0xFD, 0x00, 0xFD,
+  0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32,
+  0x33, 0x37, 0x33, 0x39, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38,
+  0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38
+};
+
+const uint8_t SIG_VALUE[] = {
+  0x17, 0x80, // SignatureValue
+    0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+    0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+    0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+    0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+    0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+    0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+    0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+    0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+    0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+    0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+const uint8_t CERT[] = {
+  0x06, 0xFD, 0x01, 0xBB, // Data
+    0x07, 0x33, // Name /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+      0x08, 0x03, 0x6E, 0x64, 0x6E,
+      0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+      0x08, 0x03, 0x4B, 0x45, 0x59,
+      0x08, 0x11,
+        0x6B, 0x73, 0x6B, 0x2D, 0x31, 0x34, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+        0x34,
+      0x08, 0x04, 0x30, 0x31, 0x32, 0x33,
+      0x08, 0x07, 0xFD, 0x00, 0x00, 0x01, 0x49, 0xC9, 0x8B,
+    0x14, 0x09, // MetaInfo
+      0x18, 0x01, 0x02, // ContentType = Key
+      0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, // FreshnessPeriod = 3600000 ms
+    0x15, 0xA0, // Content
+      0x30, 0x81, 0x9D, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+      0x05, 0x00, 0x03, 0x81, 0x8B, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9E, 0x06, 0x3E,
+      0x47, 0x85, 0xB2, 0x34, 0x37, 0xAA, 0x85, 0x47, 0xAC, 0x03, 0x24, 0x83, 0xB5, 0x9C, 0xA8, 0x05,
+      0x3A, 0x24, 0x1E, 0xEB, 0x89, 0x01, 0xBB, 0xE9, 0x9B, 0xB2, 0xC3, 0x22, 0xAC, 0x68, 0xE3, 0xF0,
+      0x6C, 0x02, 0xCE, 0x68, 0xA6, 0xC4, 0xD0, 0xA7, 0x06, 0x90, 0x9C, 0xAA, 0x1B, 0x08, 0x1D, 0x8B,
+      0x43, 0x9A, 0x33, 0x67, 0x44, 0x6D, 0x21, 0xA3, 0x1B, 0x88, 0x9A, 0x97, 0x5E, 0x59, 0xC4, 0x15,
+      0x0B, 0xD9, 0x2C, 0xBD, 0x51, 0x07, 0x61, 0x82, 0xAD, 0xC1, 0xB8, 0xD7, 0xBF, 0x9B, 0xCF, 0x7D,
+      0x24, 0xC2, 0x63, 0xF3, 0x97, 0x17, 0xEB, 0xFE, 0x62, 0x25, 0xBA, 0x5B, 0x4D, 0x8A, 0xC2, 0x7A,
+      0xBD, 0x43, 0x8A, 0x8F, 0xB8, 0xF2, 0xF1, 0xC5, 0x6A, 0x30, 0xD3, 0x50, 0x8C, 0xC8, 0x9A, 0xDF,
+      0xEF, 0xED, 0x35, 0xE7, 0x7A, 0x62, 0xEA, 0x76, 0x7C, 0xBB, 0x08, 0x26, 0xC7, 0x02, 0x01, 0x11,
+    0x16, 0x55, // SignatureInfo
+      0x1B, 0x01, 0x01, // SignatureType
+      0x1C, 0x26, // KeyLocator: /ndn/site1/KEY/ksk-2516425377094
+        0x07, 0x24,
+          0x08, 0x03, 0x6E, 0x64, 0x6E,
+          0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+          0x08, 0x03, 0x4B, 0x45, 0x59,
+          0x08, 0x11,
+            0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+            0x34,
+      0xFD, 0x00, 0xFD, 0x26, // ValidityPeriod: (20150814T223739, 20150818T223738)
+        0xFD, 0x00, 0xFE, 0x0F,
+          0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x39,
+        0xFD, 0x00, 0xFF, 0x0F,
+          0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38,
+    0x17, 0x80, // SignatureValue
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+Signature
+generateFakeSignature()
+{
+  Block block1(SIG_INFO, sizeof(SIG_INFO));
+  SignatureInfo signatureInfo(block1);
+
+  Name keyLocatorName("/ndn/site1/KEY/ksk-2516425377094");
+  KeyLocator keyLocator(keyLocatorName);
+  signatureInfo.setKeyLocator(keyLocator);
+
+  ValidityPeriod period(time::fromIsoString("20141111T050000"), time::fromIsoString("20141111T060000"));
+  signatureInfo.setValidityPeriod(period);
+
+  Signature signature(signatureInfo);
+  Block block2(SIG_VALUE, sizeof(SIG_VALUE));
+  signature.setValue(block2);
+
+  return signature;
+}
+
+BOOST_AUTO_TEST_CASE(Construction)
+{
+  Block block(CERT, sizeof(CERT));
+  Certificate certificate(block);
+
+  BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+  BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+  BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+  BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+  BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20150814T223739, 20150818T223738)");
+
+  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+  BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+
+  Data data(block);
+  Certificate certificate2(std::move(data));
+  BOOST_CHECK_EQUAL(certificate, certificate2);
+}
+
+BOOST_AUTO_TEST_CASE(Setters)
+{
+  Certificate certificate;
+  certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  certificate.setFreshnessPeriod(1_h);
+  certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+  certificate.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+  BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+  BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+  BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+  BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20141111T050000, 20141111T060000)");
+
+  BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+  BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+}
+
+BOOST_AUTO_TEST_CASE(ValidityPeriodChecking)
+{
+  Certificate certificate;
+  certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  certificate.setFreshnessPeriod(1_h);
+  certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+  certificate.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_EQUAL(certificate.isValid(), true);
+  BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T045959")), false);
+  BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T060001")), false);
+}
+
+// This fixture prepares a well-formed certificate. A test case then modifies one of the
+// fields, and verifies the Certificate class correctly identifies the certificate as
+// malformed.
+class InvalidCertFixture
+{
+public:
+  InvalidCertFixture()
+  {
+    Certificate certBase(Block(CERT, sizeof(CERT)));
+    BOOST_CHECK_NO_THROW((Certificate(certBase)));
+
+    m_certBase = Data(certBase);
+    m_certBase.setSignature(generateFakeSignature());
+
+    BOOST_CHECK_NO_THROW((Certificate(m_certBase)));
+  }
+
+public:
+  Data m_certBase;
+};
+
+BOOST_FIXTURE_TEST_CASE(InvalidName, InvalidCertFixture)
+{
+  Data data(m_certBase);
+  data.setName("/ndn/site1/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+  data.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+  BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(InvalidType, InvalidCertFixture)
+{
+  Data data(m_certBase);
+  data.setContentType(tlv::ContentType_Blob);
+  data.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+  BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(EmptyContent, InvalidCertFixture)
+{
+  Data data(m_certBase);
+  data.setContent(nullptr, 0);
+  data.setSignature(generateFakeSignature());
+
+  BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+  BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+
+  Certificate cert(m_certBase);
+  cert.setContent(nullptr, 0);
+  cert.setSignature(generateFakeSignature());
+  BOOST_CHECK_THROW(cert.getPublicKey(), Certificate::Error);
+}
+
+BOOST_AUTO_TEST_CASE(PrintCertificateInfo)
+{
+  const std::string expectedCertificateInfo = std::string(R"INFO(
+Certificate name:
+  /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+Validity:
+  NotBefore: 20150814T223739
+  NotAfter: 20150818T223738
+Public key bits:
+  MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCeBj5HhbI0N6qFR6wDJIO1nKgF
+  OiQe64kBu+mbssMirGjj8GwCzmimxNCnBpCcqhsIHYtDmjNnRG0hoxuImpdeWcQV
+  C9ksvVEHYYKtwbjXv5vPfSTCY/OXF+v+YiW6W02Kwnq9Q4qPuPLxxWow01CMyJrf
+  7+0153pi6nZ8uwgmxwIBEQ==
+Signature Information:
+  Signature Type: SignatureSha256WithRsa
+  Key Locator: Name=/ndn/site1/KEY/ksk-2516425377094
+)INFO").substr(1);
+
+  Certificate certificate(Block(CERT, sizeof(CERT)));
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate), expectedCertificateInfo);
+
+  // @todo Check output formats of other certificates
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificate
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/key-chain.t.cpp b/tests/unit/security/v2/key-chain.t.cpp
new file mode 100644
index 0000000..649ef7b
--- /dev/null
+++ b/tests/unit/security/v2/key-chain.t.cpp
@@ -0,0 +1,445 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/key-chain.hpp"
+#include "security/signing-helpers.hpp"
+#include "security/verification-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "../../test-home-env-saver.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestKeyChain, TestHomeEnvSaver)
+
+template<class Path>
+class TestHomeAndPibFixture : public TestHomeFixture<Path>
+{
+public:
+  TestHomeAndPibFixture()
+  {
+    unsetenv("NDN_CLIENT_PIB");
+    unsetenv("NDN_CLIENT_TPM");
+  }
+
+  ~TestHomeAndPibFixture()
+  {
+    try {
+      const_cast<std::string&>(KeyChain::getDefaultPibLocator()).clear();
+    }
+    catch (const KeyChain::Error&) {
+      // ignore
+    }
+
+    try {
+      const_cast<std::string&>(KeyChain::getDefaultTpmLocator()).clear();
+    }
+    catch (const KeyChain::Error&) {
+      // ignore
+    }
+  }
+};
+
+struct PibPathConfigFileHome
+{
+  const std::string PATH = "build/config-file-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>)
+{
+  createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"});
+
+  BOOST_REQUIRE_NO_THROW(KeyChain());
+
+  KeyChain keyChain;
+  BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+  BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+  BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileEmptyHome
+{
+  const std::string PATH = "build/config-file-empty-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>)
+{
+  createClientConf({"pib=pib-memory:"});
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+  std::string oldHOME;
+  if (std::getenv("OLD_HOME"))
+    oldHOME = std::getenv("OLD_HOME");
+
+  std::string HOME;
+  if (std::getenv("HOME"))
+    HOME = std::getenv("HOME");
+
+  if (!oldHOME.empty())
+    setenv("HOME", oldHOME.c_str(), 1);
+  else
+    unsetenv("HOME");
+#endif
+
+  BOOST_REQUIRE_NO_THROW(KeyChain());
+  KeyChain keyChain;
+  BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+  BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:");
+  BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:");
+#else
+  BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:");
+  BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+#endif
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+  if (!HOME.empty())
+    setenv("HOME", HOME.c_str(), 1);
+  else
+    unsetenv("HOME");
+
+  if (!oldHOME.empty())
+    setenv("OLD_HOME", oldHOME.c_str(), 1);
+  else
+    unsetenv("OLD_HOME");
+#endif
+}
+
+struct PibPathConfigFileEmpty2Home
+{
+  const std::string PATH = "build/config-file-empty2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
+{
+  createClientConf({"tpm=tpm-memory:"});
+
+  BOOST_REQUIRE_NO_THROW(KeyChain());
+
+  KeyChain keyChain;
+  BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+  BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+  BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileMalformedHome
+{
+  const std::string PATH = "build/config-file-malformed-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
+{
+  createClientConf({"pib=lord", "tpm=ring"});
+
+  BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileMalformed2Home
+{
+  const std::string PATH = "build/config-file-malformed2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
+{
+  createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
+
+  BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileNonCanonicalTpm
+{
+  const std::string PATH = "build/config-file-non-canonical-tpm/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNonCanonicalTpm, TestHomeAndPibFixture<PibPathConfigFileNonCanonicalTpm>) // Bug 4297
+{
+  createClientConf({"pib=pib-sqlite3:", "tpm=tpm-file"});
+
+  {
+    KeyChain keyChain;
+    keyChain.createIdentity("/test");
+    BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+    BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+  }
+
+  {
+    KeyChain keyChain;
+    BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+    BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+    BOOST_CHECK(keyChain.getPib().getIdentities().find("/test") != keyChain.getPib().getIdentities().end());
+  }
+}
+
+BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib)
+{
+  BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory", "tpm-memory")));
+  BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:", "tpm-memory:")));
+  BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:/something", "tpm-memory:/something")));
+
+  KeyChain keyChain("pib-memory", "tpm-memory");
+  BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+  BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+  BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+BOOST_FIXTURE_TEST_CASE(Management, IdentityManagementFixture)
+{
+  Name identityName("/test/id");
+  Name identity2Name("/test/id2");
+
+  BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+  BOOST_REQUIRE_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error);
+
+  // Create identity
+  Identity id = m_keyChain.createIdentity(identityName);
+  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());
+  // The default key of the added identity must exist
+  Key key;
+  BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
+  // The default certificate of the default key must exist
+  BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
+
+  // Delete key
+  Name key1Name = key.getName();
+  BOOST_CHECK_NO_THROW(id.getKey(key1Name));
+  BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+  m_keyChain.deleteKey(id, key);
+  // The key instance should not be valid any more
+  BOOST_CHECK(!key);
+  BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error);
+  BOOST_CHECK_EQUAL(id.getKeys().size(), 0);
+
+  // 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());
+
+  // Create the third key
+  Key key3 = m_keyChain.createKey(id);
+  BOOST_CHECK_NE(key3.getName(), key2.getName());
+  // 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());
+
+  // Delete cert
+  BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+  Certificate key3Cert1 = *key3.getCertificates().begin();
+  Name key3CertName = key3Cert1.getName();
+  m_keyChain.deleteCertificate(key3, key3CertName);
+  BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0);
+  BOOST_REQUIRE_THROW(key3.getDefaultCertificate(), Pib::Error);
+
+  // Add cert
+  m_keyChain.addCertificate(key3, key3Cert1);
+  BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+  BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+  m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work
+  BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+  // Add another cert
+  Certificate key3Cert2 = key3Cert1;
+  Name key3Cert2Name = key3.getName();
+  key3Cert2Name.append("Self");
+  key3Cert2Name.appendVersion();
+  key3Cert2.setName(key3Cert2Name);
+  m_keyChain.addCertificate(key3, key3Cert2);
+  BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2);
+
+  // Default certificate setting
+  BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName);
+  m_keyChain.setDefaultCertificate(key3, key3Cert2);
+  BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name);
+
+  // Default key setting
+  BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
+  m_keyChain.setDefaultKey(id, key3);
+  BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName());
+
+  // Default identity setting
+  Identity id2 = m_keyChain.createIdentity(identity2Name);
+  BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName());
+  m_keyChain.setDefaultIdentity(id2);
+  BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName());
+
+  // Delete identity
+  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(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
+}
+
+BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, IdentityManagementFixture)
+{
+  Identity id = addIdentity("/id");
+  Key key = id.getDefaultKey();
+  Certificate cert = key.getDefaultCertificate();
+
+  std::list<SigningInfo> signingInfos = {
+    SigningInfo(),
+
+    SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
+    signingByIdentity(id.getName()),
+
+    SigningInfo(id),
+    signingByIdentity(id),
+
+    SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName()),
+    signingByKey(key.getName()),
+
+    SigningInfo(key),
+    signingByKey(key),
+
+    SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
+    signingByCertificate(cert.getName()),
+    signingByCertificate(cert),
+
+    SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
+    signingWithSha256()
+  };
+
+  for (const auto& signingInfo : signingInfos) {
+    BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
+    Data data("/data");
+    Interest interest("/interest");
+
+    if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_NULL) {
+      m_keyChain.sign(data);
+      m_keyChain.sign(interest);
+    }
+    else {
+      m_keyChain.sign(data, signingInfo);
+      m_keyChain.sign(interest, signingInfo);
+    }
+
+    Signature interestSignature(interest.getName()[-2].blockFromValue(), interest.getName()[-1].blockFromValue());
+
+    if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
+      BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::DigestSha256);
+      BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::DigestSha256);
+
+      BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
+      BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
+    }
+    else {
+      BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
+      BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
+
+      BOOST_CHECK_EQUAL(data.getSignature().getKeyLocator().getName(), cert.getName().getPrefix(-2));
+      BOOST_CHECK_EQUAL(interestSignature.getKeyLocator().getName(), cert.getName().getPrefix(-2));
+
+      BOOST_CHECK(verifySignature(data, key));
+      BOOST_CHECK(verifySignature(interest, key));
+    }
+  }
+}
+
+BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, IdentityManagementFixture)
+{
+  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 = addIdentity("/ndn/test/ec", EcKeyParams());
+  BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+  BOOST_CHECK_EQUAL(data.getSignature().getType(),
+                    KeyChain::getSignatureType(EcKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+  BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+
+  // Create identity with RSA key and the corresponding self-signed certificate
+  id = addIdentity("/ndn/test/rsa", RsaKeyParams());
+  BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+  BOOST_CHECK_EQUAL(data.getSignature().getType(),
+                    KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+  BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+}
+
+BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
+{
+  Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
+  Certificate cert = id.getDefaultKey().getDefaultCertificate();
+
+  shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
+  Block block = exported->wireEncode();
+
+  m_keyChain.deleteIdentity(id);
+  BOOST_CHECK_THROW(m_keyChain.exportSafeBag(cert, "1234", 4), KeyChain::Error);
+
+  BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+  BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+
+  SafeBag imported;
+  imported.wireDecode(block);
+  m_keyChain.importSafeBag(imported, "1234", 4);
+  BOOST_CHECK_THROW(m_keyChain.importSafeBag(imported, "1234", 4), KeyChain::Error);
+
+  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()));
+
+  m_keyChain.deleteIdentity(newId);
+  BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+  BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(SelfSignedCertValidity, IdentityManagementFixture)
+{
+  Certificate cert = addIdentity("/Security/V2/TestKeyChain/SelfSignedCertValidity")
+                       .getDefaultKey()
+                       .getDefaultCertificate();
+  BOOST_CHECK(cert.isValid());
+  BOOST_CHECK(cert.isValid(time::system_clock::now() + 10 * 365_days));
+  BOOST_CHECK_GT(cert.getValidityPeriod().getPeriod().second, time::system_clock::now() + 10 * 365_days);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyChain
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/trust-anchor-container.t.cpp b/tests/unit/security/v2/trust-anchor-container.t.cpp
new file mode 100644
index 0000000..efd6ca3
--- /dev/null
+++ b/tests/unit/security/v2/trust-anchor-container.t.cpp
@@ -0,0 +1,198 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/trust-anchor-container.hpp"
+#include "util/io.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+#include "boost-test.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+/**
+ * This fixture creates a directory and prepares two certificates.
+ * cert1 is written to a file under the directory, while cert2 is not.
+ */
+class AnchorContainerTestFixture : public IdentityManagementTimeFixture
+{
+public:
+  AnchorContainerTestFixture()
+  {
+    namespace fs = boost::filesystem;
+
+    fs::create_directory(fs::path(UNIT_TEST_CONFIG_PATH));
+
+    certDirPath = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir";
+    fs::create_directory(certDirPath);
+
+    certPath1 = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir" / "trust-anchor-1.cert";
+    certPath2 = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir" / "trust-anchor-2.cert";
+
+    identity1 = addIdentity("/TestAnchorContainer/First");
+    cert1 = identity1.getDefaultKey().getDefaultCertificate();
+    saveCertToFile(cert1, certPath1.string());
+
+    identity2 = addIdentity("/TestAnchorContainer/Second");
+    cert2 = identity2.getDefaultKey().getDefaultCertificate();
+    saveCertToFile(cert2, certPath2.string());
+  }
+
+  ~AnchorContainerTestFixture()
+  {
+    boost::filesystem::remove_all(UNIT_TEST_CONFIG_PATH);
+  }
+
+public:
+  TrustAnchorContainer anchorContainer;
+
+  boost::filesystem::path certDirPath;
+  boost::filesystem::path certPath1;
+  boost::filesystem::path certPath2;
+
+  Identity identity1;
+  Identity identity2;
+
+  Certificate cert1;
+  Certificate cert2;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestTrustAnchorContainer, AnchorContainerTestFixture)
+
+// one static group and one dynamic group created from file
+BOOST_AUTO_TEST_CASE(Insert)
+{
+  // Static
+  anchorContainer.insert("group1", Certificate(cert1));
+  BOOST_CHECK(anchorContainer.find(cert1.getName()) != nullptr);
+  BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+  const Certificate* cert = anchorContainer.find(cert1.getName());
+  BOOST_CHECK_NO_THROW(anchorContainer.insert("group1", Certificate(cert1)));
+  BOOST_CHECK_EQUAL(cert, anchorContainer.find(cert1.getName())); // still the same instance of the certificate
+  // cannot add dynamic group when static already exists
+  BOOST_CHECK_THROW(anchorContainer.insert("group1", certPath1.string(), 1_s), TrustAnchorContainer::Error);
+  BOOST_CHECK_EQUAL(anchorContainer.getGroup("group1").size(), 1);
+  BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
+
+  // From file
+  anchorContainer.insert("group2", certPath2.string(), 1_s);
+  BOOST_CHECK(anchorContainer.find(cert2.getName()) != nullptr);
+  BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
+  BOOST_CHECK_THROW(anchorContainer.insert("group2", Certificate(cert2)), TrustAnchorContainer::Error);
+  BOOST_CHECK_THROW(anchorContainer.insert("group2", certPath2.string(), 1_s), TrustAnchorContainer::Error);
+  BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 1);
+  BOOST_CHECK_EQUAL(anchorContainer.size(), 2);
+
+  boost::filesystem::remove(certPath2);
+  advanceClocks(1_s, 11);
+
+  BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+  BOOST_CHECK(anchorContainer.find(cert2.getName()) == nullptr);
+  BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 0);
+  BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
+
+  TrustAnchorGroup& group = anchorContainer.getGroup("group1");
+  auto staticGroup = dynamic_cast<StaticTrustAnchorGroup*>(&group);
+  BOOST_REQUIRE(staticGroup != nullptr);
+  BOOST_CHECK_EQUAL(staticGroup->size(), 1);
+  staticGroup->remove(cert1.getName());
+  BOOST_CHECK_EQUAL(staticGroup->size(), 0);
+  BOOST_CHECK_EQUAL(anchorContainer.size(), 0);
+
+  BOOST_CHECK_THROW(anchorContainer.getGroup("non-existing-group"), TrustAnchorContainer::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DynamicAnchorFromDir)
+{
+  boost::filesystem::remove(certPath2);
+
+  anchorContainer.insert("group", certDirPath.string(), 1_s, true /* isDir */);
+
+  BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+  BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+  BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 1);
+
+  saveCertToFile(cert2, certPath2.string());
+
+  advanceClocks(100_ms, 11);
+
+  BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+  BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
+  BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 2);
+
+  boost::filesystem::remove_all(certDirPath);
+
+  advanceClocks(100_ms, 11);
+
+  BOOST_CHECK(anchorContainer.find(identity1.getName()) == nullptr);
+  BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+  BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(FindByInterest, AnchorContainerTestFixture)
+{
+  anchorContainer.insert("group1", certPath1.string(), 1_s);
+  Interest interest(identity1.getName());
+  BOOST_CHECK(anchorContainer.find(interest) != nullptr);
+  Interest interest1(identity1.getName().getPrefix(-1));
+  BOOST_CHECK(anchorContainer.find(interest1) != nullptr);
+  Interest interest2(Name(identity1.getName()).appendVersion());
+  BOOST_CHECK(anchorContainer.find(interest2) == nullptr);
+
+  Certificate cert3 = addCertificate(identity1.getDefaultKey(), "3");
+  Certificate cert4 = addCertificate(identity1.getDefaultKey(), "4");
+  Certificate cert5 = addCertificate(identity1.getDefaultKey(), "5");
+
+  Certificate cert3Copy = cert3;
+  anchorContainer.insert("group2", std::move(cert3Copy));
+  anchorContainer.insert("group3", std::move(cert4));
+  anchorContainer.insert("group4", std::move(cert5));
+
+  Interest interest3(cert3.getKeyName());
+  const Certificate* foundCert = anchorContainer.find(interest3);
+  BOOST_REQUIRE(foundCert != nullptr);
+  BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
+  BOOST_CHECK_EQUAL(foundCert->getName(), cert3.getName());
+
+  interest3.setExclude(Exclude().excludeOne(cert3.getName().at(Certificate::ISSUER_ID_OFFSET)));
+  foundCert = anchorContainer.find(interest3);
+  BOOST_REQUIRE(foundCert != nullptr);
+  BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
+  BOOST_CHECK_NE(foundCert->getName(), cert3.getName());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTrustAnchorContainer
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-error.t.cpp b/tests/unit/security/v2/validation-error.t.cpp
new file mode 100644
index 0000000..1db3aac
--- /dev/null
+++ b/tests/unit/security/v2/validation-error.t.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-error.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestValidationError)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  ValidationError e1{ValidationError::Code::INVALID_SIGNATURE};
+  BOOST_CHECK_EQUAL(e1.getCode(), 1);
+  BOOST_CHECK_EQUAL(e1.getInfo(), "");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e1), "Invalid signature");
+
+  ValidationError e2{ValidationError::Code::NO_SIGNATURE, "message"};
+  BOOST_CHECK_EQUAL(e2.getCode(), 2);
+  BOOST_CHECK_EQUAL(e2.getInfo(), "message");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e2), "Missing signature (message)");
+
+  ValidationError e3{65535, "other message"};
+  BOOST_CHECK_EQUAL(e3.getCode(), 65535);
+  BOOST_CHECK_EQUAL(e3.getInfo(), "other message");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e3), "Custom error code 65535 (other message)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationError
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-accept-all.t.cpp b/tests/unit/security/v2/validation-policy-accept-all.t.cpp
new file mode 100644
index 0000000..61466d6
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-accept-all.t.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-accept-all.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class ValidationPolicyAcceptAllFixture : public ValidatorFixture<ValidationPolicyAcceptAll>
+{
+public:
+  ValidationPolicyAcceptAllFixture()
+  {
+    identity = addIdentity("/Security/V2/TestValidationPolicyAcceptAll");
+    // don't add trust anchors
+  }
+
+public:
+  Identity identity;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyAcceptAll, ValidationPolicyAcceptAllFixture)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+  Packet unsignedPacket("/Security/V2/TestValidationPolicyAcceptAll/Sub/Packet");
+
+  Packet packet = unsignedPacket;
+  VALIDATE_SUCCESS(packet, "Should accept unsigned");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingWithSha256());
+  VALIDATE_SUCCESS(packet, "Should accept Sha256Digest signature");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(identity));
+  VALIDATE_SUCCESS(packet, "Should accept signature while no trust anchors configured");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyAcceptAll
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-command-interest.t.cpp b/tests/unit/security/v2/validation-policy-command-interest.t.cpp
new file mode 100644
index 0000000..7bd42ea
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-command-interest.t.cpp
@@ -0,0 +1,485 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-command-interest.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "security/v2/validation-policy-accept-all.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+#include "make-interest-data.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class DefaultOptions
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    return {};
+  }
+};
+
+template<class T, class InnerPolicy>
+class CommandInterestPolicyWrapper : public ValidationPolicyCommandInterest
+{
+public:
+  CommandInterestPolicyWrapper()
+    : ValidationPolicyCommandInterest(make_unique<InnerPolicy>(), T::getOptions())
+  {
+  }
+};
+
+template<class T, class InnerPolicy = ValidationPolicySimpleHierarchy>
+class ValidationPolicyCommandInterestFixture : public HierarchicalValidatorFixture<CommandInterestPolicyWrapper<T, InnerPolicy>>
+{
+public:
+  ValidationPolicyCommandInterestFixture()
+    : m_signer(this->m_keyChain)
+  {
+  }
+
+  Interest
+  makeCommandInterest(const Identity& identity)
+  {
+    return m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"),
+                                        signingByIdentity(identity));
+  }
+
+public:
+  CommandInterestSigner m_signer;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyCommandInterest, ValidationPolicyCommandInterestFixture<DefaultOptions>)
+
+BOOST_AUTO_TEST_SUITE(Accepts)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  auto i1 = makeCommandInterest(identity);
+  VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
+  VALIDATE_FAILURE(i1, "Should fail (replay attack)");
+
+  advanceClocks(5_ms);
+  auto i2 = makeCommandInterest(identity);
+  VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
+
+  auto i3 =  m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"), signingWithSha256());
+  VALIDATE_FAILURE(i3, "Should fail (Sha256 signature violates policy)");
+}
+
+BOOST_AUTO_TEST_CASE(DataPassthru)
+{
+  Data d1("/Security/V2/ValidatorFixture/Sub1");
+  m_keyChain.sign(d1);
+  VALIDATE_SUCCESS(d1, "Should succeed (fallback on inner validation policy for data)");
+}
+
+using ValidationPolicyAcceptAllCommands = ValidationPolicyCommandInterestFixture<DefaultOptions,
+                                                                                 ValidationPolicyAcceptAll>;
+
+BOOST_FIXTURE_TEST_CASE(SignedWithSha256, ValidationPolicyAcceptAllCommands) // Bug 4635
+{
+  auto i1 = m_signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+  VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
+  VALIDATE_FAILURE(i1, "Should fail (replay attack)");
+
+  advanceClocks(5_ms);
+  auto i2 = m_signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+  VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Accepts
+
+BOOST_AUTO_TEST_SUITE(Rejects)
+
+BOOST_AUTO_TEST_CASE(NameTooShort)
+{
+  auto i1 = makeInterest("/name/too/short");
+  VALIDATE_FAILURE(*i1, "Should fail (name is too short)");
+}
+
+BOOST_AUTO_TEST_CASE(BadTimestamp)
+{
+  auto i1 = makeCommandInterest(identity);
+  setNameComponent(i1, command_interest::POS_TIMESTAMP, "not-timestamp");
+  VALIDATE_FAILURE(i1, "Should fail (timestamp is missing)");
+}
+
+BOOST_AUTO_TEST_CASE(BadSigInfo)
+{
+  auto i1 = makeCommandInterest(identity);
+  setNameComponent(i1, command_interest::POS_SIG_INFO, "not-SignatureInfo");
+  VALIDATE_FAILURE(i1, "Should fail (signature info is missing)");
+}
+
+BOOST_AUTO_TEST_CASE(MissingKeyLocator)
+{
+  auto i1 = makeCommandInterest(identity);
+  SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+  setNameComponent(i1, command_interest::POS_SIG_INFO,
+                   sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+  VALIDATE_FAILURE(i1, "Should fail (missing KeyLocator)");
+}
+
+BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
+{
+  auto i1 = makeCommandInterest(identity);
+  KeyLocator kl;
+  kl.setKeyDigest(makeBinaryBlock(tlv::KeyDigest, "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD", 8));
+  SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+  sigInfo.setKeyLocator(kl);
+  setNameComponent(i1, command_interest::POS_SIG_INFO,
+                   sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+  VALIDATE_FAILURE(i1, "Should fail (bad KeyLocator type)");
+}
+
+BOOST_AUTO_TEST_CASE(BadCertName)
+{
+  auto i1 = makeCommandInterest(identity);
+  KeyLocator kl;
+  kl.setName("/bad/cert/name");
+  SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+  sigInfo.setKeyLocator(kl);
+  setNameComponent(i1, command_interest::POS_SIG_INFO,
+                   sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+  VALIDATE_FAILURE(i1, "Should fail (bad certificate name)");
+}
+
+BOOST_AUTO_TEST_CASE(InnerPolicyReject)
+{
+  auto i1 = makeCommandInterest(otherIdentity);
+  VALIDATE_FAILURE(i1, "Should fail (inner policy should reject)");
+}
+
+class GracePeriod15Sec
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = 15_s;
+    return options;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(TimestampOutOfGracePositive, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
+{
+  auto i1 = makeCommandInterest(identity); // signed at 0s
+  advanceClocks(16_s); // verifying at +16s
+  VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
+  rewindClockAfterValidation();
+
+  auto i2 = makeCommandInterest(identity); // signed at +16s
+  VALIDATE_SUCCESS(i2, "Should succeed");
+}
+
+BOOST_FIXTURE_TEST_CASE(TimestampOutOfGraceNegative, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
+{
+  auto i1 = makeCommandInterest(identity); // signed at 0s
+  advanceClocks(1_s);
+  auto i2 = makeCommandInterest(identity); // signed at +1s
+  advanceClocks(1_s);
+  auto i3 = makeCommandInterest(identity); // signed at +2s
+
+  systemClock->advance(-18_s); // verifying at -16s
+  VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
+  rewindClockAfterValidation();
+
+  // CommandInterestValidator should not remember i1's timestamp
+  VALIDATE_FAILURE(i2, "Should fail (timestamp outside the grace period)");
+  rewindClockAfterValidation();
+
+  // CommandInterestValidator should not remember i2's timestamp, and should treat i3 as initial
+  advanceClocks(18_s); // verifying at +2s
+  VALIDATE_SUCCESS(i3, "Should succeed");
+}
+
+BOOST_AUTO_TEST_CASE(TimestampReorderEqual)
+{
+  auto i1 = makeCommandInterest(identity); // signed at 0s
+  VALIDATE_SUCCESS(i1, "Should succeed");
+
+  auto i2 = makeCommandInterest(identity); // signed at 0s
+  setNameComponent(i2, command_interest::POS_TIMESTAMP,
+                   i1.getName()[command_interest::POS_TIMESTAMP]);
+  VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
+
+  advanceClocks(2_s);
+  auto i3 = makeCommandInterest(identity); // signed at +2s
+  VALIDATE_SUCCESS(i3, "Should succeed");
+}
+
+BOOST_AUTO_TEST_CASE(TimestampReorderNegative)
+{
+  auto i2 = makeCommandInterest(identity); // signed at 0ms
+  advanceClocks(200_ms);
+  auto i3 = makeCommandInterest(identity); // signed at +200ms
+  advanceClocks(900_ms);
+  auto i1 = makeCommandInterest(identity); // signed at +1100ms
+  advanceClocks(300_ms);
+  auto i4 = makeCommandInterest(identity); // signed at +1400ms
+
+  systemClock->advance(-300_ms); // verifying at +1100ms
+  VALIDATE_SUCCESS(i1, "Should succeed");
+  rewindClockAfterValidation();
+
+  systemClock->advance(-1100_ms); // verifying at 0ms
+  VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
+  rewindClockAfterValidation();
+
+  // CommandInterestValidator should not remember i2's timestamp
+  advanceClocks(200_ms); // verifying at +200ms
+  VALIDATE_FAILURE(i3, "Should fail (timestamp reordered)");
+  rewindClockAfterValidation();
+
+  advanceClocks(1200_ms); // verifying at 1400ms
+  VALIDATE_SUCCESS(i4, "Should succeed");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Rejects
+
+BOOST_AUTO_TEST_SUITE(Options)
+
+template<class T>
+class GracePeriod
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = time::seconds(T::value);
+    return options;
+  }
+};
+
+typedef boost::mpl::vector<
+  GracePeriod<boost::mpl::int_<0>>,
+  GracePeriod<boost::mpl::int_<-1>>
+> GraceNonPositiveValues;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(GraceNonPositive, GracePeriod, GraceNonPositiveValues,
+                                 ValidationPolicyCommandInterestFixture<GracePeriod>)
+{
+  auto i1 = this->makeCommandInterest(this->identity); // signed at 0ms
+  auto i2 = this->makeCommandInterest(this->subIdentity); // signed at 0ms
+  for (auto interest : {&i1, &i2}) {
+    setNameComponent(*interest, command_interest::POS_TIMESTAMP,
+                     name::Component::fromNumber(time::toUnixTimestamp(time::system_clock::now()).count()));
+  } // ensure timestamps are exactly 0ms
+
+  VALIDATE_SUCCESS(i1, "Should succeed when validating at 0ms");
+  this->rewindClockAfterValidation();
+
+  this->advanceClocks(1_ms);
+  VALIDATE_FAILURE(i2, "Should fail when validating at 1ms");
+}
+
+class LimitedRecordsOptions
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = 15_s;
+    options.maxRecords = 3;
+    return options;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(LimitedRecords, ValidationPolicyCommandInterestFixture<LimitedRecordsOptions>)
+{
+  Identity id1 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
+  this->cache.insert(id1.getDefaultKey().getDefaultCertificate());
+  Identity id2 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub2", identity);
+  this->cache.insert(id2.getDefaultKey().getDefaultCertificate());
+  Identity id3 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub3", identity);
+  this->cache.insert(id3.getDefaultKey().getDefaultCertificate());
+  Identity id4 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub4", identity);
+  this->cache.insert(id4.getDefaultKey().getDefaultCertificate());
+
+  auto i1 = makeCommandInterest(id2);
+  auto i2 = makeCommandInterest(id3);
+  auto i3 = makeCommandInterest(id4);
+  auto i00 = makeCommandInterest(id1); // signed at 0s
+  advanceClocks(1_s);
+  auto i01 = makeCommandInterest(id1); // signed at 1s
+  advanceClocks(1_s);
+  auto i02 = makeCommandInterest(id1); // signed at 2s
+
+  VALIDATE_SUCCESS(i00, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i02, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i1, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i2, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i3, "Should succeed, forgets identity id1");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i01, "Should succeed despite timestamp is reordered, because record has been evicted");
+}
+
+class UnlimitedRecordsOptions
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = 15_s;
+    options.maxRecords = -1;
+    return options;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(UnlimitedRecords, ValidationPolicyCommandInterestFixture<UnlimitedRecordsOptions>)
+{
+  std::vector<Identity> identities;
+  for (int i = 0; i < 20; ++i) {
+    Identity id = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub" + to_string(i), identity);
+    this->cache.insert(id.getDefaultKey().getDefaultCertificate());
+    identities.push_back(id);
+  }
+
+  auto i1 = makeCommandInterest(identities.at(0)); // signed at 0s
+  advanceClocks(1_s);
+  for (int i = 0; i < 20; ++i) {
+    auto i2 = makeCommandInterest(identities.at(i)); // signed at +1s
+
+    VALIDATE_SUCCESS(i2, "Should succeed");
+    rewindClockAfterValidation();
+  }
+  VALIDATE_FAILURE(i1, "Should fail (timestamp reorder)");
+}
+
+class ZeroRecordsOptions
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = 15_s;
+    options.maxRecords = 0;
+    return options;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ZeroRecords, ValidationPolicyCommandInterestFixture<ZeroRecordsOptions>)
+{
+  auto i1 = makeCommandInterest(identity); // signed at 0s
+  advanceClocks(1_s);
+  auto i2 = makeCommandInterest(identity); // signed at +1s
+  VALIDATE_SUCCESS(i2, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record isn't kept");
+}
+
+class LimitedRecordLifetimeOptions
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = 400_s;
+    options.recordLifetime = 300_s;
+    return options;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(LimitedRecordLifetime, ValidationPolicyCommandInterestFixture<LimitedRecordLifetimeOptions>)
+{
+  auto i1 = makeCommandInterest(identity); // signed at 0s
+  advanceClocks(240_s);
+  auto i2 = makeCommandInterest(identity); // signed at +240s
+  advanceClocks(120_s);
+  auto i3 = makeCommandInterest(identity); // signed at +360s
+
+  systemClock->advance(-360_s); // rewind system clock to 0s
+  VALIDATE_SUCCESS(i1, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i3, "Should succeed");
+  rewindClockAfterValidation();
+
+  advanceClocks(30_s, 301_s); // advance steady clock by 301s, and system clock to +301s
+  VALIDATE_SUCCESS(i2, "Should succeed despite timestamp is reordered, because record has been expired");
+}
+
+class ZeroRecordLifetimeOptions
+{
+public:
+  static ValidationPolicyCommandInterest::Options
+  getOptions()
+  {
+    ValidationPolicyCommandInterest::Options options;
+    options.gracePeriod = 15_s;
+    options.recordLifetime = time::seconds::zero();
+    return options;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ZeroRecordLifetime, ValidationPolicyCommandInterestFixture<ZeroRecordLifetimeOptions>)
+{
+  auto i1 = makeCommandInterest(identity); // signed at 0s
+  advanceClocks(1_s);
+  auto i2 = makeCommandInterest(identity); // signed at +1s
+  VALIDATE_SUCCESS(i2, "Should succeed");
+  rewindClockAfterValidation();
+
+  VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record has been expired");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Options
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyCommandInterest
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-config.t.cpp b/tests/unit/security/v2/validation-policy-config.t.cpp
new file mode 100644
index 0000000..cca3e7a
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-config.t.cpp
@@ -0,0 +1,539 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-config.hpp"
+#include "security/transform/base64-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "util/logger.hpp"
+#include "util/io.hpp"
+
+#include "boost-test.hpp"
+#include "validator-config/common.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestValidationPolicyConfig)
+
+template<typename Packet>
+class PacketName;
+
+template<>
+class PacketName<Interest>
+{
+public:
+  static std::string
+  getName()
+  {
+    return "interest";
+  }
+};
+
+template<>
+class PacketName<Data>
+{
+public:
+  static std::string
+  getName()
+  {
+    return "data";
+  }
+};
+
+template<typename PacketType>
+class ValidationPolicyConfigFixture : public HierarchicalValidatorFixture<ValidationPolicyConfig>
+{
+public:
+  ValidationPolicyConfigFixture()
+    : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "v2" / "validation-policy-config")
+  {
+    boost::filesystem::create_directories(path);
+    baseConfig = R"CONF(
+        rule
+        {
+          id test-rule-id
+          for )CONF" + PacketName<Packet>::getName() + R"CONF(
+          filter
+          {
+            type name
+            name )CONF" + identity.getName().toUri() + R"CONF(
+            relation is-prefix-of
+          }
+          checker
+          {
+            type hierarchical
+            sig-type rsa-sha256
+          }
+        }
+      )CONF";
+  }
+
+  ~ValidationPolicyConfigFixture()
+  {
+    boost::filesystem::remove_all(path);
+  }
+
+public:
+  using Packet = PacketType;
+
+  const boost::filesystem::path path;
+  std::string baseConfig;
+};
+
+template<typename PacketType>
+class LoadStringWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+  LoadStringWithFileAnchor()
+  {
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+    this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+    this->policy.load(this->baseConfig + R"CONF(
+        trust-anchor
+        {
+          type file
+          file-name "trust-anchor.ndncert"
+        }
+      )CONF", (this->path / "test-config").string());
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+    BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+  }
+};
+
+template<typename PacketType>
+class LoadFileWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+  LoadFileWithFileAnchor()
+  {
+    std::string configFile = (this->path / "config.conf").string();
+    {
+      std::ofstream config(configFile.c_str());
+      config << this->baseConfig << R"CONF(
+        trust-anchor
+        {
+          type file
+          file-name "trust-anchor.ndncert"
+        }
+      )CONF";
+    }
+    this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+    this->policy.load(configFile);
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+    BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+  }
+};
+
+template<typename PacketType>
+class LoadSectionWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+  LoadSectionWithFileAnchor()
+  {
+    auto section = makeSection(this->baseConfig + R"CONF(
+        trust-anchor
+        {
+          type file
+          file-name "trust-anchor.ndncert"
+        }
+      )CONF");
+
+    this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+    this->policy.load(section, (this->path / "test-config").string());
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+    BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+  }
+};
+
+template<typename PacketType>
+class LoadStringWithBase64Anchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+  LoadStringWithBase64Anchor()
+  {
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+    std::ostringstream os;
+    using namespace ndn::security::transform;
+    const auto& cert = this->identity.getDefaultKey().getDefaultCertificate();
+    bufferSource(cert.wireEncode().wire(), cert.wireEncode().size()) >> base64Encode(false) >> streamSink(os);
+
+    this->policy.load(this->baseConfig + R"CONF(
+        trust-anchor
+        {
+          type base64
+          base64-string ")CONF" + os.str() + R"CONF("
+        }
+      )CONF", (this->path / "test-config").string());
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+    BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+  }
+};
+
+class NoRefresh
+{
+public:
+  static std::string
+  getRefreshString()
+  {
+    return "";
+  }
+};
+
+class Refresh1h
+{
+public:
+  static std::string
+  getRefreshString()
+  {
+    return "refresh 1h";
+  }
+
+  static time::milliseconds
+  getRefreshTime()
+  {
+    return 1_h;
+  }
+};
+
+class Refresh1m
+{
+public:
+  static std::string
+  getRefreshString()
+  {
+    return "refresh 1m";
+  }
+
+  static time::milliseconds
+  getRefreshTime()
+  {
+    return 1_min;
+  }
+};
+
+class Refresh1s
+{
+public:
+  static std::string
+  getRefreshString()
+  {
+    return "refresh 1s";
+  }
+
+  static time::milliseconds
+  getRefreshTime()
+  {
+    return 1_s;
+  }
+};
+
+template<typename PacketType, typename Refresh = NoRefresh>
+class LoadStringWithDirAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+  LoadStringWithDirAnchor()
+  {
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+    boost::filesystem::create_directories(this->path / "keys");
+    this->saveCertificate(this->identity, (this->path / "keys" / "identity.ndncert").string());
+
+    this->policy.load(this->baseConfig + R"CONF(
+        trust-anchor
+        {
+          type dir
+          dir keys
+          )CONF" + Refresh::getRefreshString() + R"CONF(
+        }
+      )CONF", (this->path / "test-config").string());
+
+    BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+    BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+  }
+};
+
+using DataPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Data>,
+                                        LoadFileWithFileAnchor<Data>,
+                                        LoadSectionWithFileAnchor<Data>,
+                                        LoadStringWithBase64Anchor<Data>,
+                                        LoadStringWithDirAnchor<Data>,
+                                        LoadStringWithDirAnchor<Data, Refresh1h>,
+                                        LoadStringWithDirAnchor<Data, Refresh1m>,
+                                        LoadStringWithDirAnchor<Data, Refresh1s>
+                                        >;
+
+using InterestPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Interest>,
+                                            LoadFileWithFileAnchor<Interest>,
+                                            LoadSectionWithFileAnchor<Interest>,
+                                            LoadStringWithBase64Anchor<Interest>,
+                                            LoadStringWithDirAnchor<Interest>,
+                                            LoadStringWithDirAnchor<Interest, Refresh1h>,
+                                            LoadStringWithDirAnchor<Interest, Refresh1m>,
+                                            LoadStringWithDirAnchor<Interest, Refresh1s>
+                                            >;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateData, Policy, DataPolicies, Policy)
+{
+  BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
+  BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+
+  using Packet = typename Policy::Packet;
+  Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+  Packet packet = unsignedPacket;
+  VALIDATE_FAILURE(packet, "Unsigned");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingWithSha256());
+  VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+  VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+  VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateInterest, Policy, InterestPolicies, Policy)
+{
+  BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+  BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
+
+  using Packet = typename Policy::Packet;
+  Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+  Packet packet = unsignedPacket;
+  VALIDATE_FAILURE(packet, "Unsigned");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingWithSha256());
+  VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+  VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, as there is no matching rule for data");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+}
+
+BOOST_FIXTURE_TEST_CASE(Reload, 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 /foo/bar
+          relation is-prefix-of
+        }
+        checker
+        {
+          type hierarchical
+          sig-type rsa-sha256
+        }
+      }
+      rule
+      {
+        id test-rule-interest-id
+        for interest
+        filter
+        {
+          type name
+          name /foo/bar
+          relation is-prefix-of
+        }
+        checker
+        {
+          type hierarchical
+          sig-type rsa-sha256
+        }
+      }
+      trust-anchor
+      {
+        type dir
+        dir keys
+        refresh 1h
+      }
+    )CONF", "test-config");
+  BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+  BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+  BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
+  BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
+
+  this->policy.load(R"CONF(
+      trust-anchor
+      {
+        type any
+      }
+    )CONF", "test-config");
+  BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+  BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
+  BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+  BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+}
+
+using Packets = boost::mpl::vector<Interest, Data>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
+{
+  this->policy.load(R"CONF(
+      trust-anchor
+      {
+        type any
+      }
+    )CONF", "test-config");
+
+  BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+  BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
+  BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+  BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+
+  Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+  Packet packet = unsignedPacket;
+  VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingWithSha256());
+  VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+  VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+  VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+  VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+  VALIDATE_SUCCESS(packet, "Policy should accept everything");
+}
+
+using RefreshPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
+
+// Somehow, didn't work without this wrapper
+template<typename RefreshPolicy>
+class RefreshPolicyFixture : public LoadStringWithDirAnchor<Data, RefreshPolicy>
+{
+public:
+};
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateRefresh, Refresh, RefreshPolicies, RefreshPolicyFixture<Refresh>)
+{
+  using Packet = Data;
+  Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+  boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
+  this->advanceClocks(Refresh::getRefreshTime(), 3);
+
+  Packet packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+  VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
+
+  packet = unsignedPacket;
+  this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
+}
+
+BOOST_FIXTURE_TEST_CASE(OrphanedPolicyLoad, HierarchicalValidatorFixture<ValidationPolicyConfig>) // Bug #4758
+{
+  ValidationPolicyConfig policy1;
+  BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
+
+  // Reloading would have triggered a segfault
+  BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
+
+  ValidationPolicyConfig policy2;
+
+  std::string config = R"CONF(
+      trust-anchor
+      {
+        type dir
+        dir keys
+        refresh 1h
+      }
+    )CONF";
+
+  // Inserting trust anchor would have triggered a segfault
+  BOOST_CHECK_THROW(policy2.load(config, "test-config"), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp b/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp
new file mode 100644
index 0000000..8661ac5
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicySimpleHierarchy,
+                         HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+  Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+  Packet packet = unsignedPacket;
+  VALIDATE_FAILURE(packet, "Unsigned");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingWithSha256());
+  VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(identity));
+  VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(subIdentity));
+  VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(otherIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+  packet = unsignedPacket;
+  m_keyChain.sign(packet, signingByIdentity(subSelfSignedIdentity));
+  VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+
+  // TODO add checks with malformed packets
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidator
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/checker.t.cpp b/tests/unit/security/v2/validator-config/checker.t.cpp
new file mode 100644
index 0000000..2170908
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/checker.t.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/checker.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/v2/validation-policy.hpp"
+#include "security/v2/validation-state.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+#include "../validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+class CheckerFixture : public IdentityManagementFixture
+{
+public:
+  CheckerFixture()
+  {
+    names.push_back("/foo/bar");
+    names.push_back("/foo/bar/bar");
+    names.push_back("/foo");
+    names.push_back("/other/prefix");
+  }
+
+  Name
+  makeSignedInterestName(const Name& name)
+  {
+    return Name(name).append("SignatureInfo").append("SignatureValue");
+  }
+
+  Name
+  makeKeyLocatorName(const Name& name)
+  {
+    return Name(name).append("KEY").append("v=1");
+  }
+
+public:
+  std::vector<Name> names;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestChecker, CheckerFixture)
+
+class NameRelationEqual : public CheckerFixture
+{
+public:
+  NameRelationEqual()
+    : checker("/foo/bar", NameRelation::EQUAL)
+  {
+  }
+
+public:
+  NameRelationChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false}};
+};
+
+class NameRelationIsPrefixOf : public CheckerFixture
+{
+public:
+  NameRelationIsPrefixOf()
+    : checker("/foo/bar", NameRelation::IS_PREFIX_OF)
+  {
+  }
+
+public:
+  NameRelationChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{true, true, false, false},
+                                             {true, true, false, false},
+                                             {true, true, false, false},
+                                             {true, true, false, false}};
+};
+
+class NameRelationIsStrictPrefixOf : public CheckerFixture
+{
+public:
+  NameRelationIsStrictPrefixOf()
+    : checker("/foo/bar", NameRelation::IS_STRICT_PREFIX_OF)
+  {
+  }
+
+public:
+  NameRelationChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{false, true, false, false},
+                                             {false, true, false, false},
+                                             {false, true, false, false},
+                                             {false, true, false, false}};
+};
+
+class RegexEqual : public CheckerFixture
+{
+public:
+  RegexEqual()
+    : checker(Regex("^<foo><bar><KEY><>$"))
+  {
+  }
+
+public:
+  RegexChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false}};
+};
+
+class RegexIsPrefixOf : public CheckerFixture
+{
+public:
+  RegexIsPrefixOf()
+    : checker(Regex("^<foo><bar><>*<KEY><>$"))
+  {
+  }
+
+public:
+  RegexChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{true, true, false, false},
+                                             {true, true, false, false},
+                                             {true, true, false, false},
+                                             {true, true, false, false}};
+};
+
+class RegexIsStrictPrefixOf : public CheckerFixture
+{
+public:
+  RegexIsStrictPrefixOf()
+    : checker(Regex("^<foo><bar><>+<KEY><>$"))
+  {
+  }
+
+public:
+  RegexChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{false, true, false, false},
+                                             {false, true, false, false},
+                                             {false, true, false, false},
+                                             {false, true, false, false}};
+};
+
+class HyperRelationEqual : public CheckerFixture
+{
+public:
+  HyperRelationEqual()
+    : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::EQUAL)
+  {
+  }
+
+public:
+  HyperRelationChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{true,  false, false, false},
+                                             {false, true,  false, false},
+                                             {false, false, true,  false},
+                                             {false, false, false, true}};
+};
+
+class HyperRelationIsPrefixOf : public CheckerFixture
+{
+public:
+  HyperRelationIsPrefixOf()
+    : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::IS_PREFIX_OF)
+  {
+  }
+
+public:
+  HyperRelationChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{true,  false, true,  false},
+                                             {true,  true,  true,  false},
+                                             {false, false, true,  false},
+                                             {false, false, false, true}};
+};
+
+class HyperRelationIsStrictPrefixOf : public CheckerFixture
+{
+public:
+  HyperRelationIsStrictPrefixOf()
+    : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::IS_STRICT_PREFIX_OF)
+  {
+  }
+
+public:
+  HyperRelationChecker checker;
+  std::vector<std::vector<bool>> outcomes = {{false, false, true,  false},
+                                             {true,  false, true,  false},
+                                             {false, false, false, false},
+                                             {false, false, false, false}};
+};
+
+class Hierarchical : public CheckerFixture
+{
+public:
+  Hierarchical()
+    : checkerPtr(Checker::create(makeSection(R"CONF(
+          type hierarchical
+          sig-type rsa-sha256
+        )CONF"), "test-config"))
+    , checker(*checkerPtr)
+  {
+  }
+
+public:
+  std::unique_ptr<Checker> checkerPtr;
+  Checker& checker;
+
+  std::vector<std::vector<bool>> outcomes = {{true,  false, true,  false},
+                                             {true,  true,  true,  false},
+                                             {false, false, true,  false},
+                                             {false, false, false, true}};
+};
+
+class CustomizedNameRelation : public CheckerFixture
+{
+public:
+  CustomizedNameRelation()
+    : checkerPtr(Checker::create(makeSection(R"CONF(
+          type customized
+          sig-type rsa-sha256
+          key-locator
+          {
+            type name
+            name /foo/bar
+            relation equal
+          }
+        )CONF"), "test-config"))
+    , checker(*checkerPtr)
+  {
+  }
+
+public:
+  std::unique_ptr<Checker> checkerPtr;
+  Checker& checker;
+
+  std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false}};
+};
+
+class CustomizedRegex : public CheckerFixture
+{
+public:
+  CustomizedRegex()
+    : checkerPtr(Checker::create(makeSection(R"CONF(
+          type customized
+          sig-type rsa-sha256
+          key-locator
+          {
+            type name
+            regex ^<foo><bar><KEY><>$
+          }
+        )CONF"), "test-config"))
+    , checker(*checkerPtr)
+  {
+  }
+
+public:
+  std::unique_ptr<Checker> checkerPtr;
+  Checker& checker;
+
+  std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false},
+                                             {true, false, false, false}};
+};
+
+class CustomizedHyperRelation : public CheckerFixture
+{
+public:
+  CustomizedHyperRelation()
+    : checkerPtr(Checker::create(makeSection(R"CONF(
+          type customized
+          sig-type rsa-sha256
+          key-locator
+          {
+            type name
+            hyper-relation
+            {
+              k-regex ^(<>+)<KEY><>$
+              k-expand \\1
+              h-relation is-prefix-of
+              p-regex ^(<>+)$
+              p-expand \\1
+            }
+          }
+        )CONF"), "test-config"))
+    , checker(*checkerPtr)
+  {
+  }
+
+public:
+  std::unique_ptr<Checker> checkerPtr;
+  Checker& checker;
+
+  std::vector<std::vector<bool>> outcomes = {{true,  false, true,  false},
+                                             {true,  true,  true,  false},
+                                             {false, false, true,  false},
+                                             {false, false, false, true}};
+};
+
+
+using Tests = boost::mpl::vector<NameRelationEqual, NameRelationIsPrefixOf, NameRelationIsStrictPrefixOf,
+                                 RegexEqual, RegexIsPrefixOf, RegexIsStrictPrefixOf,
+                                 HyperRelationEqual, HyperRelationIsPrefixOf, HyperRelationIsStrictPrefixOf,
+                                 Hierarchical,
+                                 CustomizedNameRelation, CustomizedRegex, CustomizedHyperRelation>;
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Checks, T, Tests, T)
+{
+  BOOST_ASSERT(this->outcomes.size() == this->names.size());
+  for (size_t i = 0; i < this->names.size(); ++i) {
+    BOOST_ASSERT(this->outcomes[i].size() == this->names.size());
+    for (size_t j = 0; j < this->names.size(); ++j) {
+      const Name& pktName = this->names[i];
+      Name klName = this->makeKeyLocatorName(this->names[j]);
+      bool expectedOutcome = this->outcomes[i][j];
+
+      auto dataState = make_shared<DummyValidationState>();
+      BOOST_CHECK_EQUAL(this->checker.check(tlv::Data, pktName, klName, dataState), expectedOutcome);
+      BOOST_CHECK_EQUAL(boost::logic::indeterminate(dataState->getOutcome()), expectedOutcome);
+      if (boost::logic::indeterminate(dataState->getOutcome()) == !expectedOutcome) {
+        BOOST_CHECK_EQUAL(dataState->getOutcome(), !expectedOutcome);
+      }
+
+      auto interestState = make_shared<DummyValidationState>();
+      BOOST_CHECK_EQUAL(this->checker.check(tlv::Interest, this->makeSignedInterestName(pktName),
+                                            klName, interestState), expectedOutcome);
+      BOOST_CHECK_EQUAL(boost::logic::indeterminate(interestState->getOutcome()), expectedOutcome);
+      if (boost::logic::indeterminate(interestState->getOutcome()) == !expectedOutcome) {
+        BOOST_CHECK_EQUAL(interestState->getOutcome(), !expectedOutcome);
+      }
+    }
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestChecker
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/common.hpp b/tests/unit/security/v2/validator-config/common.hpp
new file mode 100644
index 0000000..231e8eb
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/common.hpp
@@ -0,0 +1,48 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+#define NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+
+#include <boost/property_tree/info_parser.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+inline ConfigSection
+makeSection(const std::string& config)
+{
+  std::istringstream inputStream(config);
+  ConfigSection section;
+  boost::property_tree::read_info(inputStream, section);
+  return section;
+}
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
diff --git a/tests/unit/security/v2/validator-config/filter.t.cpp b/tests/unit/security/v2/validator-config/filter.t.cpp
new file mode 100644
index 0000000..fbd1264
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/filter.t.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/filter.hpp"
+#include "security/command-interest-signer.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+class FilterFixture : public IdentityManagementFixture
+{
+public:
+  Interest
+  makeSignedInterest(const Name& name)
+  {
+    Interest interest(name);
+    m_keyChain.sign(interest);
+    return interest;
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestFilter, FilterFixture)
+
+#define CHECK_FOR_MATCHES(filter, same, longer, shorter, different)     \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo/bar").getName()), same); \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo/bar").getName()), same);   \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo/bar/bar").getName()), longer); \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo/bar/bar").getName()), longer);       \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo").getName()), shorter); \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo").getName()), shorter);              \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/other/prefix").getName()), different); \
+  BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/other/prefix").getName()), different);
+
+BOOST_AUTO_TEST_CASE(RelationName)
+{
+  RelationNameFilter f1("/foo/bar", NameRelation::EQUAL);
+  CHECK_FOR_MATCHES(f1, true, false, false, false);
+
+  RelationNameFilter f2("/foo/bar", NameRelation::IS_PREFIX_OF);
+  CHECK_FOR_MATCHES(f2, true, true, false, false);
+
+  RelationNameFilter f3("/foo/bar", NameRelation::IS_STRICT_PREFIX_OF);
+  CHECK_FOR_MATCHES(f3, false, true, false, false);
+}
+
+BOOST_AUTO_TEST_CASE(RegexName)
+{
+  RegexNameFilter f1(Regex("^<foo><bar>$"));
+  CHECK_FOR_MATCHES(f1, true, false, false, false);
+
+  RegexNameFilter f2(Regex("^<foo><bar><>*$"));
+  CHECK_FOR_MATCHES(f2, true, true, false, false);
+
+  RegexNameFilter f3(Regex("^<foo><bar><>+$"));
+  CHECK_FOR_MATCHES(f3, false, true, false, false);
+}
+
+BOOST_FIXTURE_TEST_SUITE(Create, FilterFixture)
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  BOOST_CHECK_THROW(Filter::create(makeSection(""), "test-config"), Error);
+  BOOST_CHECK_THROW(Filter::create(makeSection("type unknown"), "test-config"), Error);
+  BOOST_CHECK_THROW(Filter::create(makeSection("type name"), "test-config"), Error);
+
+  std::string config = R"CONF(
+      type name
+      not-name-or-regex stuff
+    )CONF";
+  BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+  config = R"CONF(
+      type name
+      name /foo/bar
+    )CONF";
+  BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+  config = R"CONF(
+      type name
+      name /foo/bar
+      not-relation stuff
+    )CONF";
+  BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+  config = R"CONF(
+      type name
+      name /foo/bar
+      relation equal
+      not-end stuff
+    )CONF";
+  BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+  config = R"CONF(
+      type name
+      regex ^<foo><bar>$
+      not-end stuff
+    )CONF";
+  BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameFilter)
+{
+  std::string config = R"CONF(
+      type name
+      name /foo/bar
+      relation equal
+    )CONF";
+  auto f1 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f1), true, false, false, false);
+
+  config = R"CONF(
+      type name
+      name /foo/bar
+      relation is-prefix-of
+    )CONF";
+  auto f2 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f2), true, true, false, false);
+
+  config = R"CONF(
+      type name
+      name /foo/bar
+      relation is-strict-prefix-of
+    )CONF";
+  auto f3 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f3), false, true, false, false);
+}
+
+BOOST_AUTO_TEST_CASE(RegexFilter)
+{
+  std::string config = R"CONF(
+      type name
+      regex ^<foo><bar>$
+    )CONF";
+  auto f1 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f1), true, false, false, false);
+
+  config = R"CONF(
+      type name
+      regex ^<foo><bar><>*$
+    )CONF";
+  auto f2 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f2), true, true, false, false);
+
+  config = R"CONF(
+      type name
+      regex ^<foo><bar><>+$
+    )CONF";
+  auto f3 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f3), false, true, false, false);
+
+  config = R"CONF(
+      type name
+      regex ^<>*$
+    )CONF";
+  auto f4 = Filter::create(makeSection(config), "test-config");
+  CHECK_FOR_MATCHES((*f4), true, true, true, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Create
+
+BOOST_AUTO_TEST_SUITE_END() // TestFilter
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/name-relation.t.cpp b/tests/unit/security/v2/validator-config/name-relation.t.cpp
new file mode 100644
index 0000000..157b55a
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/name-relation.t.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/name-relation.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+BOOST_AUTO_TEST_SUITE(TestNameRelation)
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::EQUAL), "equal");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::IS_PREFIX_OF),
+                    "is-prefix-of");
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::IS_STRICT_PREFIX_OF),
+                    "is-strict-prefix-of");
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+  BOOST_CHECK_EQUAL(getNameRelationFromString("equal"), NameRelation::EQUAL);
+  BOOST_CHECK_EQUAL(getNameRelationFromString("is-prefix-of"), NameRelation::IS_PREFIX_OF);
+  BOOST_CHECK_EQUAL(getNameRelationFromString("is-strict-prefix-of"), NameRelation::IS_STRICT_PREFIX_OF);
+  BOOST_CHECK_THROW(getNameRelationFromString("unknown"), validator_config::Error);
+}
+
+BOOST_AUTO_TEST_CASE(CheckRelation)
+{
+  BOOST_CHECK(checkNameRelation(NameRelation::EQUAL, "/prefix", "/prefix"));
+  BOOST_CHECK(!checkNameRelation(NameRelation::EQUAL, "/prefix", "/prefix/other"));
+
+  BOOST_CHECK(checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix", "/prefix"));
+  BOOST_CHECK(checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix", "/prefix/other"));
+  BOOST_CHECK(!checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix/other", "/prefix"));
+
+  BOOST_CHECK(!checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix", "/prefix"));
+  BOOST_CHECK(checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix", "/prefix/other"));
+  BOOST_CHECK(!checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix/other", "/prefix"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNameRelation
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/rule.t.cpp b/tests/unit/security/v2/validator-config/rule.t.cpp
new file mode 100644
index 0000000..bb78dfe
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/rule.t.cpp
@@ -0,0 +1,209 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator-config/rule.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+#include "../validator-fixture.hpp"
+
+#include <boost/mpl/vector_c.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+template<uint32_t PktType>
+class RuleFixture : public IdentityManagementFixture
+{
+public:
+  RuleFixture()
+    : rule(ruleId, PktType)
+    , pktName("/foo/bar")
+  {
+    if (PktType == tlv::Interest) {
+      pktName = Name("/foo/bar/SigInfo/SigValue");
+    }
+  }
+
+public:
+  const std::string ruleId = "rule-id";
+  Rule rule;
+  Name pktName;
+};
+
+using PktTypes = boost::mpl::vector_c<uint32_t, tlv::Data, tlv::Interest>;
+
+BOOST_AUTO_TEST_SUITE(TestRule)
+
+BOOST_FIXTURE_TEST_CASE(Errors, RuleFixture<tlv::Data>)
+{
+  BOOST_CHECK_THROW(rule.match(tlv::Interest, this->pktName), Error);
+
+  auto state = make_shared<DummyValidationState>();
+  BOOST_CHECK_THROW(rule.check(tlv::Interest, this->pktName, "/foo/bar", state), Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Constructor, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+  BOOST_CHECK_EQUAL(this->rule.getId(), this->ruleId);
+  BOOST_CHECK_EQUAL(this->rule.getPktType(), PktType::value);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(EmptyRule, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+  BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+
+  auto state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), false);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Filters, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+  this->rule.addFilter(make_unique<RegexNameFilter>(Regex("^<foo><bar>$")));
+
+  BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+  BOOST_CHECK_EQUAL(this->rule.match(PktType::value, "/not" + this->pktName.toUri()), false);
+
+  this->rule.addFilter(make_unique<RegexNameFilter>(Regex("^<not><foo><bar>$")));
+
+  BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+  BOOST_CHECK_EQUAL(this->rule.match(PktType::value, "/not" + this->pktName.toUri()), true);
+
+  auto state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), false);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Checkers, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+  this->rule.addChecker(make_unique<HyperRelationChecker>("^(<>+)$", "\\1",
+                                                        "^<not>?(<>+)$", "\\1",
+                                                        NameRelation::EQUAL));
+
+  auto state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+  state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/not/foo/bar", state), true);
+
+  this->rule.addChecker(make_unique<HyperRelationChecker>("^(<>+)$", "\\1",
+                                                        "^(<>+)$", "\\1",
+                                                        NameRelation::EQUAL));
+  state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+  state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/not/foo/bar", state), false);
+}
+
+BOOST_AUTO_TEST_SUITE(Create)
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+  BOOST_CHECK_THROW(Rule::create(makeSection(""), "test-config"), Error);
+
+  std::string config = R"CONF(
+      id rule-id
+      for something
+    )CONF";
+  BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error);
+
+  config = R"CONF(
+      id rule-id
+      for data
+    )CONF";
+  BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error); // at least one checker required
+
+  config = R"CONF(
+      id rule-id
+      for data
+      checker
+      {
+        type hierarchical
+        sig-type rsa-sha256
+      }
+      other stuff
+    )CONF";
+  BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(FilterAndChecker, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+  std::string config = R"CONF(
+      id rule-id
+      for )CONF" + (PktType::value == tlv::Data ? "data"s : "interest"s) + R"CONF(
+      filter
+      {
+        type name
+        regex ^<foo><bar>$
+      }
+      checker
+      {
+        type customized
+        sig-type rsa-sha256
+        key-locator
+        {
+          type name
+          hyper-relation
+          {
+            k-regex ^(<>+)$
+            k-expand \\1
+            h-relation equal
+            p-regex ^(<>+)$
+            p-expand \\1
+          }
+        }
+      }
+    )CONF";
+  auto rule = Rule::create(makeSection(config), "test-config");
+
+  BOOST_CHECK_EQUAL(rule->match(PktType::value, this->pktName), true);
+  BOOST_CHECK_EQUAL(rule->match(PktType::value, "/not" + this->pktName.toUri()), false);
+
+  auto state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(rule->check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+  state = make_shared<DummyValidationState>();
+  BOOST_CHECK_EQUAL(rule->check(PktType::value, this->pktName, "/not/foo/bar", state), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Create
+
+BOOST_AUTO_TEST_SUITE_END() // TestRule
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-fixture.hpp b/tests/unit/security/v2/validator-fixture.hpp
new file mode 100644
index 0000000..c9a5763
--- /dev/null
+++ b/tests/unit/security/v2/validator-fixture.hpp
@@ -0,0 +1,183 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
+#define NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
+
+#include "security/v2/validator.hpp"
+#include "security/v2/certificate-fetcher-from-network.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+template<class ValidationPolicy, class CertificateFetcher = CertificateFetcherFromNetwork>
+class ValidatorFixture : public ndn::tests::IdentityManagementTimeFixture
+{
+public:
+  ValidatorFixture()
+    : face(io, {true, true})
+    , validator(make_unique<ValidationPolicy>(), make_unique<CertificateFetcher>(face))
+    , policy(static_cast<ValidationPolicy&>(validator.getPolicy()))
+    , cache(100_days)
+  {
+    processInterest = [this] (const Interest& interest) {
+      auto cert = cache.find(interest);
+      if (cert != nullptr) {
+        face.receive(*cert);
+      }
+    };
+  }
+
+  virtual
+  ~ValidatorFixture() = default;
+
+  template<class Packet>
+  void
+  validate(const Packet& packet, const std::string& msg, bool expectSuccess, int line)
+  {
+    std::string detailedInfo = msg + " on line " + to_string(line);
+    size_t nCallbacks = 0;
+    this->validator.validate(packet,
+      [&] (const Packet&) {
+        ++nCallbacks;
+        BOOST_CHECK_MESSAGE(expectSuccess,
+                            (expectSuccess ? "OK: " : "FAILED: ") + detailedInfo);
+      },
+      [&] (const Packet&, const ValidationError& error) {
+        ++nCallbacks;
+        BOOST_CHECK_MESSAGE(!expectSuccess,
+                            (!expectSuccess ? "OK: " : "FAILED: ") + detailedInfo +
+                            (expectSuccess ? " (" + boost::lexical_cast<std::string>(error) + ")" : ""));
+      });
+
+    mockNetworkOperations();
+    BOOST_CHECK_EQUAL(nCallbacks, 1);
+  }
+
+  void
+  mockNetworkOperations()
+  {
+    util::signal::ScopedConnection connection = face.onSendInterest.connect([this] (const Interest& interest) {
+        if (processInterest != nullptr) {
+          io.post(bind(processInterest, interest));
+        }
+      });
+    advanceClocks(time::milliseconds(s_mockPeriod), s_mockTimes);
+  }
+
+  /** \brief undo clock advancement of mockNetworkOperations
+   */
+  void
+  rewindClockAfterValidation()
+  {
+    this->systemClock->advance(time::milliseconds(s_mockPeriod * s_mockTimes * -1));
+  }
+
+public:
+  util::DummyClientFace face;
+  std::function<void(const Interest& interest)> processInterest;
+  Validator validator;
+  ValidationPolicy& policy;
+
+  CertificateCache cache;
+
+private:
+  const static int s_mockPeriod;
+  const static int s_mockTimes;
+};
+
+template<class ValidationPolicy, class CertificateFetcher>
+const int ValidatorFixture<ValidationPolicy, CertificateFetcher>::s_mockPeriod = 250;
+
+template<class ValidationPolicy, class CertificateFetcher>
+const int ValidatorFixture<ValidationPolicy, CertificateFetcher>::s_mockTimes = 200;
+
+template<class ValidationPolicy, class CertificateFetcher = CertificateFetcherFromNetwork>
+class HierarchicalValidatorFixture : public ValidatorFixture<ValidationPolicy, CertificateFetcher>
+{
+public:
+  HierarchicalValidatorFixture()
+  {
+    identity = this->addIdentity("/Security/V2/ValidatorFixture");
+    subIdentity = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
+    subSelfSignedIdentity = this->addIdentity("/Security/V2/ValidatorFixture/Sub1/Sub2");
+    otherIdentity = this->addIdentity("/Security/V2/OtherIdentity");
+
+    this->validator.loadAnchor("", Certificate(identity.getDefaultKey().getDefaultCertificate()));
+
+    this->cache.insert(identity.getDefaultKey().getDefaultCertificate());
+    this->cache.insert(subIdentity.getDefaultKey().getDefaultCertificate());
+    this->cache.insert(subSelfSignedIdentity.getDefaultKey().getDefaultCertificate());
+    this->cache.insert(otherIdentity.getDefaultKey().getDefaultCertificate());
+  }
+
+public:
+  Identity identity;
+  Identity subIdentity;
+  Identity subSelfSignedIdentity;
+  Identity otherIdentity;
+};
+
+#define VALIDATE_SUCCESS(packet, message) this->template validate(packet, message, true, __LINE__)
+#define VALIDATE_FAILURE(packet, message) this->template validate(packet, message, false, __LINE__)
+
+class DummyValidationState : public ValidationState
+{
+public:
+  ~DummyValidationState()
+  {
+    m_outcome = false;
+  }
+
+  void
+  fail(const ValidationError& error) override
+  {
+    // BOOST_TEST_MESSAGE(error);
+    m_outcome = false;
+  }
+
+private:
+  void
+  verifyOriginalPacket(const Certificate& trustedCert) override
+  {
+    // do nothing
+  }
+
+  void
+  bypassValidation() override
+  {
+    // do nothing
+  }
+};
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
diff --git a/tests/unit/security/v2/validator.t.cpp b/tests/unit/security/v2/validator.t.cpp
new file mode 100644
index 0000000..ff33984
--- /dev/null
+++ b/tests/unit/security/v2/validator.t.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/v2/validator.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestValidator, HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
+
+BOOST_AUTO_TEST_CASE(ConstructorSetValidator)
+{
+  auto middlePolicy = make_unique<ValidationPolicySimpleHierarchy>();
+  auto innerPolicy = make_unique<ValidationPolicySimpleHierarchy>();
+
+  validator.getPolicy().setInnerPolicy(std::move(middlePolicy));
+  validator.getPolicy().setInnerPolicy(std::move(innerPolicy));
+
+  BOOST_CHECK(validator.getPolicy().m_validator != nullptr);
+  BOOST_CHECK(validator.getPolicy().getInnerPolicy().m_validator != nullptr);
+  BOOST_CHECK(validator.getPolicy().getInnerPolicy().getInnerPolicy().m_validator != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(Timeouts)
+{
+  processInterest = nullptr; // no response for all interests
+
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+  VALIDATE_FAILURE(data, "Should fail to retrieve certificate");
+  BOOST_CHECK_GT(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(NackedInterests)
+{
+  processInterest = [this] (const Interest& interest) {
+    lp::Nack nack(interest);
+    nack.setReason(lp::NackReason::NO_ROUTE);
+    face.receive(nack);
+  };
+
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+  VALIDATE_FAILURE(data, "All interests should get NACKed");
+  // 1 for the first interest, 3 for the retries on nack
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCert)
+{
+  Data malformedCert = subIdentity.getDefaultKey().getDefaultCertificate();
+  malformedCert.setContentType(tlv::ContentType_Blob);
+  m_keyChain.sign(malformedCert, signingByIdentity(identity));
+  // wrong content type & missing ValidityPeriod
+  BOOST_REQUIRE_THROW(Certificate(malformedCert.wireEncode()), tlv::Error);
+
+  auto originalProcessInterest = processInterest;
+  processInterest = [this, &originalProcessInterest, &malformedCert] (const Interest& interest) {
+    if (interest.getName().isPrefixOf(malformedCert.getName())) {
+      face.receive(malformedCert);
+    }
+    else {
+      originalProcessInterest(interest);
+    }
+  };
+
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+  VALIDATE_FAILURE(data, "Signed by a malformed certificate");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ExpiredCert)
+{
+  Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
+  SignatureInfo info;
+  info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 2_h,
+                                        time::system_clock::now() - 1_h));
+  m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
+  BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
+
+  auto originalProcessInterest = processInterest;
+  processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
+    if (interest.getName().isPrefixOf(expiredCert.getName())) {
+      face.receive(expiredCert);
+    }
+    else {
+      originalProcessInterest(interest);
+    }
+  };
+
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+  VALIDATE_FAILURE(data, "Signed by an expired certificate");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ResetAnchors)
+{
+  validator.resetAnchors();
+
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+  VALIDATE_FAILURE(data, "Should fail, as no anchors configured");
+}
+
+BOOST_AUTO_TEST_CASE(TrustedCertCaching)
+{
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+  VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+  face.sentInterests.clear();
+
+  processInterest = nullptr; // disable data responses from mocked network
+
+  VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+  face.sentInterests.clear();
+
+  advanceClocks(1_h, 2); // expire trusted cache
+
+  VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
+  BOOST_CHECK_GT(face.sentInterests.size(), 1);
+  face.sentInterests.clear();
+}
+
+BOOST_AUTO_TEST_CASE(ResetVerifiedCertificates)
+{
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+  VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
+
+  // reset anchors
+  validator.resetAnchors();
+  VALIDATE_SUCCESS(data, "Should get accepted, as signed by the cert in trusted cache");
+
+  // reset trusted cache
+  validator.resetVerifiedCertificates();
+  VALIDATE_FAILURE(data, "Should fail, as no trusted cache or anchors");
+}
+
+BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
+{
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
+
+  VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+  face.sentInterests.clear();
+
+  processInterest = nullptr; // disable data responses from mocked network
+
+  VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+  face.sentInterests.clear();
+
+  advanceClocks(10_min, 2); // expire untrusted cache
+
+  VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
+  BOOST_CHECK_GT(face.sentInterests.size(), 1);
+  face.sentInterests.clear();
+}
+
+class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
+{
+public:
+  void
+  checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
+              const ValidationContinuation& continueValidation) override
+  {
+    continueValidation(nullptr, state);
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
+                        HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
+{
+  Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+
+  VALIDATE_FAILURE(interest, "Unsigned");
+  VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+  face.sentInterests.clear();
+
+  interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+  m_keyChain.sign(interest, signingWithSha256());
+  m_keyChain.sign(data, signingWithSha256());
+  VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
+  VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+  face.sentInterests.clear();
+
+  m_keyChain.sign(interest, signingByIdentity(identity));
+  m_keyChain.sign(data, signingByIdentity(identity));
+  VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
+  VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+  face.sentInterests.clear();
+
+  m_keyChain.sign(interest, signingByIdentity(subIdentity));
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+  VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
+  VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+  face.sentInterests.clear();
+
+  m_keyChain.sign(interest, signingByIdentity(otherIdentity));
+  m_keyChain.sign(data, signingByIdentity(otherIdentity));
+  VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
+  VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+  // no network operations expected, as certificate is not validated by the policy
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+  face.sentInterests.clear();
+
+  advanceClocks(1_h, 2); // expire trusted cache
+
+  m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
+  m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
+  VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
+  VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+  face.sentInterests.clear();
+}
+
+BOOST_AUTO_TEST_CASE(InfiniteCertChain)
+{
+  processInterest = [this] (const Interest& interest) {
+    // create another key for the same identity and sign it properly
+    Key parentKey = m_keyChain.createKey(subIdentity);
+    Key requestedKey = subIdentity.getKey(interest.getName());
+
+    Name certificateName = requestedKey.getName();
+    certificateName
+    .append("looper")
+    .appendVersion();
+    v2::Certificate certificate;
+    certificate.setName(certificateName);
+
+    // set metainfo
+    certificate.setContentType(tlv::ContentType_Key);
+    certificate.setFreshnessPeriod(1_h);
+
+    // set content
+    certificate.setContent(requestedKey.getPublicKey().data(), requestedKey.getPublicKey().size());
+
+    // set signature-info
+    SignatureInfo info;
+    info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - 10_days,
+                                                    time::system_clock::now() + 10_days));
+
+    m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
+    face.receive(certificate);
+  };
+
+  Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+  m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+  validator.setMaxDepth(40);
+  BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
+  VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
+  face.sentInterests.clear();
+
+  advanceClocks(1_h, 5); // expire caches
+
+  validator.setMaxDepth(30);
+  BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
+  VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
+}
+
+BOOST_AUTO_TEST_CASE(LoopedCertChain)
+{
+  auto s1 = addIdentity("/loop");
+  auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
+  auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
+  auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
+
+  auto makeCert = [this] (Key& key, const Key& signer) {
+    v2::Certificate request = key.getDefaultCertificate();
+    request.setName(Name(key.getName()).append("looper").appendVersion());
+
+    SignatureInfo info;
+    info.setValidityPeriod({time::system_clock::now() - 100_days,
+                            time::system_clock::now() + 100_days});
+    m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
+    m_keyChain.addCertificate(key, request);
+
+    cache.insert(request);
+  };
+
+  makeCert(k1, k2);
+  makeCert(k2, k3);
+  makeCert(k3, k1);
+
+  Data data("/loop/Data");
+  m_keyChain.sign(data, signingByKey(k1));
+  VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidator
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/validator-config.t.cpp b/tests/unit/security/validator-config.t.cpp
new file mode 100644
index 0000000..3e49d24
--- /dev/null
+++ b/tests/unit/security/validator-config.t.cpp
@@ -0,0 +1,159 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/validator-config.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/v2/certificate-fetcher-offline.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "v2/validator-config/common.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestValidatorConfig, IdentityManagementFixture)
+
+// This test only for API, actual tests are in ValidationPolicyConfig and corresponding CertificateFetchers
+
+BOOST_AUTO_TEST_CASE(Construct)
+{
+  util::DummyClientFace face;
+
+  ValidatorConfig v1(face);
+  BOOST_CHECK_EQUAL(v1.m_policyConfig.m_isConfigured, false);
+
+  ValidatorConfig v2(make_unique<v2::CertificateFetcherOffline>());
+  BOOST_CHECK_EQUAL(v2.m_policyConfig.m_isConfigured, false);
+}
+
+class ValidatorConfigFixture : public IdentityManagementFixture
+{
+public:
+  ValidatorConfigFixture()
+    : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "validator-config")
+    , validator(make_unique<v2::CertificateFetcherOffline>())
+  {
+    boost::filesystem::create_directories(path);
+    config = R"CONF(
+        trust-anchor
+        {
+          type any
+        }
+      )CONF";
+    configFile = (this->path / "config.conf").string();
+    std::ofstream f(configFile.c_str());
+    f << config;
+  }
+
+  ~ValidatorConfigFixture()
+  {
+    boost::filesystem::remove_all(path);
+  }
+
+public:
+  const boost::filesystem::path path;
+  std::string config;
+  std::string configFile;
+  ValidatorConfig validator;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Loads, ValidatorConfigFixture)
+
+BOOST_AUTO_TEST_CASE(FromFile)
+{
+  validator.load(configFile);
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+  // should reload policy
+  validator.load(configFile);
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+  validator.load(config, "config-file-from-string");
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+  // should reload policy
+  validator.load(config, "config-file-from-string");
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_CASE(FromIstream)
+{
+  std::istringstream is(config);
+  validator.load(is, "config-file-from-istream");
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+  // should reload policy
+  std::istringstream is2(config);
+  validator.load(is2, "config-file-from-istream");
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_CASE(FromSection)
+{
+  validator.load(v2::validator_config::tests::makeSection(config), "config-file-from-section");
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+  // should reload policy
+  validator.load(v2::validator_config::tests::makeSection(config), "config-file-from-section");
+  BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Loads
+
+
+BOOST_FIXTURE_TEST_CASE(ValidateCommandInterestWithDigestSha256, ValidatorConfigFixture) // Bug 4635
+{
+  validator.load(configFile);
+
+  CommandInterestSigner signer(m_keyChain);
+  auto i = signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+  size_t nValidated = 0, nFailed = 0;
+
+  validator.validate(i, [&] (auto&&...) { ++nValidated; }, [&] (auto&&...) { ++nFailed; });
+  BOOST_CHECK_EQUAL(nValidated, 1);
+  BOOST_CHECK_EQUAL(nFailed, 0);
+
+  validator.validate(i, [&] (auto&&...) { ++nValidated; }, [&] (auto&&...) { ++nFailed; });
+  BOOST_CHECK_EQUAL(nValidated, 1);
+  BOOST_CHECK_EQUAL(nFailed, 1);
+
+  i = signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+  validator.validate(i, [&] (auto&&...) { ++nValidated; }, [&] (auto&&...) { ++nFailed; });
+  BOOST_CHECK_EQUAL(nValidated, 2);
+  BOOST_CHECK_EQUAL(nFailed, 1);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/validator-null.t.cpp b/tests/unit/security/validator-null.t.cpp
new file mode 100644
index 0000000..69cef48
--- /dev/null
+++ b/tests/unit/security/validator-null.t.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/validator-null.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestValidatorNull, IdentityManagementFixture)
+
+BOOST_AUTO_TEST_CASE(ValidateData)
+{
+  auto identity = addIdentity("/TestValidator/Null");
+  Data data("/Some/Other/Data/Name");
+  m_keyChain.sign(data, signingByIdentity(identity));
+
+  ValidatorNull validator;
+  validator.validate(data,
+                     bind([] { BOOST_CHECK_MESSAGE(true, "Validation should succeed"); }),
+                     bind([] { BOOST_CHECK_MESSAGE(false, "Validation should not have failed"); }));
+}
+
+BOOST_AUTO_TEST_CASE(ValidateInterest)
+{
+  auto identity = addIdentity("/TestValidator/Null");
+  Interest interest("/Some/Other/Interest/Name");
+  m_keyChain.sign(interest, signingByIdentity(identity));
+
+  ValidatorNull validator;
+  validator.validate(interest,
+                     bind([] { BOOST_CHECK_MESSAGE(true, "Validation should succeed"); }),
+                     bind([] { BOOST_CHECK_MESSAGE(false, "Validation should not have failed"); }));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidatorNull
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/validity-period.t.cpp b/tests/unit/security/validity-period.t.cpp
new file mode 100644
index 0000000..14d2a0f
--- /dev/null
+++ b/tests/unit/security/validity-period.t.cpp
@@ -0,0 +1,200 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/validity-period.hpp"
+
+#include "boost-test.hpp"
+#include "../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestValidityPeriod)
+
+BOOST_FIXTURE_TEST_CASE(ConstructorSetter, UnitTestTimeFixture)
+{
+  time::system_clock::TimePoint now = this->systemClock->getNow();
+
+  time::system_clock::TimePoint notBefore = now - 1_day;
+  time::system_clock::TimePoint notAfter = notBefore + 2_days;
+
+  ValidityPeriod validity1 = ValidityPeriod(notBefore, notAfter);
+
+  auto period = validity1.getPeriod();
+  BOOST_CHECK_GE(period.first, notBefore); // fractional seconds will be removed
+  BOOST_CHECK_LT(period.first, notBefore + 1_s);
+
+  BOOST_CHECK_LE(period.second, notAfter); // fractional seconds will be removed
+  BOOST_CHECK_GT(period.second, notAfter - 1_s);
+  BOOST_CHECK_EQUAL(validity1.isValid(), true);
+
+  BOOST_CHECK_EQUAL(ValidityPeriod(now - 2_days, now - 1_day).isValid(), false);
+
+  BOOST_CHECK_NO_THROW((ValidityPeriod()));
+  ValidityPeriod validity2;
+  BOOST_CHECK_EQUAL(validity2.isValid(), false);
+
+  validity2.setPeriod(notBefore, notAfter);
+  BOOST_CHECK(validity2.getPeriod() != std::make_pair(time::getUnixEpoch(), time::getUnixEpoch()));
+  BOOST_CHECK_EQUAL(validity2, validity1);
+
+  validity1.setPeriod(time::getUnixEpoch(), time::getUnixEpoch() + 10 * 365_days);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(validity1),
+                    "(19700101T000000, 19791230T000000)");
+
+  validity1.setPeriod(time::getUnixEpoch() + 1_ns,
+                      time::getUnixEpoch() + (10 * 365_days) + 1_ns);
+  BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(validity1),
+                    "(19700101T000001, 19791230T000000)");
+
+  BOOST_CHECK_EQUAL(ValidityPeriod(now, now).isValid(), true);
+  BOOST_CHECK_EQUAL(ValidityPeriod(now + 1_s, now).isValid(), false);
+}
+
+const uint8_t VP1[] = {
+  0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+    0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0xfd, 0x00, 0xff, 0x0f, // NotAfter
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+BOOST_AUTO_TEST_CASE(EncodingDecoding)
+{
+  time::system_clock::TimePoint notBefore = time::getUnixEpoch();
+  time::system_clock::TimePoint notAfter = notBefore + 1_day;
+
+  ValidityPeriod v1(notBefore, notAfter);
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(v1.wireEncode().begin(), v1.wireEncode().end(),
+                                VP1, VP1 + sizeof(VP1));
+
+  BOOST_REQUIRE_NO_THROW(ValidityPeriod(Block(VP1, sizeof(VP1))));
+  Block block(VP1, sizeof(VP1));
+  ValidityPeriod v2(block);
+  BOOST_CHECK(v1.getPeriod() == v2.getPeriod());
+}
+
+const uint8_t VP_E1[] = {
+  0xfd, 0x00, 0xff, 0x26, // ValidityPeriod (error)
+    0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0xfd, 0x00, 0xff, 0x0f, // NotAfter
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E2[] = {
+  0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+    0xfd, 0x00, 0xff, 0x0f, // NotBefore (error)
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0xfd, 0x00, 0xff, 0x0f, // NotAfter
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E3[] = {
+  0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+    0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0xfd, 0x00, 0xfe, 0x0f, // NotAfter (error)
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E4[] = {
+  0xfd, 0x00, 0xfd, 0x39, // ValidityPeriod
+    0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0xfd, 0x00, 0xff, 0x0f, // NotAfter
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0xfd, 0x00, 0xff, 0x0f, // NotAfter (error)
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E5[] = {
+  0xfd, 0x00, 0xfd, 0x13, // ValidityPeriod
+    0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E6[] = {
+  0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+    0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T00000\xFF
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFF,
+    0xfd, 0x00, 0xff, 0x0f, // NotAfter
+      0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+      0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+
+BOOST_AUTO_TEST_CASE(DecodingError)
+{
+  BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E1, sizeof(VP_E1))), ValidityPeriod::Error);
+
+  BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E2, sizeof(VP_E2))), ValidityPeriod::Error);
+  BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E3, sizeof(VP_E3))), ValidityPeriod::Error);
+
+  BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E4, sizeof(VP_E4))), ValidityPeriod::Error);
+  BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E5, sizeof(VP_E5))), ValidityPeriod::Error);
+
+  Block emptyBlock;
+  BOOST_CHECK_THROW((ValidityPeriod(emptyBlock)), ValidityPeriod::Error);
+
+  BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E6, sizeof(VP_E6))), ValidityPeriod::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Comparison)
+{
+  time::system_clock::TimePoint notBefore = time::getUnixEpoch();
+  time::system_clock::TimePoint notAfter = notBefore + 1_day;
+  time::system_clock::TimePoint notAfter2 = notBefore + 2_days;
+
+  ValidityPeriod validity1(notBefore, notAfter);
+  ValidityPeriod validity2(notBefore, notAfter);
+  BOOST_CHECK(validity1 == validity2);
+
+  ValidityPeriod validity3(notBefore, notAfter2);
+  BOOST_CHECK(validity1 != validity3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidityPeriod
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/verification-helpers.t.cpp b/tests/unit/security/verification-helpers.t.cpp
new file mode 100644
index 0000000..d06580a
--- /dev/null
+++ b/tests/unit/security/verification-helpers.t.cpp
@@ -0,0 +1,504 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "security/verification-helpers.hpp"
+#include "security/transform/public-key.hpp"
+// #include "util/string-helper.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "make-interest-data.hpp"
+
+#include <boost/mpl/list.hpp>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestVerificationHelpers)
+
+// // Using this test case to generate dataset if signature format changes
+// BOOST_FIXTURE_TEST_CASE(Generator, IdentityManagementV2Fixture)
+// {
+//   Identity wrongIdentity = addIdentity("/Security/TestVerificationHelpers/Wrong");
+//   std::map<std::string, SigningInfo> identities = {
+//     {"Ecdsa", signingByIdentity(addIdentity("/Security/TestVerificationHelpers/EC", EcKeyParams()))},
+//     {"Rsa", signingByIdentity(addIdentity("/Security/TestVerificationHelpers/RSA", RsaKeyParams()))},
+//     {"Sha256", signingWithSha256()}
+//   };
+
+//   auto print = [] (const std::string& name, const uint8_t* buf, size_t size) {
+//     std::cout << "  std::vector<uint8_t> " + name + " = {\n    ";
+
+//     std::string hex = toHex(buf, size);
+
+//     for (size_t i = 0; i < hex.size(); i++) {
+//       if (i > 0 && i % 32 == 0)
+//         std::cout << "\n    ";
+
+//       std::cout << "0x" << hex[i];
+//       std::cout << hex[++i];
+
+//       if ((i + 1) != hex.size())
+//         std::cout << ", ";
+//     }
+//     std::cout << "\n  };";
+//   };
+
+//   for (const auto& i : identities) {
+//     const std::string& type = i.first;
+//     const SigningInfo& signingInfo = i.second;
+
+//     std::cout << "struct " + type + "Dataset\n{\n";
+//     std::cout << "  const std::string name = \"" << type << "\";\n";
+
+//     if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_ID) {
+//       print("cert", signingInfo.getPibIdentity().getDefaultKey().getDefaultCertificate().wireEncode().wire(),
+//             signingInfo.getPibIdentity().getDefaultKey().getDefaultCertificate().wireEncode().size());
+//     }
+//     else {
+//       print("cert", nullptr, 0);
+//     }
+//     std::cout << "\n";
+
+//     // Create data that can be verified by cert
+//     Data data(Name("/test/data").append(type));
+//     m_keyChain.sign(data, signingInfo);
+//     print("goodData", data.wireEncode().wire(), data.wireEncode().size());
+//     std::cout << "\n";
+
+//     // Create data that cannot be verified by cert
+//     m_keyChain.sign(data, signingByIdentity(wrongIdentity));
+//     print("badSigData", data.wireEncode().wire(), data.wireEncode().size());
+//     std::cout << "\n";
+
+//     // Create interest that can be verified by cert
+//     Interest interest(Name("/test/interest/").append(type));
+//     m_keyChain.sign(interest, signingInfo);
+//     print("goodInterest", interest.wireEncode().wire(), interest.wireEncode().size());
+//     std::cout << "\n";
+
+//     // Create interest that cannot be verified by cert
+//     m_keyChain.sign(interest, signingByIdentity(wrongIdentity));
+//     print("badSigInterest", interest.wireEncode().wire(), interest.wireEncode().size());
+//     std::cout << "\n};\n\n";
+//   }
+// }
+
+struct EcdsaDataset
+{
+  const std::string name = "Ecdsa";
+  std::vector<uint8_t> cert = {
+    0x06, 0xFD, 0x02, 0x59, 0x07, 0x47, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
+    0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+    0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B,
+    0x45, 0x59, 0x08, 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x08, 0x04, 0x73, 0x65,
+    0x6C, 0x66, 0x08, 0x09, 0xFD, 0x00, 0x00, 0x01, 0x59, 0x90, 0x5F, 0x6F, 0x4F, 0x14, 0x09, 0x18,
+    0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B,
+    0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x30, 0x81, 0xF7,
+    0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+    0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+    0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B,
+    0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7,
+    0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D, 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41,
+    0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40,
+    0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+    0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E,
+    0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51,
+    0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2,
+    0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x78, 0xF2, 0x68, 0x89, 0x92,
+    0x8D, 0x84, 0x17, 0xF1, 0x2B, 0x50, 0x4C, 0x9B, 0xFA, 0x6C, 0x4D, 0x8D, 0x29, 0x7D, 0x85, 0xDC,
+    0x03, 0x09, 0x72, 0xFA, 0x06, 0xD4, 0x5C, 0xCB, 0xA6, 0x62, 0x0A, 0x7E, 0x2D, 0x50, 0xF0, 0x07,
+    0xDE, 0xE0, 0x34, 0xF6, 0xC2, 0xAE, 0xA9, 0x32, 0x9B, 0x2C, 0xBD, 0x25, 0xAF, 0xB7, 0xE1, 0x7C,
+    0xCD, 0x85, 0xF4, 0x6D, 0x31, 0xD6, 0xC0, 0x01, 0xC3, 0xEF, 0xB3, 0x16, 0x67, 0x1B, 0x01, 0x03,
+    0x1C, 0x38, 0x07, 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17,
+    0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+    0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B, 0x45, 0x59,
+    0x08, 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0xFD, 0x00, 0xFD, 0x26, 0xFD, 0x00,
+    0xFE, 0x0F, 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+    0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x33, 0x37, 0x30, 0x31, 0x30, 0x37, 0x54, 0x30, 0x31,
+    0x35, 0x31, 0x33, 0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xC3, 0xB4, 0x2A, 0x00, 0x58,
+    0x97, 0x42, 0xDA, 0x54, 0x4C, 0xA6, 0xEF, 0x0C, 0x40, 0x51, 0x88, 0x5C, 0x86, 0x99, 0xF2, 0xBC,
+    0x15, 0xEA, 0x06, 0x64, 0xA5, 0xD5, 0xFF, 0x2B, 0xFA, 0xD1, 0xD3, 0x02, 0x20, 0x35, 0x4C, 0xC0,
+    0x0D, 0x0F, 0x8E, 0x43, 0x56, 0x12, 0x60, 0x8C, 0x98, 0xF1, 0x5F, 0xC4, 0xD5, 0xF2, 0x25, 0x21,
+    0xD4, 0x9C, 0x94, 0x55, 0x0D, 0x4F, 0xDE, 0x14, 0x51, 0x82, 0xB3, 0x8E, 0xA1
+  };
+  std::vector<uint8_t> goodData = {
+    0x06, 0x9E, 0x07, 0x10, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+    0x08, 0x02, 0x45, 0x63, 0x14, 0x00, 0x15, 0x00, 0x16, 0x3D, 0x1B, 0x01, 0x03, 0x1C, 0x38, 0x07,
+    0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73,
+    0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C,
+    0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xDC,
+    0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x17, 0x47, 0x30, 0x45, 0x02, 0x20, 0x29, 0x8B, 0xE5,
+    0x0B, 0xD4, 0x24, 0x92, 0xA7, 0x5A, 0x36, 0x73, 0x5A, 0xC2, 0xBE, 0x17, 0x0D, 0xCA, 0x6C, 0xBB,
+    0xF9, 0x15, 0x60, 0xBD, 0x08, 0x8E, 0xA9, 0x5B, 0x31, 0x94, 0xF2, 0xB7, 0x97, 0x02, 0x21, 0x00,
+    0xA9, 0x02, 0xEF, 0x0F, 0x45, 0xCB, 0xC8, 0x5C, 0xF5, 0xD6, 0xCB, 0x90, 0x8E, 0x42, 0x07, 0xA0,
+    0xA0, 0x34, 0x6A, 0x61, 0xAD, 0x24, 0x9E, 0xB7, 0x73, 0x98, 0xA4, 0x6C, 0x2D, 0xD6, 0x12, 0xE2
+  };
+  std::vector<uint8_t> badSigData = {
+    0x06, 0xA2, 0x07, 0x10, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+    0x08, 0x02, 0x45, 0x63, 0x14, 0x00, 0x15, 0x00, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B, 0x07,
+    0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73,
+    0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C,
+    0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45, 0x59,
+    0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x17, 0x48, 0x30, 0x46, 0x02, 0x21,
+    0x00, 0xB9, 0x4B, 0x40, 0x62, 0xAD, 0xAD, 0xFA, 0x75, 0x69, 0xDE, 0x48, 0x90, 0xC4, 0x11, 0x6B,
+    0xC7, 0x9E, 0x6C, 0x3E, 0x04, 0x78, 0x53, 0xAB, 0xF2, 0x18, 0x16, 0x9F, 0x8D, 0x1A, 0x14, 0x68,
+    0x57, 0x02, 0x21, 0x00, 0xC1, 0xA4, 0xC8, 0x00, 0x0B, 0x61, 0xA0, 0x2C, 0x88, 0x33, 0x7C, 0xE1,
+    0x84, 0xE7, 0xF8, 0xAC, 0x8B, 0x46, 0x19, 0x9C, 0x03, 0xE4, 0xF1, 0xBE, 0x83, 0x09, 0x97, 0xD5,
+    0xA8, 0x98, 0x1A, 0x47
+  };
+  std::vector<uint8_t> goodInterest = {
+    0x05, 0xA9, 0x07, 0xA1, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08, 0x69, 0x6E, 0x74, 0x65,
+    0x72, 0x65, 0x73, 0x74, 0x08, 0x02, 0x45, 0x63, 0x08, 0x3F, 0x16, 0x3D, 0x1B, 0x01, 0x03, 0x1C,
+    0x38, 0x07, 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54,
+    0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48,
+    0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08,
+    0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x08, 0x4A, 0x17, 0x48, 0x30, 0x46, 0x02,
+    0x21, 0x00, 0xE9, 0x35, 0xF0, 0x7B, 0x51, 0xB5, 0x2E, 0xB7, 0xE8, 0x6B, 0xE8, 0xAC, 0xC7, 0xC1,
+    0x90, 0x35, 0x64, 0x21, 0x53, 0x0F, 0x6D, 0x03, 0xB5, 0x1A, 0x58, 0xEA, 0xC4, 0x8A, 0xCA, 0xAF,
+    0xDB, 0x4B, 0x02, 0x21, 0x00, 0x95, 0xF3, 0xEB, 0xF9, 0xF9, 0x66, 0x51, 0x87, 0x97, 0xB0, 0xBF,
+    0xF8, 0x07, 0x5F, 0xF0, 0x70, 0x90, 0x36, 0xD8, 0x57, 0x65, 0xEA, 0xDB, 0x91, 0x79, 0x0E, 0x7E,
+    0x0E, 0xD0, 0x20, 0x96, 0x19, 0x0A, 0x04, 0xF7, 0x2C, 0x8A, 0x4B
+  };
+  std::vector<uint8_t> badSigInterest = {
+    0x05, 0xFD, 0x01, 0x3A, 0x07, 0xFD, 0x01, 0x30, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08,
+    0x69, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x08, 0x02, 0x45, 0x63, 0x08, 0x3F, 0x16, 0x3D,
+    0x1B, 0x01, 0x03, 0x1C, 0x38, 0x07, 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+    0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+    0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03,
+    0x4B, 0x45, 0x59, 0x08, 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x08, 0x4A, 0x17,
+    0x48, 0x30, 0x46, 0x02, 0x21, 0x00, 0xE9, 0x35, 0xF0, 0x7B, 0x51, 0xB5, 0x2E, 0xB7, 0xE8, 0x6B,
+    0xE8, 0xAC, 0xC7, 0xC1, 0x90, 0x35, 0x64, 0x21, 0x53, 0x0F, 0x6D, 0x03, 0xB5, 0x1A, 0x58, 0xEA,
+    0xC4, 0x8A, 0xCA, 0xAF, 0xDB, 0x4B, 0x02, 0x21, 0x00, 0x95, 0xF3, 0xEB, 0xF9, 0xF9, 0x66, 0x51,
+    0x87, 0x97, 0xB0, 0xBF, 0xF8, 0x07, 0x5F, 0xF0, 0x70, 0x90, 0x36, 0xD8, 0x57, 0x65, 0xEA, 0xDB,
+    0x91, 0x79, 0x0E, 0x7E, 0x0E, 0xD0, 0x20, 0x96, 0x19, 0x08, 0x42, 0x16, 0x40, 0x1B, 0x01, 0x03,
+    0x1C, 0x3B, 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17,
+    0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+    0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03,
+    0x4B, 0x45, 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x08, 0x49, 0x17,
+    0x47, 0x30, 0x45, 0x02, 0x20, 0x30, 0x34, 0xB8, 0x9C, 0x33, 0x4C, 0x54, 0x56, 0xF0, 0x34, 0x7A,
+    0x95, 0x72, 0x20, 0xEC, 0xF5, 0x0F, 0xBB, 0x90, 0x3D, 0xC4, 0x21, 0x90, 0xB8, 0x3D, 0xA1, 0x10,
+    0x5A, 0x56, 0x71, 0xC3, 0x3F, 0x02, 0x21, 0x00, 0xAE, 0xE0, 0x60, 0xCF, 0x2C, 0xF7, 0x54, 0xC2,
+    0x1C, 0xC5, 0x54, 0x88, 0xDA, 0x31, 0xD2, 0xDE, 0x53, 0x56, 0xEC, 0xE2, 0xC6, 0x4F, 0xDD, 0xF2,
+    0x78, 0x5D, 0xB3, 0xD4, 0x8E, 0x45, 0x36, 0x23, 0x0A, 0x04, 0xF7, 0x2C, 0x8A, 0x4B
+  };
+};
+
+struct RsaDataset
+{
+  const std::string name = "Rsa";
+  std::vector<uint8_t> cert = {
+    0x06, 0xFD, 0x02, 0xED, 0x07, 0x48, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
+    0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+    0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41, 0x08, 0x03,
+    0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x08, 0x04, 0x73,
+    0x65, 0x6C, 0x66, 0x08, 0x09, 0xFD, 0x00, 0x00, 0x01, 0x59, 0x90, 0x5F, 0x6F, 0x9F, 0x14, 0x09,
+    0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, 0x15, 0xFD, 0x01, 0x26, 0x30, 0x82, 0x01,
+    0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00,
+    0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE7, 0x7B,
+    0x1F, 0x75, 0x3C, 0xCD, 0xB4, 0x62, 0x80, 0xC3, 0xA4, 0x5E, 0xA9, 0x8A, 0x7B, 0xC6, 0x4C, 0x43,
+    0x27, 0x0D, 0xA4, 0x54, 0xEB, 0x67, 0x4F, 0xB4, 0x6E, 0xEF, 0x6D, 0x54, 0x9B, 0x85, 0x44, 0xE9,
+    0x6B, 0x4D, 0x09, 0x31, 0xC0, 0xDC, 0xC5, 0x06, 0x5E, 0x88, 0x6B, 0xFC, 0x0B, 0x08, 0xDE, 0x14,
+    0x0F, 0xAB, 0xE8, 0xA7, 0xED, 0x93, 0x5D, 0x17, 0x19, 0x4B, 0x2D, 0x7D, 0x29, 0xE7, 0x43, 0x42,
+    0x19, 0xE0, 0x3C, 0xBA, 0x8D, 0xAB, 0xE9, 0x4A, 0xBF, 0x21, 0x1E, 0x13, 0xD5, 0x0D, 0x9A, 0xC5,
+    0xD8, 0x67, 0x4A, 0x7F, 0x2D, 0xA6, 0xA9, 0xCE, 0x31, 0x82, 0x62, 0x6B, 0x89, 0xB1, 0x78, 0xE0,
+    0x6E, 0x19, 0x8B, 0xE6, 0x5C, 0x1A, 0x10, 0x8B, 0xC2, 0x9D, 0xF4, 0xB6, 0x66, 0xE9, 0x73, 0xD0,
+    0x93, 0xE9, 0x0A, 0xA8, 0xDA, 0x68, 0xC1, 0x23, 0xBC, 0xBE, 0x17, 0xA0, 0x8E, 0x88, 0xC6, 0x71,
+    0xFF, 0x25, 0x83, 0x75, 0x2C, 0x0E, 0x49, 0x76, 0x27, 0xA0, 0x9E, 0x08, 0x55, 0xA2, 0xE1, 0x60,
+    0xAC, 0x5E, 0x03, 0xC3, 0x9E, 0xF3, 0x2B, 0x56, 0x80, 0xE0, 0x30, 0xD8, 0x0A, 0x5A, 0xAB, 0x92,
+    0xA4, 0x32, 0xF2, 0xEC, 0xE8, 0xFB, 0x6D, 0xE2, 0xE6, 0x2F, 0x87, 0x94, 0xEA, 0xA3, 0x39, 0x47,
+    0x70, 0x71, 0x08, 0xBC, 0x11, 0x0B, 0xC5, 0x9A, 0xCF, 0x13, 0xCA, 0x68, 0x7C, 0x22, 0xF8, 0x33,
+    0xCA, 0x5F, 0xEB, 0x98, 0xF3, 0x29, 0x12, 0xF0, 0x33, 0xDE, 0x30, 0xC4, 0x56, 0xB5, 0x3B, 0x0B,
+    0x0C, 0x23, 0xF2, 0x0F, 0x93, 0x3D, 0x60, 0xC9, 0xD2, 0xAA, 0x5A, 0x94, 0x3E, 0xAB, 0xFB, 0xD5,
+    0x9B, 0xF0, 0xC9, 0x79, 0x11, 0xAD, 0x78, 0x4E, 0xE9, 0x91, 0x1E, 0x62, 0x8D, 0x81, 0x71, 0xC9,
+    0xE4, 0x5D, 0x41, 0xDD, 0x19, 0xC6, 0x77, 0x81, 0xF6, 0xA7, 0x38, 0xD4, 0xE1, 0xB3, 0x02, 0x03,
+    0x01, 0x00, 0x01, 0x16, 0x68, 0x1B, 0x01, 0x01, 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65,
+    0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69,
+    0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08,
+    0x03, 0x52, 0x53, 0x41, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60,
+    0x6F, 0x0F, 0xDF, 0xFD, 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x31, 0x39, 0x37, 0x30, 0x30,
+    0x31, 0x30, 0x31, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30,
+    0x33, 0x37, 0x30, 0x31, 0x30, 0x37, 0x54, 0x30, 0x31, 0x35, 0x31, 0x33, 0x30, 0x17, 0xFD, 0x01,
+    0x00, 0xD1, 0xEB, 0xB4, 0x09, 0x2B, 0x9D, 0xFA, 0x82, 0x88, 0x1A, 0xB6, 0x71, 0x99, 0xD0, 0x14,
+    0x2B, 0xB5, 0xD8, 0xB8, 0x36, 0xA5, 0x70, 0xC0, 0xDD, 0x7E, 0xD1, 0xAD, 0x93, 0xEC, 0xCE, 0x9F,
+    0x3E, 0x75, 0x1F, 0x7F, 0x95, 0x0A, 0x34, 0xCC, 0x8C, 0x5A, 0x45, 0x37, 0xE2, 0x65, 0xE4, 0x1D,
+    0xEC, 0xE2, 0xC2, 0x39, 0x7F, 0xD7, 0xB7, 0x9B, 0x78, 0xDF, 0x0B, 0x39, 0x2B, 0xC6, 0x5E, 0x49,
+    0x6F, 0xDB, 0xB0, 0x44, 0xFD, 0xC7, 0x5B, 0x00, 0xB4, 0xDD, 0x23, 0xBC, 0xA8, 0xC8, 0x9C, 0xCB,
+    0xAD, 0x63, 0x3C, 0x40, 0x57, 0x07, 0x09, 0xC7, 0x26, 0x84, 0xBF, 0x49, 0x8B, 0xD0, 0x5A, 0xFD,
+    0xBA, 0xA0, 0x0C, 0x06, 0xE5, 0x84, 0xA3, 0x9B, 0x36, 0x5E, 0x95, 0xBF, 0x34, 0xF5, 0x5A, 0x70,
+    0x31, 0x3B, 0x70, 0x3E, 0x99, 0x84, 0xBA, 0x03, 0xE7, 0x5A, 0x9D, 0x6F, 0x46, 0x33, 0xA3, 0x95,
+    0xAD, 0xB5, 0xC9, 0x20, 0x28, 0xA8, 0x6E, 0x52, 0x02, 0x97, 0x49, 0xDA, 0x89, 0x55, 0x6B, 0x5D,
+    0xCB, 0x84, 0x75, 0x5F, 0x1F, 0x51, 0x0C, 0x59, 0x49, 0xC8, 0xCE, 0x2D, 0xC3, 0xA2, 0x2F, 0x7F,
+    0x42, 0x71, 0x26, 0x4F, 0xF4, 0x1F, 0xBB, 0x09, 0x7D, 0xF8, 0x7E, 0x6D, 0x44, 0x74, 0xB1, 0x7F,
+    0x76, 0xAE, 0x7B, 0x1C, 0x56, 0x75, 0x9B, 0xE7, 0x23, 0xF4, 0xF3, 0xA0, 0x3C, 0x47, 0xF0, 0x98,
+    0x2B, 0xC1, 0x54, 0xDA, 0x75, 0x1B, 0x2E, 0x94, 0xFB, 0xB5, 0xDD, 0x44, 0xE9, 0x16, 0x27, 0xE5,
+    0xE9, 0xB5, 0xA5, 0x70, 0x5E, 0xBC, 0x48, 0xB1, 0x02, 0x92, 0x64, 0x73, 0x08, 0x8A, 0x5C, 0xD8,
+    0xF7, 0x58, 0x07, 0x66, 0xEF, 0x4A, 0x9E, 0xB5, 0x77, 0xDF, 0xDE, 0x54, 0xF1, 0x0A, 0xDF, 0xE1,
+    0xE1, 0xF1, 0x76, 0x91, 0xEB, 0x78, 0xB6, 0x9A, 0x40, 0x57, 0x97, 0x9C, 0x2C, 0x8C, 0x00, 0x05,
+    0x01
+  };
+  std::vector<uint8_t> goodData = {
+    0x06, 0xFD, 0x01, 0x5B, 0x07, 0x11, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61,
+    0x74, 0x61, 0x08, 0x03, 0x52, 0x73, 0x61, 0x14, 0x00, 0x15, 0x00, 0x16, 0x3E, 0x1B, 0x01, 0x01,
+    0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17,
+    0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+    0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41, 0x08, 0x03, 0x4B, 0x45,
+    0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x17, 0xFD, 0x01, 0x00, 0x88,
+    0xEA, 0x30, 0x2E, 0xF2, 0x40, 0xA4, 0x2F, 0xF6, 0x95, 0x83, 0x1B, 0xD7, 0x89, 0xB2, 0xDB, 0x73,
+    0xB8, 0x88, 0x97, 0x48, 0xDC, 0xC4, 0x8F, 0xBD, 0x83, 0x49, 0x5E, 0x39, 0x14, 0xE5, 0x15, 0xE2,
+    0x94, 0xB3, 0x7A, 0x4A, 0x46, 0x1F, 0xEE, 0x50, 0xF5, 0x1F, 0x46, 0xE9, 0xAB, 0x31, 0x94, 0x9C,
+    0x41, 0xF1, 0xA7, 0x1E, 0xA7, 0x2F, 0x2C, 0x26, 0x5E, 0x97, 0x7D, 0x06, 0x86, 0x77, 0x63, 0x4B,
+    0xD0, 0x38, 0x91, 0x0F, 0xB6, 0x2B, 0xFD, 0xF0, 0x77, 0xB1, 0xA8, 0x73, 0xBB, 0xF9, 0x8A, 0xED,
+    0x8D, 0xDD, 0x32, 0x8E, 0x8A, 0xC2, 0xEE, 0x83, 0xA6, 0x72, 0xFF, 0xDB, 0x91, 0xA8, 0x83, 0x1D,
+    0xDC, 0x37, 0x1F, 0x58, 0xF6, 0x16, 0x5D, 0x89, 0x50, 0xDD, 0x1D, 0x42, 0x96, 0x81, 0x75, 0x94,
+    0xA7, 0x8D, 0x2E, 0x8C, 0xB2, 0x75, 0x36, 0x99, 0x7A, 0x65, 0x34, 0x07, 0xC8, 0x28, 0x98, 0xA2,
+    0x46, 0x9D, 0x6F, 0x30, 0x8E, 0x32, 0x49, 0x20, 0xE6, 0xFC, 0x1A, 0x05, 0x4F, 0x6F, 0xE8, 0x5D,
+    0x34, 0x1D, 0x8D, 0x7F, 0x09, 0xA8, 0xDD, 0xA7, 0x48, 0xB1, 0x14, 0xDC, 0x5A, 0xAF, 0xAA, 0x12,
+    0xEA, 0x57, 0xDE, 0x47, 0x6C, 0xC6, 0x07, 0xBC, 0x6B, 0x76, 0xB1, 0x40, 0x56, 0x3B, 0x12, 0x47,
+    0x1D, 0x89, 0x66, 0x12, 0x81, 0xE8, 0x8B, 0xBA, 0x70, 0x8A, 0x3E, 0x78, 0x64, 0x00, 0x3E, 0x56,
+    0x3B, 0xCF, 0x26, 0xF0, 0x3D, 0x09, 0x1D, 0xB9, 0x22, 0x9E, 0xED, 0x67, 0xB8, 0x86, 0x1F, 0x44,
+    0x92, 0x0A, 0x08, 0xE1, 0x8F, 0x4C, 0x6D, 0x9C, 0x11, 0x5F, 0x26, 0xEA, 0x3E, 0x95, 0x32, 0x13,
+    0x20, 0x51, 0x95, 0x24, 0x37, 0x18, 0x13, 0x92, 0xBD, 0x22, 0xF0, 0x0E, 0xAF, 0x34, 0x37, 0x9B,
+    0xDC, 0x61, 0x03, 0x29, 0x09, 0x9A, 0x45, 0x92, 0x1B, 0xA0, 0x29, 0x9E, 0xD8, 0x98, 0x54
+  };
+  std::vector<uint8_t> badSigData = {
+    0x06, 0xA1, 0x07, 0x11, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+    0x08, 0x03, 0x52, 0x73, 0x61, 0x14, 0x00, 0x15, 0x00, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B,
+    0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65,
+    0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65,
+    0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45,
+    0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x17, 0x46, 0x30, 0x44, 0x02,
+    0x20, 0x5E, 0xF9, 0x02, 0xF4, 0x78, 0xE7, 0x5E, 0x1B, 0xB9, 0x3B, 0x23, 0xDE, 0x9D, 0xB7, 0x87,
+    0xC6, 0x30, 0x7F, 0x4A, 0xE2, 0xBE, 0x11, 0xFE, 0x29, 0xC7, 0x6F, 0x70, 0x97, 0xAF, 0x45, 0xE1,
+    0x0B, 0x02, 0x20, 0x67, 0x45, 0x47, 0x52, 0xBE, 0x13, 0x59, 0x76, 0x16, 0x28, 0x70, 0xF6, 0x50,
+    0x13, 0xB2, 0xC0, 0xFA, 0x8F, 0xF3, 0x05, 0xFF, 0xBC, 0x92, 0xAC, 0xF7, 0xD0, 0x12, 0x3A, 0x6E,
+    0x31, 0x76, 0x02
+  };
+  std::vector<uint8_t> goodInterest = {
+    0x05, 0xFD, 0x01, 0x69, 0x07, 0xFD, 0x01, 0x5F, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08,
+    0x69, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x08, 0x03, 0x52, 0x73, 0x61, 0x08, 0x40, 0x16,
+    0x3E, 0x1B, 0x01, 0x01, 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+    0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61,
+    0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41,
+    0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x08,
+    0xFD, 0x01, 0x04, 0x17, 0xFD, 0x01, 0x00, 0x9C, 0x44, 0x23, 0xF4, 0x3A, 0x9F, 0xDD, 0x62, 0xD2,
+    0x47, 0xE4, 0x73, 0x9E, 0x58, 0x54, 0xF5, 0xE7, 0x72, 0x8D, 0x2E, 0x1B, 0x26, 0xE7, 0xA2, 0xA1,
+    0x56, 0x23, 0xBD, 0xB8, 0x75, 0x17, 0x78, 0x01, 0x67, 0xF9, 0x4D, 0xA9, 0xCC, 0xB8, 0x00, 0xF6,
+    0xCD, 0xC4, 0xB9, 0xF6, 0x50, 0xC7, 0xAB, 0x57, 0xD8, 0xA7, 0x8B, 0xA8, 0x5C, 0xED, 0xD0, 0xCC,
+    0x29, 0xC3, 0x5D, 0x80, 0x2B, 0xFA, 0xAF, 0x0D, 0xCB, 0x29, 0x1E, 0x74, 0xA8, 0x41, 0x80, 0xDE,
+    0x52, 0x94, 0xDD, 0xE8, 0xAA, 0xA9, 0x61, 0x83, 0xC1, 0x5F, 0xA3, 0x11, 0x48, 0x0B, 0xB6, 0x53,
+    0xB8, 0xE3, 0x77, 0x6A, 0xED, 0xF0, 0xFA, 0xED, 0x79, 0x43, 0x10, 0x10, 0x79, 0x98, 0x5D, 0xFD,
+    0x66, 0xBF, 0x2F, 0x14, 0x9F, 0x7D, 0xA4, 0x3C, 0xBA, 0x67, 0x5F, 0xDB, 0xE3, 0x67, 0x13, 0x96,
+    0x60, 0xC6, 0x69, 0x78, 0x5A, 0x8D, 0x52, 0xB7, 0xB7, 0x6B, 0x7F, 0xEE, 0xF4, 0x22, 0x3A, 0x64,
+    0xE4, 0xB4, 0xA1, 0x8B, 0xDD, 0x3F, 0x80, 0xCD, 0xF4, 0x9E, 0x92, 0x06, 0x98, 0x23, 0x47, 0x58,
+    0x70, 0xF0, 0xAC, 0x79, 0x76, 0x91, 0x7A, 0x78, 0xDF, 0xAD, 0xDD, 0x81, 0x30, 0x01, 0x5D, 0xCE,
+    0x37, 0xEC, 0x7E, 0xDA, 0xDA, 0x36, 0x75, 0x50, 0x52, 0x57, 0x95, 0xBF, 0xCF, 0x3A, 0xC4, 0x9F,
+    0x52, 0x97, 0x17, 0x15, 0x99, 0xA5, 0x2F, 0x68, 0x35, 0x91, 0x70, 0xDE, 0x98, 0x8A, 0xB0, 0x5F,
+    0xF4, 0x63, 0x14, 0xB9, 0xCC, 0x76, 0x81, 0x87, 0xAE, 0x10, 0x8E, 0x9F, 0xEC, 0xCB, 0xF2, 0x33,
+    0x1D, 0x50, 0xD4, 0xAB, 0x5B, 0xBB, 0xB9, 0x7F, 0x8C, 0xAD, 0xEC, 0xE3, 0xF8, 0xE1, 0x63, 0xDA,
+    0x4E, 0x0D, 0x17, 0x28, 0xCD, 0x8D, 0x16, 0x00, 0x22, 0x4A, 0x51, 0x5C, 0xB2, 0x8C, 0xE7, 0x4B,
+    0x3B, 0x00, 0x16, 0x92, 0xAD, 0x3A, 0xAB, 0x0A, 0x04, 0x10, 0x04, 0xFB, 0x38
+  };
+  std::vector<uint8_t> badSigInterest = {
+    0x05, 0xFD, 0x01, 0xF7, 0x07, 0xFD, 0x01, 0xED, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08,
+    0x69, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x08, 0x03, 0x52, 0x73, 0x61, 0x08, 0x40, 0x16,
+    0x3E, 0x1B, 0x01, 0x01, 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+    0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61,
+    0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41,
+    0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x08,
+    0xFD, 0x01, 0x04, 0x17, 0xFD, 0x01, 0x00, 0x9C, 0x44, 0x23, 0xF4, 0x3A, 0x9F, 0xDD, 0x62, 0xD2,
+    0x47, 0xE4, 0x73, 0x9E, 0x58, 0x54, 0xF5, 0xE7, 0x72, 0x8D, 0x2E, 0x1B, 0x26, 0xE7, 0xA2, 0xA1,
+    0x56, 0x23, 0xBD, 0xB8, 0x75, 0x17, 0x78, 0x01, 0x67, 0xF9, 0x4D, 0xA9, 0xCC, 0xB8, 0x00, 0xF6,
+    0xCD, 0xC4, 0xB9, 0xF6, 0x50, 0xC7, 0xAB, 0x57, 0xD8, 0xA7, 0x8B, 0xA8, 0x5C, 0xED, 0xD0, 0xCC,
+    0x29, 0xC3, 0x5D, 0x80, 0x2B, 0xFA, 0xAF, 0x0D, 0xCB, 0x29, 0x1E, 0x74, 0xA8, 0x41, 0x80, 0xDE,
+    0x52, 0x94, 0xDD, 0xE8, 0xAA, 0xA9, 0x61, 0x83, 0xC1, 0x5F, 0xA3, 0x11, 0x48, 0x0B, 0xB6, 0x53,
+    0xB8, 0xE3, 0x77, 0x6A, 0xED, 0xF0, 0xFA, 0xED, 0x79, 0x43, 0x10, 0x10, 0x79, 0x98, 0x5D, 0xFD,
+    0x66, 0xBF, 0x2F, 0x14, 0x9F, 0x7D, 0xA4, 0x3C, 0xBA, 0x67, 0x5F, 0xDB, 0xE3, 0x67, 0x13, 0x96,
+    0x60, 0xC6, 0x69, 0x78, 0x5A, 0x8D, 0x52, 0xB7, 0xB7, 0x6B, 0x7F, 0xEE, 0xF4, 0x22, 0x3A, 0x64,
+    0xE4, 0xB4, 0xA1, 0x8B, 0xDD, 0x3F, 0x80, 0xCD, 0xF4, 0x9E, 0x92, 0x06, 0x98, 0x23, 0x47, 0x58,
+    0x70, 0xF0, 0xAC, 0x79, 0x76, 0x91, 0x7A, 0x78, 0xDF, 0xAD, 0xDD, 0x81, 0x30, 0x01, 0x5D, 0xCE,
+    0x37, 0xEC, 0x7E, 0xDA, 0xDA, 0x36, 0x75, 0x50, 0x52, 0x57, 0x95, 0xBF, 0xCF, 0x3A, 0xC4, 0x9F,
+    0x52, 0x97, 0x17, 0x15, 0x99, 0xA5, 0x2F, 0x68, 0x35, 0x91, 0x70, 0xDE, 0x98, 0x8A, 0xB0, 0x5F,
+    0xF4, 0x63, 0x14, 0xB9, 0xCC, 0x76, 0x81, 0x87, 0xAE, 0x10, 0x8E, 0x9F, 0xEC, 0xCB, 0xF2, 0x33,
+    0x1D, 0x50, 0xD4, 0xAB, 0x5B, 0xBB, 0xB9, 0x7F, 0x8C, 0xAD, 0xEC, 0xE3, 0xF8, 0xE1, 0x63, 0xDA,
+    0x4E, 0x0D, 0x17, 0x28, 0xCD, 0x8D, 0x16, 0x00, 0x22, 0x4A, 0x51, 0x5C, 0xB2, 0x8C, 0xE7, 0x4B,
+    0x3B, 0x00, 0x16, 0x92, 0xAD, 0x3A, 0xAB, 0x08, 0x42, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B,
+    0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65,
+    0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65,
+    0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45,
+    0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x08, 0x48, 0x17, 0x46, 0x30,
+    0x44, 0x02, 0x20, 0x48, 0x87, 0xFE, 0x7D, 0x43, 0x35, 0x3C, 0x55, 0xCB, 0x4A, 0x4B, 0x83, 0x50,
+    0xC2, 0x10, 0xAD, 0x01, 0x4A, 0x99, 0xED, 0x6C, 0x29, 0x38, 0xEF, 0xE4, 0x9E, 0x10, 0x23, 0x4D,
+    0x57, 0xC3, 0xE4, 0x02, 0x20, 0x7B, 0x35, 0xDC, 0xF2, 0x98, 0xD8, 0xFE, 0x13, 0x4D, 0x3B, 0x5D,
+    0xE7, 0xCE, 0xFF, 0x02, 0xBD, 0x6B, 0x50, 0x30, 0x8B, 0x93, 0x91, 0x7A, 0xC9, 0xE0, 0x95, 0x21,
+    0x5F, 0x91, 0xB6, 0xEE, 0x4E, 0x0A, 0x04, 0x10, 0x04, 0xFB, 0x38
+  };
+};
+
+struct Sha256Dataset
+{
+  const std::string name = "Sha256";
+  std::vector<uint8_t> cert = {
+
+  };
+  std::vector<uint8_t> goodData = {
+    0x06, 0x41, 0x07, 0x14, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+    0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x14, 0x00, 0x15, 0x00, 0x16, 0x03, 0x1B, 0x01,
+    0x00, 0x17, 0x20, 0xE2, 0xE2, 0x2F, 0x02, 0x70, 0xA7, 0xF7, 0x48, 0x70, 0x45, 0x29, 0x46, 0xBD,
+    0xD2, 0x62, 0x24, 0xA6, 0x1E, 0x1D, 0x75, 0x2A, 0x26, 0x98, 0x04, 0xAD, 0x9C, 0x47, 0x63, 0xF8,
+    0x98, 0x5A, 0x49
+  };
+  std::vector<uint8_t> badSigData = {
+    0x06, 0xA5, 0x07, 0x14, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+    0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x14, 0x00, 0x15, 0x00, 0x16, 0x40, 0x1B, 0x01,
+    0x03, 0x1C, 0x3B, 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08,
+    0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F,
+    0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08,
+    0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x17, 0x47,
+    0x30, 0x45, 0x02, 0x20, 0x5F, 0x5E, 0x1E, 0xC0, 0xB5, 0xAC, 0x13, 0xF9, 0x51, 0x2F, 0x22, 0x33,
+    0xFB, 0xDE, 0x57, 0xF6, 0xC8, 0xBF, 0xAE, 0x55, 0x3A, 0xDC, 0x30, 0x8A, 0x12, 0x61, 0xB3, 0x5D,
+    0xB9, 0x31, 0x95, 0xD3, 0x02, 0x21, 0x00, 0xFA, 0xEC, 0x54, 0xEB, 0x35, 0x4D, 0xBF, 0x87, 0x4C,
+    0xD8, 0x20, 0x3A, 0xE5, 0x05, 0x2C, 0xA1, 0x70, 0x74, 0x2E, 0xF9, 0x1E, 0xE1, 0xEF, 0xB9, 0x47,
+    0xC4, 0x53, 0x57, 0xED, 0xB5, 0xB7, 0x60
+  };
+  std::vector<uint8_t> goodInterest = {
+    0x05, 0x4B, 0x07, 0x43, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08, 0x69, 0x6E, 0x74, 0x65,
+    0x72, 0x65, 0x73, 0x74, 0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x08, 0x05, 0x16, 0x03,
+    0x1B, 0x01, 0x00, 0x08, 0x22, 0x17, 0x20, 0x38, 0x65, 0xB5, 0x37, 0x8A, 0xF4, 0xEB, 0xB9, 0xCD,
+    0xEF, 0x18, 0x49, 0xF5, 0x79, 0x85, 0xE5, 0x76, 0x6F, 0x3C, 0x72, 0x63, 0x0E, 0x4F, 0x5D, 0xC7,
+    0x42, 0x6B, 0xDF, 0xB0, 0xE1, 0x75, 0x2C, 0x0A, 0x04, 0xEE, 0x0A, 0x69, 0x16
+  };
+  std::vector<uint8_t> badSigInterest = {
+    0x05, 0xD9, 0x07, 0xD1, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08, 0x69, 0x6E, 0x74, 0x65,
+    0x72, 0x65, 0x73, 0x74, 0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x08, 0x05, 0x16, 0x03,
+    0x1B, 0x01, 0x00, 0x08, 0x22, 0x17, 0x20, 0x38, 0x65, 0xB5, 0x37, 0x8A, 0xF4, 0xEB, 0xB9, 0xCD,
+    0xEF, 0x18, 0x49, 0xF5, 0x79, 0x85, 0xE5, 0x76, 0x6F, 0x3C, 0x72, 0x63, 0x0E, 0x4F, 0x5D, 0xC7,
+    0x42, 0x6B, 0xDF, 0xB0, 0xE1, 0x75, 0x2C, 0x08, 0x42, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B,
+    0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65,
+    0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65,
+    0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45,
+    0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x08, 0x48, 0x17, 0x46, 0x30,
+    0x44, 0x02, 0x20, 0x73, 0xB7, 0x0E, 0x17, 0x6C, 0x98, 0xB5, 0x6B, 0x25, 0x99, 0x2C, 0x6E, 0x41,
+    0x26, 0xE6, 0x08, 0xCF, 0x81, 0xB9, 0x51, 0x53, 0x6A, 0x6B, 0x21, 0xF3, 0x2D, 0x4D, 0x62, 0x53,
+    0x86, 0x85, 0xEE, 0x02, 0x20, 0x7D, 0x9D, 0xFF, 0xE3, 0x18, 0xF7, 0xBD, 0x7F, 0x9B, 0xC6, 0x4D,
+    0x76, 0x09, 0x58, 0x74, 0x69, 0x67, 0x9B, 0x51, 0xBC, 0x14, 0xF0, 0x1C, 0x46, 0xA7, 0xA3, 0xA7,
+    0xCC, 0x9A, 0xBB, 0x33, 0x07, 0x0A, 0x04, 0xEE, 0x0A, 0x69, 0x16
+  };
+};
+
+// Note about the datasets:
+// - .cert a valid certificate
+// - .goodData is a data packet that can be verified by .cert
+// - .badSigData a valid and signed data packet that cannot be verified by cert (signed by a
+//   different private key)
+// - .goodInterest is an interest packet that can be verified by .cert
+// - .badSigInterest a valid and signed interest packet that cannot be verified by cert
+//   (signed by a different private key)
+
+typedef boost::mpl::list<EcdsaDataset, RsaDataset> SignatureDatasets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(VerifySignature, Dataset, SignatureDatasets)
+{
+  Dataset dataset;
+  v2::Certificate cert(Block(dataset.cert.data(), dataset.cert.size()));
+  Buffer keyRaw = cert.getPublicKey();
+  v2::PublicKey key;
+  key.loadPkcs8(keyRaw.data(), keyRaw.size());
+  Data data(Block(dataset.goodData.data(), dataset.goodData.size()));
+  Data badSigData(Block(dataset.badSigData.data(), dataset.badSigData.size()));
+  Interest interest(Block(dataset.goodInterest.data(), dataset.goodInterest.size()));
+  Interest badSigInterest(Block(dataset.badSigInterest.data(), dataset.badSigInterest.size()));
+
+  BOOST_CHECK(verifySignature(data, key));
+  BOOST_CHECK(verifySignature(data, keyRaw.data(), keyRaw.size()));
+  BOOST_CHECK(verifySignature(data, cert));
+  BOOST_CHECK(verifySignature(interest, key));
+  BOOST_CHECK(verifySignature(interest, keyRaw.data(), keyRaw.size()));
+  BOOST_CHECK(verifySignature(interest, cert));
+
+  BOOST_CHECK(!verifySignature(badSigData, key));
+  BOOST_CHECK(!verifySignature(badSigData, keyRaw.data(), keyRaw.size()));
+  BOOST_CHECK(!verifySignature(badSigData, cert));
+  BOOST_CHECK(!verifySignature(badSigInterest, key));
+  BOOST_CHECK(!verifySignature(badSigInterest, keyRaw.data(), keyRaw.size()));
+  BOOST_CHECK(!verifySignature(badSigInterest, cert));
+
+  Data unsignedData("/some/data");
+  Interest unsignedInterest1("/some/interest/with/several/name/components");
+  Interest unsignedInterest2("/interest-with-one-name-component");
+
+  BOOST_CHECK(!verifySignature(unsignedData, cert));
+  BOOST_CHECK(!verifySignature(unsignedData, key));
+  BOOST_CHECK(!verifySignature(unsignedInterest1, cert));
+  BOOST_CHECK(!verifySignature(unsignedInterest1, key));
+  BOOST_CHECK(!verifySignature(unsignedInterest2, cert));
+  BOOST_CHECK(!verifySignature(unsignedInterest2, key));
+
+  uint8_t invalidKey[] = {0x00, 0x00};
+  BOOST_CHECK(!verifySignature(unsignedData, invalidKey, sizeof(invalidKey)));
+  BOOST_CHECK(!verifySignature(unsignedInterest1, invalidKey, sizeof(invalidKey)));
+
+  // - base version of verifySignature is tested transitively
+  // - pib::Key version is tested as part of v2/key-chain.t.cpp (Security/V2/TestKeyChain)
+}
+
+typedef boost::mpl::list<Sha256Dataset> DigestDatasets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(VerifyDigest, Dataset, DigestDatasets)
+{
+  Dataset dataset;
+  Data data(Block(dataset.goodData.data(), dataset.goodData.size()));
+  Data badSigData(Block(dataset.badSigData.data(), dataset.badSigData.size()));
+  Interest interest(Block(dataset.goodInterest.data(), dataset.goodInterest.size()));
+  Interest badSigInterest(Block(dataset.badSigInterest.data(), dataset.badSigInterest.size()));
+
+  BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
+  BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
+
+  BOOST_CHECK(!verifyDigest(badSigData, DigestAlgorithm::SHA256));
+  BOOST_CHECK(!verifyDigest(badSigInterest, DigestAlgorithm::SHA256));
+
+  Data unsignedData("/some/data");
+  Interest unsignedInterest1("/some/interest/with/several/name/components");
+  Interest unsignedInterest2("/interest-with-one-name-component");
+
+  BOOST_CHECK(!verifyDigest(unsignedData, DigestAlgorithm::SHA256));
+  BOOST_CHECK(!verifyDigest(unsignedInterest1, DigestAlgorithm::SHA256));
+  BOOST_CHECK(!verifyDigest(unsignedInterest2, DigestAlgorithm::SHA256));
+
+  // - base version of verifyDigest is tested transitively
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestVerificationHelpers
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn