blob: 9439fcce226f059460f99a60c45935407d80c808 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2020, Regents of the University of California
*
* NAC 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.
*
* NAC 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 NAC library authors and contributors.
*/
#include "encryptor.hpp"
#include "tests/boost-test.hpp"
#include "tests/io-key-chain-fixture.hpp"
#include "tests/unit/static-data.hpp"
#include <iostream>
#include <ndn-cxx/util/dummy-client-face.hpp>
#include <ndn-cxx/util/string-helper.hpp>
namespace ndn {
namespace nac {
namespace tests {
class EncryptorStaticDataEnvironment : public IoKeyChainFixture
{
public:
EncryptorStaticDataEnvironment(bool shouldPublishData)
{
if (shouldPublishData) {
publishData();
}
auto serveFromIms = [this] (const Name&, const Interest& interest) {
auto data = m_ims.find(interest);
if (data != nullptr) {
m_imsFace.put(*data);
}
};
m_imsFace.setInterestFilter("/", serveFromIms, [] (auto...) {});
advanceClocks(1_ms, 10);
m_imsFace.sentData.clear();
m_imsFace.sentInterests.clear();
}
void
publishData()
{
StaticData data;
for (const auto& block : data.managerPackets) {
m_ims.insert(*make_shared<Data>(block));
}
advanceClocks(1_ms, 10);
}
protected:
util::DummyClientFace m_imsFace{m_io, m_keyChain, {true, true}};
private:
InMemoryStoragePersistent m_ims;
};
template<bool shouldPublishData = true>
class EncryptorFixture : public EncryptorStaticDataEnvironment
{
public:
EncryptorFixture()
: EncryptorStaticDataEnvironment(shouldPublishData)
, face(m_io, m_keyChain, {true, true})
, encryptor("/access/policy/identity/NAC/dataset", "/some/ck/prefix", signingWithSha256(),
[=] (const ErrorCode& code, const std::string& error) {
onFailure(code, error);
},
validator, m_keyChain, face)
{
face.linkTo(m_imsFace);
advanceClocks(1_ms, 10);
}
public:
util::DummyClientFace face;
ValidatorNull validator;
Encryptor encryptor;
util::Signal<EncryptorFixture, ErrorCode, std::string> onFailure;
};
BOOST_FIXTURE_TEST_SUITE(TestEncryptor, EncryptorFixture<>)
BOOST_AUTO_TEST_CASE(EncryptAndPublishedCk)
{
encryptor.m_kek.reset();
BOOST_CHECK_EQUAL(encryptor.m_isKekRetrievalInProgress, false);
encryptor.regenerateCk();
BOOST_CHECK_EQUAL(encryptor.m_isKekRetrievalInProgress, true);
std::string plaintext = "Data to encrypt";
auto block = encryptor.encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size());
EncryptedContent content(block);
auto ckPrefix = content.getKeyLocator();
BOOST_CHECK_EQUAL(ckPrefix.getPrefix(-1), "/some/ck/prefix/CK");
BOOST_CHECK(content.hasIv());
BOOST_CHECK_NE(std::string(reinterpret_cast<const char*>(content.getPayload().value()),
content.getPayload().value_size()),
plaintext);
advanceClocks(1_ms, 10);
// check that KEK interests has been sent
BOOST_CHECK_EQUAL(face.sentInterests.at(0).getName().getPrefix(6),
Name("/access/policy/identity/NAC/dataset/KEK"));
auto kek = m_imsFace.sentData.at(0);
BOOST_CHECK_EQUAL(kek.getName().getPrefix(6), Name("/access/policy/identity/NAC/dataset/KEK"));
BOOST_CHECK_EQUAL(kek.getName().size(), 7);
face.sentData.clear();
face.sentInterests.clear();
face.receive(Interest(ckPrefix)
.setCanBePrefix(true).setMustBeFresh(true));
advanceClocks(1_ms, 10);
auto ckName = face.sentData.at(0).getName();
BOOST_CHECK_EQUAL(ckName.getPrefix(4), "/some/ck/prefix/CK");
BOOST_CHECK_EQUAL(ckName.get(5), name::Component("ENCRYPTED-BY"));
auto extractedKek = ckName.getSubName(6);
BOOST_CHECK_EQUAL(extractedKek, kek.getName());
BOOST_CHECK_EQUAL(encryptor.m_isKekRetrievalInProgress, false);
}
BOOST_FIXTURE_TEST_CASE(KekRetrievalFailure, EncryptorFixture<false>)
{
size_t nErrors = 0;
onFailure.connect([&] (auto&&...) { ++nErrors; });
std::string plaintext = "Data to encrypt";
auto block = encryptor.encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size());
advanceClocks(1_ms, 10);
// check that KEK interests has been sent
BOOST_CHECK_EQUAL(face.sentInterests.at(0).getName().getPrefix(6), Name("/access/policy/identity/NAC/dataset/KEK"));
// and failed
BOOST_CHECK_EQUAL(m_imsFace.sentData.size(), 0);
advanceClocks(1_s, 13); // 4_s default interest lifetime x 3
BOOST_CHECK_EQUAL(nErrors, 1);
BOOST_CHECK_EQUAL(m_imsFace.sentData.size(), 0);
advanceClocks(1_s, 730); // 60 seconds between attempts + ~12 seconds for each attempt
BOOST_CHECK_EQUAL(nErrors, 11);
BOOST_CHECK_EQUAL(m_imsFace.sentData.size(), 0);
// check recovery
publishData();
advanceClocks(1_s, 73);
auto kek = m_imsFace.sentData.at(0);
BOOST_CHECK_EQUAL(kek.getName().getPrefix(6), Name("/access/policy/identity/NAC/dataset/KEK"));
BOOST_CHECK_EQUAL(kek.getName().size(), 7);
}
BOOST_AUTO_TEST_CASE(EnumerateDataFromIms)
{
encryptor.regenerateCk();
advanceClocks(1_ms, 10);
encryptor.regenerateCk();
advanceClocks(1_ms, 10);
BOOST_CHECK_EQUAL(encryptor.size(), 3);
size_t nCk = 0;
for (const auto& data : encryptor) {
BOOST_TEST_MESSAGE(data.getName());
if (data.getName().getPrefix(4) == Name("/some/ck/prefix/CK")) {
++nCk;
}
}
BOOST_CHECK_EQUAL(nCk, 3);
}
BOOST_AUTO_TEST_CASE(GenerateTestData,
* ut::description("regenerates the static test data used by other test cases")
* ut::disabled())
{
const auto plaintext = "Data to encrypt"s;
std::cerr << "const std::vector<Block> encryptedBlobs = {\n";
for (size_t i = 0; i < 3; ++i) {
std::cerr << " \"";
auto block = encryptor.encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size());
printHex(std::cerr, block.wireEncode().wire(), block.wireEncode().size(), true);
std::cerr << "\"_block,\n";
encryptor.regenerateCk();
advanceClocks(1_ms, 10);
}
std::cerr << "};\n\n";
std::cerr << "const std::vector<Block> encryptorPackets = {\n";
for (const auto& data : encryptor) {
std::cerr << " \"";
printHex(std::cerr, data.wireEncode().wire(), data.wireEncode().size(), true);
std::cerr << "\"_block,\n";
}
std::cerr << "};\n\n";
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests
} // namespace nac
} // namespace ndn