blob: 63676087f7f8c183c2ca6b4f193437c9cdb0d591 [file] [log] [blame]
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shic1264c82016-07-14 15:04:07 +00003 * Copyright (c) 2014-2016, Regents of the University of California
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07004 *
5 * This file is part of ndn-group-encrypt (Group-based Encryption Protocol for NDN).
6 * See AUTHORS.md for complete list of ndn-group-encrypt authors and contributors.
7 *
8 * ndn-group-encrypt is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * ndn-group-encrypt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * ndn-group-encrypt, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "producer.hpp"
21#include "algo/encryptor.hpp"
22#include "algo/rsa.hpp"
23#include "algo/aes.hpp"
24#include "encrypted-content.hpp"
25#include "unit-test-time-fixture.hpp"
26#include "random-number-generator.hpp"
27
28#include <ndn-cxx/util/dummy-client-face.hpp>
29
30#include "boost-test.hpp"
31#include <boost/asio.hpp>
32#include <boost/filesystem.hpp>
33
34namespace ndn {
35namespace gep {
36namespace tests {
37
38static const uint8_t DATA_CONTEN[] = {
39 0xcb, 0xe5, 0x6a, 0x80, 0x41, 0x24, 0x58, 0x23,
40 0x84, 0x14, 0x15, 0x61, 0x80, 0xb9, 0x5e, 0xbd,
41 0xce, 0x32, 0xb4, 0xbe, 0xbc, 0x91, 0x31, 0xd6,
42 0x19, 0x00, 0x80, 0x8b, 0xfa, 0x00, 0x05, 0x9c
43};
44
45class ProducerFixture : public UnitTestTimeFixture
46{
47public:
48 ProducerFixture()
49 : tmpPath(boost::filesystem::path(TMP_TESTS_PATH))
Junxiao Shic1264c82016-07-14 15:04:07 +000050 , face1(io, keyChain, {true, true})
51 , face2(io, keyChain, {true, true})
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070052 , readInterestOffset1(0)
53 , readDataOffset1(0)
54 , readInterestOffset2(0)
55 , readDataOffset2(0)
56 {
57 boost::filesystem::create_directories(tmpPath);
58 }
59
60 ~ProducerFixture()
61 {
62 boost::filesystem::remove_all(tmpPath);
63 }
64
65 void
66 createEncryptionKey(Name eKeyName, const Name& timeMarker)
67 {
68 RandomNumberGenerator rng;
69 RsaKeyParams params;
70 eKeyName.append(timeMarker);
71
72 Buffer dKeyBuf = algo::Rsa::generateKey(rng, params).getKeyBits();
73 Buffer eKeyBuf = algo::Rsa::deriveEncryptKey(dKeyBuf).getKeyBits();
74 decryptionKeys[eKeyName] = dKeyBuf;
75
76 shared_ptr<Data> keyData = make_shared<Data>(eKeyName);
77 keyData->setContent(eKeyBuf.buf(), eKeyBuf.size());
78 keyChain.sign(*keyData);
79 encryptionKeys[eKeyName] = keyData;
80 }
81
82 bool
83 passPacket()
84 {
85 bool hasPassed = false;
86
Junxiao Shic1264c82016-07-14 15:04:07 +000087 checkFace(face1.sentInterests, readInterestOffset1, face2, hasPassed);
88 checkFace(face1.sentData, readDataOffset1, face2, hasPassed);
89 checkFace(face2.sentInterests, readInterestOffset2, face1, hasPassed);
90 checkFace(face2.sentData, readDataOffset2, face1, hasPassed);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070091
92 return hasPassed;
93 }
94
95 template<typename Packet>
96 void
97 checkFace(std::vector<Packet>& receivedPackets,
98 size_t& readPacketOffset,
99 util::DummyClientFace& receiver,
100 bool& hasPassed)
101 {
102 while (receivedPackets.size() > readPacketOffset) {
103 receiver.receive(receivedPackets[readPacketOffset]);
104 readPacketOffset++;
105 hasPassed = true;
106 }
107 }
108
109public:
110 boost::filesystem::path tmpPath;
111
Junxiao Shic1264c82016-07-14 15:04:07 +0000112 util::DummyClientFace face1;
113 util::DummyClientFace face2;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700114
115 size_t readInterestOffset1;
116 size_t readDataOffset1;
117 size_t readInterestOffset2;
118 size_t readDataOffset2;
119
120 KeyChain keyChain;
121
122 std::unordered_map<Name, Buffer> decryptionKeys;
123 std::unordered_map<Name, shared_ptr<Data>> encryptionKeys;
124};
125
126BOOST_FIXTURE_TEST_SUITE(TestProducer, ProducerFixture)
127
128BOOST_AUTO_TEST_CASE(ContentKeyRequest)
129{
130 std::string dbDir = tmpPath.c_str();
131 dbDir += "/test.db";
132
133 Name prefix("/prefix");
134 Name suffix("/a/b/c");
135 Name expectedInterest = prefix;
136 expectedInterest.append(NAME_COMPONENT_READ);
137 expectedInterest.append(suffix);
138 expectedInterest.append(NAME_COMPONENT_E_KEY);
139
140 Name cKeyName = prefix;
141 cKeyName.append(NAME_COMPONENT_SAMPLE);
142 cKeyName.append(suffix);
143 cKeyName.append(NAME_COMPONENT_C_KEY);
144
145 Name timeMarker("20150101T100000/20150101T120000");
146 time::system_clock::TimePoint testTime1 = time::fromIsoString("20150101T100001");
147 time::system_clock::TimePoint testTime2 = time::fromIsoString("20150101T110001");
148 name::Component testTimeRounded1("20150101T100000");
149 name::Component testTimeRounded2("20150101T110000");
Yingdi Yu0c530b72016-03-20 18:19:14 -0700150 name::Component testTimeComponent2("20150101T110001");
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700151
152 // Create content keys required for this test case:
153 for (size_t i = 0; i < suffix.size(); i++) {
154 createEncryptionKey(expectedInterest, timeMarker);
155 expectedInterest = expectedInterest.getPrefix(-2).append(NAME_COMPONENT_E_KEY);
156 }
157
Junxiao Shic1264c82016-07-14 15:04:07 +0000158 face2.setInterestFilter(prefix,
159 [&] (const InterestFilter&, const Interest& i) {
160 Name interestName = i.getName();
161 interestName.append(timeMarker);
162 BOOST_REQUIRE_EQUAL(encryptionKeys.find(interestName) !=
163 encryptionKeys.end(), true);
164 face2.put(*(encryptionKeys[interestName]));
165 return;
166 },
167 RegisterPrefixSuccessCallback(),
168 [] (const Name&, const std::string& e) { });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700169
170 do {
171 advanceClocks(time::milliseconds(10), 20);
172 } while (passPacket());
173
174 /*
175 Verify that content key is correctly encrypted for each domain, and the
176 produce method encrypts provided data with the same content key.
177 */
Junxiao Shic1264c82016-07-14 15:04:07 +0000178 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700179 ProducerDB testDb(dbDir);
180 Buffer contentKey;
181
182 auto checkEncryptionKeys =
183 [&](const std::vector<Data>& result,
184 const time::system_clock::TimePoint& testTime,
185 const name::Component& roundedTime) {
186 BOOST_CHECK_EQUAL(testDb.hasContentKey(testTime), true);
187 contentKey = testDb.getContentKey(testTime);
188
189 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
190 std::vector<Data>::const_iterator it;
191 for (it = result.begin(); it != result.end(); ++it) {
192 Name keyName = it->getName();
193 BOOST_CHECK_EQUAL(keyName.getSubName(0,6), cKeyName);
194 BOOST_CHECK_EQUAL(keyName.get(6), roundedTime);
195 BOOST_CHECK_EQUAL(keyName.get(7), NAME_COMPONENT_FOR);
196 BOOST_CHECK_EQUAL(decryptionKeys.find(keyName.getSubName(8)) !=
197 decryptionKeys.end(), true);
198 Name testName = it->getName().getSubName(-8);
199 Buffer decryptionKey;
200
201 decryptionKey = decryptionKeys.at(keyName.getSubName(8));
202 BOOST_CHECK_EQUAL(decryptionKey.size() != 0, true);
203 Block encryptedKeyBlock = it->getContent();
204 encryptedKeyBlock.parse();
205
206 EncryptedContent content(*(encryptedKeyBlock.elements_begin()));
207 const Buffer& encryptedKey = content.getPayload();
208 Buffer retrievedKey = algo::Rsa::decrypt(decryptionKey.buf(),
209 decryptionKey.size(),
210 encryptedKey.buf(),
211 encryptedKey.size(),
212 params);
213
214 BOOST_CHECK_EQUAL_COLLECTIONS(contentKey.begin(),
215 contentKey.end(),
216 retrievedKey.begin(),
217 retrievedKey.end());
218 }
219 BOOST_CHECK_EQUAL(result.size(), 3);
220 };
221
222 // Initial test to confirm that keys are created for this timeslot
223 Name contentKeyName1 =
224 producer.createContentKey(testTime1,
225 std::bind(checkEncryptionKeys, _1, testTime1, testTimeRounded1));
226
227 do {
228 advanceClocks(time::milliseconds(10), 20);
229 } while (passPacket());
230
231 // Verify that we do not repeat the search for e-keys, don't advance clock
232 Name contentKeyName2 =
233 producer.createContentKey(testTime2,
234 std::bind(checkEncryptionKeys, _1, testTime2, testTimeRounded2));
235
236 // Confirm content key names are correct
237 BOOST_CHECK_EQUAL(contentKeyName1.getPrefix(-1), cKeyName);
238 BOOST_CHECK_EQUAL(contentKeyName1.get(6), testTimeRounded1);
239 BOOST_CHECK_EQUAL(contentKeyName2.getPrefix(-1), cKeyName);
240 BOOST_CHECK_EQUAL(contentKeyName2.get(6), testTimeRounded2);
241
242 // Confirm produce encrypts with correct key and has right name
243 Data testData;
244 producer.produce(testData, testTime2, DATA_CONTEN, sizeof(DATA_CONTEN));
245
246 Name producedName = testData.getName();
247 BOOST_CHECK_EQUAL(producedName.getSubName(0,5), cKeyName.getPrefix(-1));
Yingdi Yu0c530b72016-03-20 18:19:14 -0700248 BOOST_CHECK_EQUAL(producedName.get(5), testTimeComponent2);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700249 BOOST_CHECK_EQUAL(producedName.get(6), NAME_COMPONENT_FOR);
250 BOOST_CHECK_EQUAL(producedName.getSubName(7,6), cKeyName);
251 BOOST_CHECK_EQUAL(producedName.get(13), testTimeRounded2);
252
253 Block dataBlock = testData.getContent();
254 dataBlock.parse();
255
256 EncryptedContent dataContent(*(dataBlock).elements_begin());
257 const Buffer& encData = dataContent.getPayload();
258 const Buffer& iv = dataContent.getInitialVector();
259
260 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
261 params.setIV(iv.buf(), iv.size());
262 Buffer decryptTest = algo::Aes::decrypt(contentKey.buf(), contentKey.size(),
263 encData.buf(), encData.size(), params);
264 BOOST_CHECK_EQUAL_COLLECTIONS(decryptTest.begin(),
265 decryptTest.end(),
266 DATA_CONTEN,
267 DATA_CONTEN + sizeof(DATA_CONTEN));
268}
269
270BOOST_AUTO_TEST_CASE(ContentKeySearch)
271{
272 std::string dbDir = tmpPath.c_str();
273 dbDir += "/test.db";
274
275 Name timeMarkerFirstHop("20150101T070000/20150101T080000");
276 Name timeMarkerSecondHop("20150101T080000/20150101T090000");
277 Name timeMarkerThirdHop("20150101T100000/20150101T110000");
278
279 Name prefix("/prefix");
280 Name suffix("/suffix");
281 Name expectedInterest = prefix;
282 expectedInterest.append(NAME_COMPONENT_READ);
283 expectedInterest.append(suffix);
284 expectedInterest.append(NAME_COMPONENT_E_KEY);
285
286 Name cKeyName = prefix;
287 cKeyName.append(NAME_COMPONENT_SAMPLE);
288 cKeyName.append(suffix);
289 cKeyName.append(NAME_COMPONENT_C_KEY);
290
291 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
292
293 // Create content keys required for this test case:
294 createEncryptionKey(expectedInterest, timeMarkerFirstHop);
295 createEncryptionKey(expectedInterest, timeMarkerSecondHop);
296 createEncryptionKey(expectedInterest, timeMarkerThirdHop);
297
298 size_t requestCount = 0;
Junxiao Shic1264c82016-07-14 15:04:07 +0000299 face2.setInterestFilter(prefix,
300 [&] (const InterestFilter&, const Interest& i) {
301 BOOST_REQUIRE_EQUAL(i.getName(), expectedInterest);
302 Name interestName = i.getName();
303 switch(requestCount) {
304 case 0:
305 interestName.append(timeMarkerFirstHop);
306 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700307
Junxiao Shic1264c82016-07-14 15:04:07 +0000308 case 1:
309 interestName.append(timeMarkerSecondHop);
310 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700311
Junxiao Shic1264c82016-07-14 15:04:07 +0000312 case 2:
313 interestName.append(timeMarkerThirdHop);
314 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700315
Junxiao Shic1264c82016-07-14 15:04:07 +0000316 default:
317 break;
318 }
319 face2.put(*(encryptionKeys[interestName]));
320 requestCount++;
321 return;
322 },
323 RegisterPrefixSuccessCallback(),
324 [] (const Name&, const std::string& e) { });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700325
326 do {
327 advanceClocks(time::milliseconds(10), 20);
328 } while (passPacket());
329
330 /*
331 Verify that if a key is found, but not within the right timeslot, the search
332 is refined until a valid timeslot is found.
333 */
Junxiao Shic1264c82016-07-14 15:04:07 +0000334 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700335 producer.createContentKey(testTime,
336 [&](const std::vector<Data>& result){
337 BOOST_CHECK_EQUAL(requestCount, 3);
338 BOOST_CHECK_EQUAL(result.size(), 1);
339
340 Data keyData = result[0];
341 Name keyName = keyData.getName();
342 BOOST_CHECK_EQUAL(keyName.getSubName(0,4), cKeyName);
343 BOOST_CHECK_EQUAL(keyName.get(4), timeMarkerThirdHop[0]);
344 BOOST_CHECK_EQUAL(keyName.get(5), NAME_COMPONENT_FOR);
345 BOOST_CHECK_EQUAL(keyName.getSubName(6),
346 expectedInterest.append(timeMarkerThirdHop));
347 });
348 do {
349 advanceClocks(time::milliseconds(10), 20);
350 } while (passPacket());
351}
352
353BOOST_AUTO_TEST_CASE(ContentKeyTimeout)
354{
355 std::string dbDir = tmpPath.c_str();
356 dbDir += "/test.db";
357
358 Name prefix("/prefix");
359 Name suffix("/suffix");
360 Name expectedInterest = prefix;
361 expectedInterest.append(NAME_COMPONENT_READ);
362 expectedInterest.append(suffix);
363 expectedInterest.append(NAME_COMPONENT_E_KEY);
364
365 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
366
367 size_t timeoutCount = 0;
Junxiao Shic1264c82016-07-14 15:04:07 +0000368 face2.setInterestFilter(prefix,
369 [&] (const InterestFilter&, const Interest& i) {
370 BOOST_CHECK_EQUAL(i.getName(), expectedInterest);
371 timeoutCount++;
372 return;
373 },
374 RegisterPrefixSuccessCallback(),
375 [] (const Name&, const std::string& e) { });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700376
377 do {
378 advanceClocks(time::milliseconds(10), 20);
379 } while (passPacket());
380
381 /*
382 Verify that if no response is received, the producer appropriately times out.
383 The result vector should not contain elements that have timed out.
384 */
Junxiao Shic1264c82016-07-14 15:04:07 +0000385 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700386 producer.createContentKey(testTime,
387 [&](const std::vector<Data>& result){
388 BOOST_CHECK_EQUAL(timeoutCount, 4);
389 BOOST_CHECK_EQUAL(result.size(), 0);
390 });
391
392 do {
393 advanceClocks(time::milliseconds(10), 500);
394 } while (passPacket());
395}
396
397BOOST_AUTO_TEST_SUITE_END()
398
399} // namespace tests
400} // namespace gep
401} // namespace ndn