blob: ac9aca09337786ad19fbac5368bc3fddf0215800 [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 *
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 * @author Prashanth Swaminathan <prashanthsw@gmail.com>
Yingdi Yu79ce2392016-03-10 10:21:55 -080020 * @author Yingdi Yu <yuyingdi@gmail.com>
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070021 * @author Zhiyi Zhang <zhiyi@cs.ucla.edu>
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070022 */
23
24#include "producer.hpp"
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070025#include "algo/aes.hpp"
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070026#include "algo/encryptor.hpp"
Yingdi Yu79ce2392016-03-10 10:21:55 -080027#include "algo/error.hpp"
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070028#include <iostream>
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070029
30namespace ndn {
31namespace gep {
32
33using time::system_clock;
34
Yingdi Yu79ce2392016-03-10 10:21:55 -080035static const int START_TS_INDEX = -2;
36static const int END_TS_INDEX = -1;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070037
Yingdi Yu48967a62016-03-11 22:04:14 -080038const Link Producer::NO_LINK = Link();
39
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070040/**
41 @brief Method to round the provided @p timeslot to the nearest whole
42 hour, so that we can store content keys uniformly (by start of the hour).
43*/
44static const system_clock::TimePoint
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070045getRoundedTimeslot(const system_clock::TimePoint& timeslot)
46{
47 return time::fromUnixTimestamp((time::toUnixTimestamp(timeslot) / 3600000) * 3600000);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070048}
49
Zhiyi Zhang19a11d22018-04-12 22:58:20 -070050Producer::Producer(const Name& prefix,
51 const Name& dataType,
52 Face& face,
53 const std::string& dbPath,
Yingdi Yu48967a62016-03-11 22:04:14 -080054 uint8_t repeatAttempts,
55 const Link& keyRetrievalLink)
56 : m_face(face)
57 , m_db(dbPath)
58 , m_maxRepeatAttempts(repeatAttempts)
59 , m_keyRetrievalLink(keyRetrievalLink)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070060{
61 Name fixedPrefix = prefix;
62 Name fixedDataType = dataType;
63 KeyInfo keyInfo;
64 /**
65 Fill m_ekeyInfo vector with all permutations of dataType, including the 'E-KEY'
66 component of the name. This will be used in DataProducer::createContentKey to
67 send interests without reconstructing names every time.
68 */
69 fixedPrefix.append(NAME_COMPONENT_READ);
70 while (!fixedDataType.empty()) {
71 Name nodeName = fixedPrefix;
72 nodeName.append(fixedDataType);
73 nodeName.append(NAME_COMPONENT_E_KEY);
74
75 m_ekeyInfo[nodeName] = keyInfo;
76 fixedDataType = fixedDataType.getPrefix(-1);
77 }
78 fixedPrefix.append(dataType);
79 m_namespace = prefix;
80 m_namespace.append(NAME_COMPONENT_SAMPLE);
81 m_namespace.append(dataType);
82}
83
84Name
85Producer::createContentKey(const system_clock::TimePoint& timeslot,
Yingdi Yu79ce2392016-03-10 10:21:55 -080086 const ProducerEKeyCallback& callback,
87 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070088{
89 const system_clock::TimePoint hourSlot = getRoundedTimeslot(timeslot);
90
91 // Create content key name.
92 Name contentKeyName = m_namespace;
93 contentKeyName.append(NAME_COMPONENT_C_KEY);
94 contentKeyName.append(time::toIsoString(hourSlot));
95
96 Buffer contentKeyBits;
Yingdi Yu79ce2392016-03-10 10:21:55 -080097
98 // Check if we have created the content key before.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070099 if (m_db.hasContentKey(timeslot)) {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800100 // We have created the content key, return its name directly.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700101 return contentKeyName;
102 }
103
Yingdi Yu79ce2392016-03-10 10:21:55 -0800104 // We haven't created the content key, create one and add it into the database.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700105 AesKeyParams aesParams(128);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700106 contentKeyBits = algo::Aes::generateKey(aesParams).getKeyBits();
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700107 m_db.addContentKey(timeslot, contentKeyBits);
108
Yingdi Yu79ce2392016-03-10 10:21:55 -0800109 // Now we need to retrieve the E-KEYs for content key encryption.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700110 uint64_t timeCount = toUnixTimestamp(timeslot).count();
111 m_keyRequests.insert({timeCount, KeyRequest(m_ekeyInfo.size())});
112 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
113
Yingdi Yu79ce2392016-03-10 10:21:55 -0800114 // Check if current E-KEYs can cover the content key.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700115 Exclude timeRange;
116 timeRange.excludeAfter(name::Component(time::toIsoString(timeslot)));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700117 std::unordered_map<Name, KeyInfo>::iterator it;
118 for (it = m_ekeyInfo.begin(); it != m_ekeyInfo.end(); ++it) {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800119 // for each current E-KEY
120 if (timeslot < it->second.beginTimeslot || timeslot >= it->second.endTimeslot) {
121 // current E-KEY cannot cover the content key, retrieve one.
122 keyRequest.repeatAttempts[it->first] = 0;
123 sendKeyInterest(Interest(it->first).setExclude(timeRange).setChildSelector(1),
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700124 timeslot,
125 callback,
126 errorCallback);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700127 }
128 else {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800129 // current E-KEY can cover the content key, encrypt the content key directly.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700130 Name eKeyName(it->first);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800131 eKeyName.append(time::toIsoString(it->second.beginTimeslot));
132 eKeyName.append(time::toIsoString(it->second.endTimeslot));
133 encryptContentKey(it->second.keyBits, eKeyName, timeslot, callback, errorCallback);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700134 }
135 }
136
137 return contentKeyName;
138}
139
140void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800141Producer::defaultErrorCallBack(const ErrorCode& code, const std::string& msg)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700142{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800143 // do nothing.
144}
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700145
Yingdi Yu79ce2392016-03-10 10:21:55 -0800146void
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700147Producer::produce(Data& data,
148 const system_clock::TimePoint& timeslot,
149 const uint8_t* content,
150 size_t contentLen,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800151 const ErrorCallBack& errorCallBack)
152{
153 // Get a content key
154 Name contentKeyName = createContentKey(timeslot, nullptr, errorCallBack);
155 Buffer contentKey = m_db.getContentKey(timeslot);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700156
Yingdi Yu79ce2392016-03-10 10:21:55 -0800157 // Produce data
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700158 Name dataName = m_namespace;
Yingdi Yu0c530b72016-03-20 18:19:14 -0700159 dataName.append(time::toIsoString(timeslot));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700160 data.setName(dataName);
161 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
162 algo::encryptData(data, content, contentLen, contentKeyName,
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700163 contentKey.data(), contentKey.size(), params);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700164 m_keychain.sign(data);
165}
166
167void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800168Producer::sendKeyInterest(const Interest& interest,
169 const system_clock::TimePoint& timeslot,
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700170 const ProducerEKeyCallback& callback,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800171 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700172{
Yingdi Yu48967a62016-03-11 22:04:14 -0800173 Interest request(interest);
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700174 if (m_keyRetrievalLink.getDelegationList().size() > 0) {
175 request.setForwardingHint(m_keyRetrievalLink.getDelegationList());
Yingdi Yu48967a62016-03-11 22:04:14 -0800176 }
177 m_face.expressInterest(request,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800178 std::bind(&Producer::handleCoveringKey, this, _1, _2,
179 timeslot, callback, errorCallback),
180 std::bind(&Producer::handleNack, this, _1, _2,
Yingdi Yu48967a62016-03-11 22:04:14 -0800181 timeslot, callback, errorCallback),
Yingdi Yu79ce2392016-03-10 10:21:55 -0800182 std::bind(&Producer::handleTimeout, this, _1,
183 timeslot, callback, errorCallback));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700184}
185
186void
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700187Producer::handleCoveringKey(const Interest& interest,
188 const Data& data,
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700189 const system_clock::TimePoint& timeslot,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800190 const ProducerEKeyCallback& callback,
191 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700192{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800193 uint64_t timeCount = toUnixTimestamp(timeslot).count();
194 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
195
196 Name interestName = interest.getName();
197 Name keyName = data.getName();
198
199 system_clock::TimePoint begin = time::fromIsoString(keyName.get(START_TS_INDEX).toUri());
200 system_clock::TimePoint end = time::fromIsoString(keyName.get(END_TS_INDEX).toUri());
201
202 if (timeslot >= end) {
203 // if received E-KEY covers some earlier period, try to retrieve an E-KEY covering later one.
204 keyRequest.repeatAttempts[interestName] = 0;
205
206 Exclude timeRange = interest.getSelectors().getExclude();
207 timeRange.excludeBefore(keyName.get(START_TS_INDEX));
208
209 sendKeyInterest(Interest(interestName).setExclude(timeRange).setChildSelector(1),
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700210 timeslot,
211 callback,
212 errorCallback);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800213 }
214 else {
215 // if received E-KEY covers the content key, encrypt the content
216 Buffer encryptionKey(data.getContent().value(), data.getContent().value_size());
217 // if everything is correct, save the E-KEY as the current key
218 if (encryptContentKey(encryptionKey, keyName, timeslot, callback, errorCallback)) {
219 m_ekeyInfo[interestName].beginTimeslot = begin;
220 m_ekeyInfo[interestName].endTimeslot = end;
221 m_ekeyInfo[interestName].keyBits = encryptionKey;
222 }
223 }
224}
225
226void
227Producer::handleTimeout(const Interest& interest,
228 const system_clock::TimePoint& timeslot,
229 const ProducerEKeyCallback& callback,
230 const ErrorCallBack& errorCallback)
231{
232 uint64_t timeCount = toUnixTimestamp(timeslot).count();
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700233 if (m_keyRequests.find(timeCount) == m_keyRequests.end()) {
234 return;
235 }
Yingdi Yu79ce2392016-03-10 10:21:55 -0800236 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
237
238 Name interestName = interest.getName();
239 if (keyRequest.repeatAttempts[interestName] < m_maxRepeatAttempts) {
240 // increase retrial count
241 keyRequest.repeatAttempts[interestName]++;
242 sendKeyInterest(interest, timeslot, callback, errorCallback);
243 }
244 else {
Yingdi Yu48967a62016-03-11 22:04:14 -0800245 // treat eventual timeout as a NACK
246 handleNack(interest, lp::Nack(), timeslot, callback, errorCallback);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800247 }
248}
249
250void
251Producer::handleNack(const Interest& interest,
252 const lp::Nack& nack,
253 const system_clock::TimePoint& timeslot,
Yingdi Yu48967a62016-03-11 22:04:14 -0800254 const ProducerEKeyCallback& callback,
255 const ErrorCallBack& errorCallback)
Yingdi Yu79ce2392016-03-10 10:21:55 -0800256{
Yingdi Yu48967a62016-03-11 22:04:14 -0800257 // we run out of options...
Yingdi Yu79ce2392016-03-10 10:21:55 -0800258 uint64_t timeCount = toUnixTimestamp(timeslot).count();
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700259
260 if (m_keyRequests.find(timeCount) != m_keyRequests.end()) {
261 updateKeyRequest(m_keyRequests.at(timeCount), timeCount, callback);
262 }
Yingdi Yu79ce2392016-03-10 10:21:55 -0800263}
264
265void
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700266Producer::updateKeyRequest(KeyRequest& keyRequest,
267 uint64_t timeCount,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800268 const ProducerEKeyCallback& callback)
269{
270 keyRequest.interestCount--;
271 if (keyRequest.interestCount == 0 && callback) {
272 callback(keyRequest.encryptedKeys);
273 m_keyRequests.erase(timeCount);
274 }
275}
276
277bool
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700278Producer::encryptContentKey(const Buffer& encryptionKey,
279 const Name& eKeyName,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800280 const system_clock::TimePoint& timeslot,
281 const ProducerEKeyCallback& callback,
282 const ErrorCallBack& errorCallBack)
283{
284 uint64_t timeCount = toUnixTimestamp(timeslot).count();
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700285 if (m_keyRequests.find(timeCount) == m_keyRequests.end()) {
286 return false;
287 }
Yingdi Yu79ce2392016-03-10 10:21:55 -0800288 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
289
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700290 Name keyName = m_namespace;
291 keyName.append(NAME_COMPONENT_C_KEY);
292 keyName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
293
294 Buffer contentKey = m_db.getContentKey(timeslot);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700295 Data cKeyData;
296 cKeyData.setName(keyName);
297 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800298 try {
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700299 algo::encryptData(cKeyData,
300 contentKey.data(),
301 contentKey.size(),
302 eKeyName,
303 encryptionKey.data(),
304 encryptionKey.size(),
305 params);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800306 }
Zhiyi Zhang19a11d22018-04-12 22:58:20 -0700307 catch (const algo::Error& e) {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800308 errorCallBack(ErrorCode::EncryptionFailure, e.what());
309 return false;
310 }
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700311 m_keychain.sign(cKeyData);
312 keyRequest.encryptedKeys.push_back(cKeyData);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800313 updateKeyRequest(keyRequest, timeCount, callback);
314 return true;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700315}
316
317} // namespace gep
Yingdi Yu266badb2016-03-09 18:58:27 -0800318} // namespace ndn