blob: a66ef82a83ffdb25950784b72b3d0294e5f7d5eb [file] [log] [blame]
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhang19a11d22018-04-12 22:58:20 -07003 * Copyright (c) 2014-2018, Regents of the University of California
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07004 *
Alexander Afanasyev9091d832018-04-18 17:21:08 -04005 * This file is part of NAC (Name-Based Access Control for NDN).
6 * See AUTHORS.md for complete list of NAC authors and contributors.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07007 *
Alexander Afanasyev9091d832018-04-18 17:21:08 -04008 * NAC is free software: you can redistribute it and/or modify it under the terms
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07009 * 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 *
Alexander Afanasyev9091d832018-04-18 17:21:08 -040012 * NAC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070013 * 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
Alexander Afanasyev9091d832018-04-18 17:21:08 -040017 * NAC, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070018 */
19
20#include "producer.hpp"
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070021#include "encrypted-content.hpp"
22#include "unit-test-time-fixture.hpp"
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070023#include "algo/aes.hpp"
24#include "algo/encryptor.hpp"
25#include "algo/rsa.hpp"
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070026#include "boost-test.hpp"
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070027#include <ndn-cxx/util/dummy-client-face.hpp>
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070028#include <boost/asio.hpp>
29#include <boost/filesystem.hpp>
30
31namespace ndn {
Alexander Afanasyev9091d832018-04-18 17:21:08 -040032namespace nac {
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070033namespace tests {
34
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070035static const uint8_t DATA_CONTEN[] = {0xcb, 0xe5, 0x6a, 0x80, 0x41, 0x24, 0x58, 0x23,
36 0x84, 0x14, 0x15, 0x61, 0x80, 0xb9, 0x5e, 0xbd,
37 0xce, 0x32, 0xb4, 0xbe, 0xbc, 0x91, 0x31, 0xd6,
38 0x19, 0x00, 0x80, 0x8b, 0xfa, 0x00, 0x05, 0x9c};
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070039
40class ProducerFixture : public UnitTestTimeFixture
41{
42public:
43 ProducerFixture()
44 : tmpPath(boost::filesystem::path(TMP_TESTS_PATH))
Junxiao Shic1264c82016-07-14 15:04:07 +000045 , face1(io, keyChain, {true, true})
46 , face2(io, keyChain, {true, true})
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070047 , readInterestOffset1(0)
48 , readDataOffset1(0)
49 , readInterestOffset2(0)
50 , readDataOffset2(0)
51 {
52 boost::filesystem::create_directories(tmpPath);
53 }
54
55 ~ProducerFixture()
56 {
57 boost::filesystem::remove_all(tmpPath);
58 }
59
60 void
61 createEncryptionKey(Name eKeyName, const Name& timeMarker)
62 {
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070063 RsaKeyParams params;
64 eKeyName.append(timeMarker);
65
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070066 Buffer dKeyBuf = algo::Rsa::generateKey(params).getKeyBits();
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070067 Buffer eKeyBuf = algo::Rsa::deriveEncryptKey(dKeyBuf).getKeyBits();
68 decryptionKeys[eKeyName] = dKeyBuf;
69
70 shared_ptr<Data> keyData = make_shared<Data>(eKeyName);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070071 keyData->setContent(eKeyBuf.data(), eKeyBuf.size());
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070072 keyChain.sign(*keyData);
73 encryptionKeys[eKeyName] = keyData;
74 }
75
76 bool
77 passPacket()
78 {
79 bool hasPassed = false;
80
Junxiao Shic1264c82016-07-14 15:04:07 +000081 checkFace(face1.sentInterests, readInterestOffset1, face2, hasPassed);
82 checkFace(face1.sentData, readDataOffset1, face2, hasPassed);
83 checkFace(face2.sentInterests, readInterestOffset2, face1, hasPassed);
84 checkFace(face2.sentData, readDataOffset2, face1, hasPassed);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070085
86 return hasPassed;
87 }
88
89 template<typename Packet>
90 void
91 checkFace(std::vector<Packet>& receivedPackets,
92 size_t& readPacketOffset,
93 util::DummyClientFace& receiver,
94 bool& hasPassed)
95 {
96 while (receivedPackets.size() > readPacketOffset) {
97 receiver.receive(receivedPackets[readPacketOffset]);
98 readPacketOffset++;
99 hasPassed = true;
100 }
101 }
102
103public:
104 boost::filesystem::path tmpPath;
105
Junxiao Shic1264c82016-07-14 15:04:07 +0000106 util::DummyClientFace face1;
107 util::DummyClientFace face2;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700108
109 size_t readInterestOffset1;
110 size_t readDataOffset1;
111 size_t readInterestOffset2;
112 size_t readDataOffset2;
113
114 KeyChain keyChain;
115
116 std::unordered_map<Name, Buffer> decryptionKeys;
117 std::unordered_map<Name, shared_ptr<Data>> encryptionKeys;
118};
119
120BOOST_FIXTURE_TEST_SUITE(TestProducer, ProducerFixture)
121
122BOOST_AUTO_TEST_CASE(ContentKeyRequest)
123{
124 std::string dbDir = tmpPath.c_str();
125 dbDir += "/test.db";
126
127 Name prefix("/prefix");
128 Name suffix("/a/b/c");
129 Name expectedInterest = prefix;
130 expectedInterest.append(NAME_COMPONENT_READ);
131 expectedInterest.append(suffix);
132 expectedInterest.append(NAME_COMPONENT_E_KEY);
133
134 Name cKeyName = prefix;
135 cKeyName.append(NAME_COMPONENT_SAMPLE);
136 cKeyName.append(suffix);
137 cKeyName.append(NAME_COMPONENT_C_KEY);
138
139 Name timeMarker("20150101T100000/20150101T120000");
140 time::system_clock::TimePoint testTime1 = time::fromIsoString("20150101T100001");
141 time::system_clock::TimePoint testTime2 = time::fromIsoString("20150101T110001");
142 name::Component testTimeRounded1("20150101T100000");
143 name::Component testTimeRounded2("20150101T110000");
Yingdi Yu0c530b72016-03-20 18:19:14 -0700144 name::Component testTimeComponent2("20150101T110001");
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700145
146 // Create content keys required for this test case:
147 for (size_t i = 0; i < suffix.size(); i++) {
148 createEncryptionKey(expectedInterest, timeMarker);
149 expectedInterest = expectedInterest.getPrefix(-2).append(NAME_COMPONENT_E_KEY);
150 }
151
Junxiao Shic1264c82016-07-14 15:04:07 +0000152 face2.setInterestFilter(prefix,
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700153 [&] (const InterestFilter&, const Interest& i) {
154 Name interestName = i.getName();
155 interestName.append(timeMarker);
156 BOOST_REQUIRE_EQUAL(encryptionKeys.find(interestName) !=
157 encryptionKeys.end(),
158 true);
159 face2.put(*(encryptionKeys[interestName]));
160 return;
161 },
162 RegisterPrefixSuccessCallback(),
163 [] (const Name&, const std::string& e) {});
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700164
165 do {
166 advanceClocks(time::milliseconds(10), 20);
167 } while (passPacket());
168
Yingdi Yu48967a62016-03-11 22:04:14 -0800169 // Verify that content key is correctly encrypted for each domain, and the
170 // produce method encrypts provided data with the same content key.
Junxiao Shic1264c82016-07-14 15:04:07 +0000171 Producer producer(prefix, suffix, face1, dbDir);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700172 ProducerDB testDb(dbDir);
173 Buffer contentKey;
174
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700175 auto checkEncryptionKeys = [&] (const std::vector<Data>& result,
176 const time::system_clock::TimePoint& testTime,
177 const name::Component& roundedTime) {
178 BOOST_CHECK_EQUAL(testDb.hasContentKey(testTime), true);
179 contentKey = testDb.getContentKey(testTime);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700180
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700181 std::vector<Data>::const_iterator it;
182 for (it = result.begin(); it != result.end(); ++it) {
183 Name keyName = it->getName();
184 BOOST_CHECK_EQUAL(keyName.getSubName(0, 6), cKeyName);
185 BOOST_CHECK_EQUAL(keyName.get(6), roundedTime);
186 BOOST_CHECK_EQUAL(keyName.get(7), NAME_COMPONENT_FOR);
187 BOOST_CHECK_EQUAL(decryptionKeys.find(keyName.getSubName(8)) != decryptionKeys.end(), true);
188 Name testName = it->getName().getSubName(-8);
189 Buffer decryptionKey;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700190
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700191 decryptionKey = decryptionKeys.at(keyName.getSubName(8));
192 BOOST_CHECK_EQUAL(decryptionKey.size() != 0, true);
193 Block encryptedKeyBlock = it->getContent();
194 encryptedKeyBlock.parse();
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700195
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700196 EncryptedContent content(*(encryptedKeyBlock.elements_begin()));
197 const Buffer& encryptedKey = content.getPayload();
198 Buffer retrievedKey = algo::Rsa::decrypt(decryptionKey.data(),
199 decryptionKey.size(),
200 encryptedKey.data(),
201 encryptedKey.size());
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700202
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700203 BOOST_CHECK_EQUAL_COLLECTIONS(contentKey.begin(),
204 contentKey.end(),
205 retrievedKey.begin(),
206 retrievedKey.end());
207 }
208 BOOST_CHECK_EQUAL(result.size(), 3);
209 };
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700210
211 // Initial test to confirm that keys are created for this timeslot
212 Name contentKeyName1 =
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700213 producer.createContentKey(testTime1,
214 std::bind(checkEncryptionKeys, _1, testTime1, testTimeRounded1));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700215
216 do {
217 advanceClocks(time::milliseconds(10), 20);
218 } while (passPacket());
219
220 // Verify that we do not repeat the search for e-keys, don't advance clock
221 Name contentKeyName2 =
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700222 producer.createContentKey(testTime2,
223 std::bind(checkEncryptionKeys, _1, testTime2, testTimeRounded2));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700224
225 // Confirm content key names are correct
226 BOOST_CHECK_EQUAL(contentKeyName1.getPrefix(-1), cKeyName);
227 BOOST_CHECK_EQUAL(contentKeyName1.get(6), testTimeRounded1);
228 BOOST_CHECK_EQUAL(contentKeyName2.getPrefix(-1), cKeyName);
229 BOOST_CHECK_EQUAL(contentKeyName2.get(6), testTimeRounded2);
230
231 // Confirm produce encrypts with correct key and has right name
232 Data testData;
233 producer.produce(testData, testTime2, DATA_CONTEN, sizeof(DATA_CONTEN));
234
235 Name producedName = testData.getName();
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700236 BOOST_CHECK_EQUAL(producedName.getSubName(0, 5), cKeyName.getPrefix(-1));
Yingdi Yu0c530b72016-03-20 18:19:14 -0700237 BOOST_CHECK_EQUAL(producedName.get(5), testTimeComponent2);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700238 BOOST_CHECK_EQUAL(producedName.get(6), NAME_COMPONENT_FOR);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700239 BOOST_CHECK_EQUAL(producedName.getSubName(7, 6), cKeyName);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700240 BOOST_CHECK_EQUAL(producedName.get(13), testTimeRounded2);
241
242 Block dataBlock = testData.getContent();
243 dataBlock.parse();
244
245 EncryptedContent dataContent(*(dataBlock).elements_begin());
246 const Buffer& encData = dataContent.getPayload();
247 const Buffer& iv = dataContent.getInitialVector();
248
249 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700250 params.setIV(iv.data(), iv.size());
251 Buffer decryptTest =
252 algo::Aes::decrypt(contentKey.data(), contentKey.size(), encData.data(), encData.size(), params);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700253 BOOST_CHECK_EQUAL_COLLECTIONS(decryptTest.begin(),
254 decryptTest.end(),
255 DATA_CONTEN,
256 DATA_CONTEN + sizeof(DATA_CONTEN));
257}
258
259BOOST_AUTO_TEST_CASE(ContentKeySearch)
260{
261 std::string dbDir = tmpPath.c_str();
262 dbDir += "/test.db";
263
264 Name timeMarkerFirstHop("20150101T070000/20150101T080000");
265 Name timeMarkerSecondHop("20150101T080000/20150101T090000");
266 Name timeMarkerThirdHop("20150101T100000/20150101T110000");
267
268 Name prefix("/prefix");
269 Name suffix("/suffix");
270 Name expectedInterest = prefix;
271 expectedInterest.append(NAME_COMPONENT_READ);
272 expectedInterest.append(suffix);
273 expectedInterest.append(NAME_COMPONENT_E_KEY);
274
275 Name cKeyName = prefix;
276 cKeyName.append(NAME_COMPONENT_SAMPLE);
277 cKeyName.append(suffix);
278 cKeyName.append(NAME_COMPONENT_C_KEY);
279
280 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
281
282 // Create content keys required for this test case:
283 createEncryptionKey(expectedInterest, timeMarkerFirstHop);
284 createEncryptionKey(expectedInterest, timeMarkerSecondHop);
285 createEncryptionKey(expectedInterest, timeMarkerThirdHop);
286
287 size_t requestCount = 0;
Junxiao Shic1264c82016-07-14 15:04:07 +0000288 face2.setInterestFilter(prefix,
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700289 [&] (const InterestFilter&, const Interest& i) {
290 BOOST_REQUIRE_EQUAL(i.getName(), expectedInterest);
291 Name interestName = i.getName();
292 switch (requestCount) {
293 case 0:
294 interestName.append(timeMarkerFirstHop);
295 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700296
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700297 case 1:
298 interestName.append(timeMarkerSecondHop);
299 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700300
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700301 case 2:
302 interestName.append(timeMarkerThirdHop);
303 break;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700304
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700305 default:
306 break;
307 }
308 face2.put(*(encryptionKeys[interestName]));
309 requestCount++;
310 return;
311 },
312 RegisterPrefixSuccessCallback(),
313 [] (const Name&, const std::string& e) {});
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700314
315 do {
316 advanceClocks(time::milliseconds(10), 20);
317 } while (passPacket());
318
Yingdi Yu48967a62016-03-11 22:04:14 -0800319 // Verify that if a key is found, but not within the right timeslot, the search
320 // is refined until a valid timeslot is found.
Junxiao Shic1264c82016-07-14 15:04:07 +0000321 Producer producer(prefix, suffix, face1, dbDir);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700322 producer.createContentKey(testTime, [&] (const std::vector<Data>& result) {
323 BOOST_CHECK_EQUAL(requestCount, 3);
324 BOOST_CHECK_EQUAL(result.size(), 1);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700325
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700326 Data keyData = result[0];
327 Name keyName = keyData.getName();
328 BOOST_CHECK_EQUAL(keyName.getSubName(0, 4), cKeyName);
329 BOOST_CHECK_EQUAL(keyName.get(4), timeMarkerThirdHop[0]);
330 BOOST_CHECK_EQUAL(keyName.get(5), NAME_COMPONENT_FOR);
331 BOOST_CHECK_EQUAL(keyName.getSubName(6), expectedInterest.append(timeMarkerThirdHop));
332 });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700333 do {
334 advanceClocks(time::milliseconds(10), 20);
335 } while (passPacket());
336}
337
338BOOST_AUTO_TEST_CASE(ContentKeyTimeout)
339{
340 std::string dbDir = tmpPath.c_str();
341 dbDir += "/test.db";
342
343 Name prefix("/prefix");
344 Name suffix("/suffix");
345 Name expectedInterest = prefix;
346 expectedInterest.append(NAME_COMPONENT_READ);
347 expectedInterest.append(suffix);
348 expectedInterest.append(NAME_COMPONENT_E_KEY);
349
350 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
351
352 size_t timeoutCount = 0;
Junxiao Shic1264c82016-07-14 15:04:07 +0000353 face2.setInterestFilter(prefix,
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700354 [&] (const InterestFilter&, const Interest& i) {
355 BOOST_CHECK_EQUAL(i.getName(), expectedInterest);
356 timeoutCount++;
357 return;
358 },
359 RegisterPrefixSuccessCallback(),
360 [] (const Name&, const std::string& e) {});
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700361
362 do {
363 advanceClocks(time::milliseconds(10), 20);
364 } while (passPacket());
365
Yingdi Yu48967a62016-03-11 22:04:14 -0800366 // Verify that if no response is received, the producer appropriately times out.
367 // The result vector should not contain elements that have timed out.
Junxiao Shic1264c82016-07-14 15:04:07 +0000368 Producer producer(prefix, suffix, face1, dbDir);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700369 producer.createContentKey(testTime, [&] (const std::vector<Data>& result) {
370 BOOST_CHECK_EQUAL(timeoutCount, 4);
371 BOOST_CHECK_EQUAL(result.size(), 0);
372 });
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700373
374 do {
375 advanceClocks(time::milliseconds(10), 500);
376 } while (passPacket());
377}
378
Yingdi Yu48967a62016-03-11 22:04:14 -0800379BOOST_AUTO_TEST_CASE(ProducerWithLink)
380{
381 std::string dbDir = tmpPath.c_str();
382 dbDir += "/test.db";
383
384 Name prefix("/prefix");
385 Name suffix("/suffix");
386 Name expectedInterest = prefix;
387 expectedInterest.append(NAME_COMPONENT_READ);
388 expectedInterest.append(suffix);
389 expectedInterest.append(NAME_COMPONENT_E_KEY);
390
391 time::system_clock::TimePoint testTime = time::fromIsoString("20150101T100001");
392
393 size_t timeoutCount = 0;
394 face2.setInterestFilter(prefix,
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700395 [&] (const InterestFilter&, const Interest& i) {
396 BOOST_CHECK_EQUAL(i.getName(), expectedInterest);
397 BOOST_CHECK(i.getForwardingHint().size() == 3);
398 timeoutCount++;
399 return;
400 },
401 RegisterPrefixSuccessCallback(),
402 [] (const Name&, const std::string& e) {});
Yingdi Yu48967a62016-03-11 22:04:14 -0800403
404 do {
405 advanceClocks(time::milliseconds(10), 20);
406 } while (passPacket());
407
408 // Verify that if no response is received, the producer appropriately times out.
409 // The result vector should not contain elements that have timed out.
410 Link link("test", {{10, "/test1"}, {20, "/test2"}, {100, "/test3"}});
411 keyChain.sign(link);
412 Producer producer(prefix, suffix, face1, dbDir, 3, link);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700413 producer.createContentKey(testTime, [&] (const std::vector<Data>& result) {
414 BOOST_CHECK_EQUAL(timeoutCount, 4);
415 BOOST_CHECK_EQUAL(result.size(), 0);
416 });
Yingdi Yu48967a62016-03-11 22:04:14 -0800417
418 do {
419 advanceClocks(time::milliseconds(10), 800);
420 } while (passPacket());
421}
422
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700423BOOST_AUTO_TEST_SUITE_END()
424
425} // namespace tests
Alexander Afanasyev9091d832018-04-18 17:21:08 -0400426} // namespace nac
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700427} // namespace ndn