blob: 37d08d2f7eec33e400f9a2ff043db40956f62d96 [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
Yingdi Yu48967a62016-03-11 22:04:14 -0800174 // Verify that content key is correctly encrypted for each domain, and the
175 // produce method encrypts provided data with the same content key.
Junxiao Shic1264c82016-07-14 15:04:07 +0000176 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700177 ProducerDB testDb(dbDir);
178 Buffer contentKey;
179
180 auto checkEncryptionKeys =
181 [&](const std::vector<Data>& result,
182 const time::system_clock::TimePoint& testTime,
183 const name::Component& roundedTime) {
184 BOOST_CHECK_EQUAL(testDb.hasContentKey(testTime), true);
185 contentKey = testDb.getContentKey(testTime);
186
187 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
188 std::vector<Data>::const_iterator it;
189 for (it = result.begin(); it != result.end(); ++it) {
190 Name keyName = it->getName();
191 BOOST_CHECK_EQUAL(keyName.getSubName(0,6), cKeyName);
192 BOOST_CHECK_EQUAL(keyName.get(6), roundedTime);
193 BOOST_CHECK_EQUAL(keyName.get(7), NAME_COMPONENT_FOR);
194 BOOST_CHECK_EQUAL(decryptionKeys.find(keyName.getSubName(8)) !=
195 decryptionKeys.end(), true);
196 Name testName = it->getName().getSubName(-8);
197 Buffer decryptionKey;
198
199 decryptionKey = decryptionKeys.at(keyName.getSubName(8));
200 BOOST_CHECK_EQUAL(decryptionKey.size() != 0, true);
201 Block encryptedKeyBlock = it->getContent();
202 encryptedKeyBlock.parse();
203
204 EncryptedContent content(*(encryptedKeyBlock.elements_begin()));
205 const Buffer& encryptedKey = content.getPayload();
206 Buffer retrievedKey = algo::Rsa::decrypt(decryptionKey.buf(),
207 decryptionKey.size(),
208 encryptedKey.buf(),
209 encryptedKey.size(),
210 params);
211
212 BOOST_CHECK_EQUAL_COLLECTIONS(contentKey.begin(),
213 contentKey.end(),
214 retrievedKey.begin(),
215 retrievedKey.end());
216 }
217 BOOST_CHECK_EQUAL(result.size(), 3);
218 };
219
220 // Initial test to confirm that keys are created for this timeslot
221 Name contentKeyName1 =
222 producer.createContentKey(testTime1,
223 std::bind(checkEncryptionKeys, _1, testTime1, testTimeRounded1));
224
225 do {
226 advanceClocks(time::milliseconds(10), 20);
227 } while (passPacket());
228
229 // Verify that we do not repeat the search for e-keys, don't advance clock
230 Name contentKeyName2 =
231 producer.createContentKey(testTime2,
232 std::bind(checkEncryptionKeys, _1, testTime2, testTimeRounded2));
233
234 // Confirm content key names are correct
235 BOOST_CHECK_EQUAL(contentKeyName1.getPrefix(-1), cKeyName);
236 BOOST_CHECK_EQUAL(contentKeyName1.get(6), testTimeRounded1);
237 BOOST_CHECK_EQUAL(contentKeyName2.getPrefix(-1), cKeyName);
238 BOOST_CHECK_EQUAL(contentKeyName2.get(6), testTimeRounded2);
239
240 // Confirm produce encrypts with correct key and has right name
241 Data testData;
242 producer.produce(testData, testTime2, DATA_CONTEN, sizeof(DATA_CONTEN));
243
244 Name producedName = testData.getName();
245 BOOST_CHECK_EQUAL(producedName.getSubName(0,5), cKeyName.getPrefix(-1));
Yingdi Yu0c530b72016-03-20 18:19:14 -0700246 BOOST_CHECK_EQUAL(producedName.get(5), testTimeComponent2);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700247 BOOST_CHECK_EQUAL(producedName.get(6), NAME_COMPONENT_FOR);
248 BOOST_CHECK_EQUAL(producedName.getSubName(7,6), cKeyName);
249 BOOST_CHECK_EQUAL(producedName.get(13), testTimeRounded2);
250
251 Block dataBlock = testData.getContent();
252 dataBlock.parse();
253
254 EncryptedContent dataContent(*(dataBlock).elements_begin());
255 const Buffer& encData = dataContent.getPayload();
256 const Buffer& iv = dataContent.getInitialVector();
257
258 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
259 params.setIV(iv.buf(), iv.size());
260 Buffer decryptTest = algo::Aes::decrypt(contentKey.buf(), contentKey.size(),
261 encData.buf(), encData.size(), params);
262 BOOST_CHECK_EQUAL_COLLECTIONS(decryptTest.begin(),
263 decryptTest.end(),
264 DATA_CONTEN,
265 DATA_CONTEN + sizeof(DATA_CONTEN));
266}
267
268BOOST_AUTO_TEST_CASE(ContentKeySearch)
269{
270 std::string dbDir = tmpPath.c_str();
271 dbDir += "/test.db";
272
273 Name timeMarkerFirstHop("20150101T070000/20150101T080000");
274 Name timeMarkerSecondHop("20150101T080000/20150101T090000");
275 Name timeMarkerThirdHop("20150101T100000/20150101T110000");
276
277 Name prefix("/prefix");
278 Name suffix("/suffix");
279 Name expectedInterest = prefix;
280 expectedInterest.append(NAME_COMPONENT_READ);
281 expectedInterest.append(suffix);
282 expectedInterest.append(NAME_COMPONENT_E_KEY);
283
284 Name cKeyName = prefix;
285 cKeyName.append(NAME_COMPONENT_SAMPLE);
286 cKeyName.append(suffix);
287 cKeyName.append(NAME_COMPONENT_C_KEY);
288
289 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
290
291 // Create content keys required for this test case:
292 createEncryptionKey(expectedInterest, timeMarkerFirstHop);
293 createEncryptionKey(expectedInterest, timeMarkerSecondHop);
294 createEncryptionKey(expectedInterest, timeMarkerThirdHop);
295
296 size_t requestCount = 0;
Junxiao Shic1264c82016-07-14 15:04:07 +0000297 face2.setInterestFilter(prefix,
298 [&] (const InterestFilter&, const Interest& i) {
299 BOOST_REQUIRE_EQUAL(i.getName(), expectedInterest);
300 Name interestName = i.getName();
301 switch(requestCount) {
302 case 0:
303 interestName.append(timeMarkerFirstHop);
304 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700305
Junxiao Shic1264c82016-07-14 15:04:07 +0000306 case 1:
307 interestName.append(timeMarkerSecondHop);
308 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700309
Junxiao Shic1264c82016-07-14 15:04:07 +0000310 case 2:
311 interestName.append(timeMarkerThirdHop);
312 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700313
Junxiao Shic1264c82016-07-14 15:04:07 +0000314 default:
315 break;
316 }
317 face2.put(*(encryptionKeys[interestName]));
318 requestCount++;
319 return;
320 },
321 RegisterPrefixSuccessCallback(),
322 [] (const Name&, const std::string& e) { });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700323
324 do {
325 advanceClocks(time::milliseconds(10), 20);
326 } while (passPacket());
327
Yingdi Yu48967a62016-03-11 22:04:14 -0800328 // Verify that if a key is found, but not within the right timeslot, the search
329 // is refined until a valid timeslot is found.
Junxiao Shic1264c82016-07-14 15:04:07 +0000330 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700331 producer.createContentKey(testTime,
332 [&](const std::vector<Data>& result){
333 BOOST_CHECK_EQUAL(requestCount, 3);
334 BOOST_CHECK_EQUAL(result.size(), 1);
335
336 Data keyData = result[0];
337 Name keyName = keyData.getName();
338 BOOST_CHECK_EQUAL(keyName.getSubName(0,4), cKeyName);
339 BOOST_CHECK_EQUAL(keyName.get(4), timeMarkerThirdHop[0]);
340 BOOST_CHECK_EQUAL(keyName.get(5), NAME_COMPONENT_FOR);
341 BOOST_CHECK_EQUAL(keyName.getSubName(6),
342 expectedInterest.append(timeMarkerThirdHop));
343 });
344 do {
345 advanceClocks(time::milliseconds(10), 20);
346 } while (passPacket());
347}
348
349BOOST_AUTO_TEST_CASE(ContentKeyTimeout)
350{
351 std::string dbDir = tmpPath.c_str();
352 dbDir += "/test.db";
353
354 Name prefix("/prefix");
355 Name suffix("/suffix");
356 Name expectedInterest = prefix;
357 expectedInterest.append(NAME_COMPONENT_READ);
358 expectedInterest.append(suffix);
359 expectedInterest.append(NAME_COMPONENT_E_KEY);
360
361 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
362
363 size_t timeoutCount = 0;
Junxiao Shic1264c82016-07-14 15:04:07 +0000364 face2.setInterestFilter(prefix,
365 [&] (const InterestFilter&, const Interest& i) {
366 BOOST_CHECK_EQUAL(i.getName(), expectedInterest);
367 timeoutCount++;
368 return;
369 },
370 RegisterPrefixSuccessCallback(),
371 [] (const Name&, const std::string& e) { });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700372
373 do {
374 advanceClocks(time::milliseconds(10), 20);
375 } while (passPacket());
376
Yingdi Yu48967a62016-03-11 22:04:14 -0800377 // Verify that if no response is received, the producer appropriately times out.
378 // The result vector should not contain elements that have timed out.
Junxiao Shic1264c82016-07-14 15:04:07 +0000379 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700380 producer.createContentKey(testTime,
381 [&](const std::vector<Data>& result){
382 BOOST_CHECK_EQUAL(timeoutCount, 4);
383 BOOST_CHECK_EQUAL(result.size(), 0);
384 });
385
386 do {
387 advanceClocks(time::milliseconds(10), 500);
388 } while (passPacket());
389}
390
Yingdi Yu48967a62016-03-11 22:04:14 -0800391BOOST_AUTO_TEST_CASE(ProducerWithLink)
392{
393 std::string dbDir = tmpPath.c_str();
394 dbDir += "/test.db";
395
396 Name prefix("/prefix");
397 Name suffix("/suffix");
398 Name expectedInterest = prefix;
399 expectedInterest.append(NAME_COMPONENT_READ);
400 expectedInterest.append(suffix);
401 expectedInterest.append(NAME_COMPONENT_E_KEY);
402
403 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
404
405 size_t timeoutCount = 0;
406 face2.setInterestFilter(prefix,
407 [&] (const InterestFilter&, const Interest& i) {
408 BOOST_CHECK_EQUAL(i.getName(), expectedInterest);
409 BOOST_CHECK(i.getLink().getDelegations().size() == 3);
410 timeoutCount++;
411 return;
412 },
413 RegisterPrefixSuccessCallback(),
414 [] (const Name&, const std::string& e) { });
415
416 do {
417 advanceClocks(time::milliseconds(10), 20);
418 } while (passPacket());
419
420 // Verify that if no response is received, the producer appropriately times out.
421 // The result vector should not contain elements that have timed out.
422 Link link("test", {{10, "/test1"}, {20, "/test2"}, {100, "/test3"}});
423 keyChain.sign(link);
424 Producer producer(prefix, suffix, face1, dbDir, 3, link);
425 producer.createContentKey(testTime,
426 [&](const std::vector<Data>& result){
427 BOOST_CHECK_EQUAL(timeoutCount, 4);
428 BOOST_CHECK_EQUAL(result.size(), 0);
429 });
430
431 do {
432 advanceClocks(time::milliseconds(10), 800);
433 } while (passPacket());
434}
435
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700436BOOST_AUTO_TEST_SUITE_END()
437
438} // namespace tests
439} // namespace gep
440} // namespace ndn