blob: f7f39ce44cf261e72fad153e6117197ac983469b [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California
*
* This file is part of ndn-group-encrypt (Group-based Encryption Protocol for NDN).
* See AUTHORS.md for complete list of ndn-group-encrypt authors and contributors.
*
* ndn-group-encrypt is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* ndn-group-encrypt 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* ndn-group-encrypt, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*
* @author Zhiyi Zhang <dreamerbarrychang@gmail.com>
* @author Yingdi Yu <yingdi@cs.ucla.edu>
*/
#include "consumer.hpp"
#include "boost-test.hpp"
#include "algo/encryptor.hpp"
#include "unit-test-time-fixture.hpp"
#include <ndn-cxx/security/key-chain.hpp>
#include <ndn-cxx/util/dummy-client-face.hpp>
#include <ndn-cxx/util/time-unit-test-clock.hpp>
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
namespace ndn {
namespace gep {
namespace tests {
static const uint8_t DATA_CONTEN[] = {
0xcb, 0xe5, 0x6a, 0x80, 0x41, 0x24, 0x58, 0x23,
0x84, 0x14, 0x15, 0x61, 0x80, 0xb9, 0x5e, 0xbd,
0xce, 0x32, 0xb4, 0xbe, 0xbc, 0x91, 0x31, 0xd6,
0x19, 0x00, 0x80, 0x8b, 0xfa, 0x00, 0x05, 0x9c
};
static const uint8_t AES_KEY[] = {
0xdd, 0x60, 0x77, 0xec, 0xa9, 0x6b, 0x23, 0x1b,
0x40, 0x6b, 0x5a, 0xf8, 0x7d, 0x3d, 0x55, 0x32
};
static const uint8_t IV[] = {
0x73, 0x6f, 0x6d, 0x65, 0x72, 0x61, 0x6e, 0x64,
0x6f, 0x6d, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72
};
class ConsumerFixture : public UnitTestTimeFixture
{
public:
ConsumerFixture()
: tmpPath(boost::filesystem::path(TMP_TESTS_PATH))
, face1(io, keyChain, {true, true})
, face2(io, keyChain, {true, true})
, readInterestOffset1(0)
, readDataOffset1(0)
, readInterestOffset2(0)
, readDataOffset2(0)
, groupName("/Prefix/READ")
, contentName("/Prefix/SAMPLE/Content")
, cKeyName("/Prefix/SAMPLE/Content/C-KEY/1")
, eKeyName("/Prefix/READ/E-KEY/1/2")
, dKeyName("/Prefix/READ/D-KEY/1/2")
, uKeyName("/U/Key")
, uName("/U")
{
boost::filesystem::create_directories(tmpPath);
// generate e/d key
RandomNumberGenerator rng;
RsaKeyParams params;
fixtureDKeyBuf = algo::Rsa::generateKey(rng, params).getKeyBits();
fixtureEKeyBuf = algo::Rsa::deriveEncryptKey(fixtureDKeyBuf).getKeyBits();
// generate user key
fixtureUDKeyBuf = algo::Rsa::generateKey(rng, params).getKeyBits();
fixtureUEKeyBuf = algo::Rsa::deriveEncryptKey(fixtureUDKeyBuf).getKeyBits();
// load C-KEY
fixtureCKeyBuf = Buffer(AES_KEY, sizeof(AES_KEY));
}
~ConsumerFixture()
{
boost::filesystem::remove_all(tmpPath);
}
shared_ptr<Data>
createEncryptedContent()
{
shared_ptr<Data> contentData = make_shared<Data>(contentName);
algo::EncryptParams eparams(tlv::AlgorithmAesCbc);
eparams.setIV(IV, sizeof(IV));
algo::encryptData(*contentData, DATA_CONTEN, sizeof(DATA_CONTEN), cKeyName,
fixtureCKeyBuf.buf(), fixtureCKeyBuf.size(), eparams);
keyChain.sign(*contentData);
return contentData;
}
shared_ptr<Data>
createEncryptedCKey()
{
shared_ptr<Data> cKeyData = make_shared<Data>(cKeyName);
algo::EncryptParams eparams(tlv::AlgorithmRsaOaep);
algo::encryptData(*cKeyData, fixtureCKeyBuf.buf(), fixtureCKeyBuf.size(), dKeyName,
fixtureEKeyBuf.buf(), fixtureEKeyBuf.size(), eparams);
keyChain.sign(*cKeyData);
return cKeyData;
}
shared_ptr<Data>
createEncryptedDKey()
{
shared_ptr<Data> dKeyData = make_shared<Data>(dKeyName);
algo::EncryptParams eparams(tlv::AlgorithmRsaOaep);
algo::encryptData(*dKeyData, fixtureDKeyBuf.buf(), fixtureDKeyBuf.size(), uKeyName,
fixtureUEKeyBuf.buf(), fixtureUEKeyBuf.size(), eparams);
keyChain.sign(*dKeyData);
return dKeyData;
}
bool
passPacket()
{
bool hasPassed = false;
checkFace(face1.sentInterests, readInterestOffset1, face2, hasPassed);
checkFace(face1.sentData, readDataOffset1, face2, hasPassed);
checkFace(face2.sentInterests, readInterestOffset2, face1, hasPassed);
checkFace(face2.sentData, readDataOffset2, face1, hasPassed);
return hasPassed;
}
template<typename Packet>
void
checkFace(std::vector<Packet>& receivedPackets,
size_t& readPacketOffset,
util::DummyClientFace& receiver,
bool& hasPassed)
{
while (receivedPackets.size() > readPacketOffset) {
receiver.receive(receivedPackets[readPacketOffset]);
readPacketOffset++;
hasPassed = true;
}
}
public:
boost::filesystem::path tmpPath;
util::DummyClientFace face1;
util::DummyClientFace face2;
size_t readInterestOffset1;
size_t readDataOffset1;
size_t readInterestOffset2;
size_t readDataOffset2;
KeyChain keyChain;
Buffer fixtureCKeyBuf;
Buffer fixtureEKeyBuf;
Buffer fixtureDKeyBuf;
Buffer fixtureUEKeyBuf;
Buffer fixtureUDKeyBuf;
Name groupName;
Name contentName;
Name cKeyName;
Name eKeyName;
Name dKeyName;
Name uKeyName;
Name uName;
};
BOOST_FIXTURE_TEST_SUITE(TestConsumer, ConsumerFixture)
BOOST_AUTO_TEST_CASE(DecryptContent)
{
std::string dbDir = tmpPath.c_str();
dbDir += "/test.db";
// generate AES key pairs
Buffer aesKeyBuf = Buffer(AES_KEY, sizeof(AES_KEY));
// generate C-KEY packet
auto cKeyData = createEncryptedCKey();
// generate Content packet
auto contentData = createEncryptedContent();
// create consumer
Consumer consumer(face1, Name("/Group"), Name("/U"), dbDir);
// decrypt
consumer.decrypt(cKeyData->getContent().blockFromValue(),
fixtureDKeyBuf,
[=](const Buffer& result){
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
aesKeyBuf.begin(),
aesKeyBuf.end());
},
[=](const ErrorCode&, const std::string&){
BOOST_CHECK(false);
});
// decrypt
consumer.decrypt(contentData->getContent().blockFromValue(),
fixtureCKeyBuf,
[=](const Buffer& result){
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
DATA_CONTEN,
DATA_CONTEN + sizeof(DATA_CONTEN));
},
[=](const ErrorCode&, const std::string&){
BOOST_CHECK(false);
});
}
BOOST_AUTO_TEST_CASE(Consume)
{
auto contentData = createEncryptedContent();
auto cKeyData = createEncryptedCKey();
auto dKeyData = createEncryptedDKey();
int contentCount = 0;
int cKeyCount = 0;
int dKeyCount = 0;
Name prefix("/Prefix");
// prepare face1
face1.setInterestFilter(prefix,
[&] (const InterestFilter&, const Interest& i) {
if (i.matchesData(*contentData)) {
contentCount = 1;
face1.put(*contentData);
return;
}
if (i.matchesData(*cKeyData)) {
cKeyCount = 1;
face1.put(*cKeyData);
return;
}
if (i.matchesData(*dKeyData)) {
dKeyCount = 1;
face1.put(*dKeyData);
return;
}
return;
},
RegisterPrefixSuccessCallback(),
[] (const Name&, const std::string& e) { });
do {
advanceClocks(time::milliseconds(10), 20);
} while (passPacket());
// create consumer
std::string dbDir = tmpPath.c_str();
dbDir += "/test.db";
Consumer consumer(face2, groupName, uName, dbDir);
consumer.addDecryptionKey(uKeyName, fixtureUDKeyBuf);
int finalCount = 0;
consumer.consume(contentName,
[&](const Data& data, const Buffer& result){
finalCount = 1;
BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(),
DATA_CONTEN,
DATA_CONTEN + sizeof(DATA_CONTEN));
},
[&](const ErrorCode& code, const std::string& str){
BOOST_CHECK(false);
});
do {
advanceClocks(time::milliseconds(10), 20);
} while (passPacket());
BOOST_CHECK_EQUAL(contentCount, 1);
BOOST_CHECK_EQUAL(cKeyCount, 1);
BOOST_CHECK_EQUAL(dKeyCount, 1);
BOOST_CHECK_EQUAL(finalCount, 1);
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace test
} // namespace gep
} // namespace ndn