blob: 88bd5c11f86652cb093e3c6787788e59efe061ab [file] [log] [blame]
Prashanth Swaminathanb2105902015-08-20 14:28:54 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Regents of the University of California
4 *
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>
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070021 */
22
23#include "producer.hpp"
24#include "random-number-generator.hpp"
25#include "algo/encryptor.hpp"
26#include "algo/aes.hpp"
Yingdi Yu79ce2392016-03-10 10:21:55 -080027#include "algo/error.hpp"
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070028
29namespace ndn {
30namespace gep {
31
32using time::system_clock;
33
Yingdi Yu79ce2392016-03-10 10:21:55 -080034static const int START_TS_INDEX = -2;
35static const int END_TS_INDEX = -1;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070036
Yingdi Yu48967a62016-03-11 22:04:14 -080037const Link Producer::NO_LINK = Link();
38
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070039/**
40 @brief Method to round the provided @p timeslot to the nearest whole
41 hour, so that we can store content keys uniformly (by start of the hour).
42*/
43static const system_clock::TimePoint
44getRoundedTimeslot(const system_clock::TimePoint& timeslot) {
45 return time::fromUnixTimestamp(
46 (time::toUnixTimestamp(timeslot) / 3600000) * 3600000);
47}
48
49Producer::Producer(const Name& prefix, const Name& dataType,
Yingdi Yu48967a62016-03-11 22:04:14 -080050 Face& face, const std::string& dbPath,
51 uint8_t repeatAttempts,
52 const Link& keyRetrievalLink)
53 : m_face(face)
54 , m_db(dbPath)
55 , m_maxRepeatAttempts(repeatAttempts)
56 , m_keyRetrievalLink(keyRetrievalLink)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070057{
58 Name fixedPrefix = prefix;
59 Name fixedDataType = dataType;
60 KeyInfo keyInfo;
61 /**
62 Fill m_ekeyInfo vector with all permutations of dataType, including the 'E-KEY'
63 component of the name. This will be used in DataProducer::createContentKey to
64 send interests without reconstructing names every time.
65 */
66 fixedPrefix.append(NAME_COMPONENT_READ);
67 while (!fixedDataType.empty()) {
68 Name nodeName = fixedPrefix;
69 nodeName.append(fixedDataType);
70 nodeName.append(NAME_COMPONENT_E_KEY);
71
72 m_ekeyInfo[nodeName] = keyInfo;
73 fixedDataType = fixedDataType.getPrefix(-1);
74 }
75 fixedPrefix.append(dataType);
76 m_namespace = prefix;
77 m_namespace.append(NAME_COMPONENT_SAMPLE);
78 m_namespace.append(dataType);
79}
80
81Name
82Producer::createContentKey(const system_clock::TimePoint& timeslot,
Yingdi Yu79ce2392016-03-10 10:21:55 -080083 const ProducerEKeyCallback& callback,
84 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070085{
86 const system_clock::TimePoint hourSlot = getRoundedTimeslot(timeslot);
87
88 // Create content key name.
89 Name contentKeyName = m_namespace;
90 contentKeyName.append(NAME_COMPONENT_C_KEY);
91 contentKeyName.append(time::toIsoString(hourSlot));
92
93 Buffer contentKeyBits;
Yingdi Yu79ce2392016-03-10 10:21:55 -080094
95 // Check if we have created the content key before.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070096 if (m_db.hasContentKey(timeslot)) {
Yingdi Yu79ce2392016-03-10 10:21:55 -080097 // We have created the content key, return its name directly.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070098 return contentKeyName;
99 }
100
Yingdi Yu79ce2392016-03-10 10:21:55 -0800101 // We haven't created the content key, create one and add it into the database.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700102 RandomNumberGenerator rng;
103 AesKeyParams aesParams(128);
104 contentKeyBits = algo::Aes::generateKey(rng, aesParams).getKeyBits();
105 m_db.addContentKey(timeslot, contentKeyBits);
106
Yingdi Yu79ce2392016-03-10 10:21:55 -0800107 // Now we need to retrieve the E-KEYs for content key encryption.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700108 uint64_t timeCount = toUnixTimestamp(timeslot).count();
109 m_keyRequests.insert({timeCount, KeyRequest(m_ekeyInfo.size())});
110 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
111
Yingdi Yu79ce2392016-03-10 10:21:55 -0800112 // Check if current E-KEYs can cover the content key.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700113 Exclude timeRange;
114 timeRange.excludeAfter(name::Component(time::toIsoString(timeslot)));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700115 std::unordered_map<Name, KeyInfo>::iterator it;
116 for (it = m_ekeyInfo.begin(); it != m_ekeyInfo.end(); ++it) {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800117 // for each current E-KEY
118 if (timeslot < it->second.beginTimeslot || timeslot >= it->second.endTimeslot) {
119 // current E-KEY cannot cover the content key, retrieve one.
120 keyRequest.repeatAttempts[it->first] = 0;
121 sendKeyInterest(Interest(it->first).setExclude(timeRange).setChildSelector(1),
122 timeslot, callback, errorCallback);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700123 }
124 else {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800125 // current E-KEY can cover the content key, encrypt the content key directly.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700126 Name eKeyName(it->first);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800127 eKeyName.append(time::toIsoString(it->second.beginTimeslot));
128 eKeyName.append(time::toIsoString(it->second.endTimeslot));
129 encryptContentKey(it->second.keyBits, eKeyName, timeslot, callback, errorCallback);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700130 }
131 }
132
133 return contentKeyName;
134}
135
136void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800137Producer::defaultErrorCallBack(const ErrorCode& code, const std::string& msg)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700138{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800139 // do nothing.
140}
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700141
Yingdi Yu79ce2392016-03-10 10:21:55 -0800142void
143Producer::produce(Data& data, const system_clock::TimePoint& timeslot,
144 const uint8_t* content, size_t contentLen,
145 const ErrorCallBack& errorCallBack)
146{
147 // Get a content key
148 Name contentKeyName = createContentKey(timeslot, nullptr, errorCallBack);
149 Buffer contentKey = m_db.getContentKey(timeslot);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700150
Yingdi Yu79ce2392016-03-10 10:21:55 -0800151 // Produce data
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700152 Name dataName = m_namespace;
Yingdi Yu0c530b72016-03-20 18:19:14 -0700153 dataName.append(time::toIsoString(timeslot));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700154 data.setName(dataName);
155 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
156 algo::encryptData(data, content, contentLen, contentKeyName,
157 contentKey.buf(), contentKey.size(), params);
158 m_keychain.sign(data);
159}
160
161void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800162Producer::sendKeyInterest(const Interest& interest,
163 const system_clock::TimePoint& timeslot,
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700164 const ProducerEKeyCallback& callback,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800165 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700166{
Yingdi Yu48967a62016-03-11 22:04:14 -0800167 Interest request(interest);
168 if (m_keyRetrievalLink.getDelegations().size() > 0) {
169 request.setLink(m_keyRetrievalLink.wireEncode());
170 }
171 m_face.expressInterest(request,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800172 std::bind(&Producer::handleCoveringKey, this, _1, _2,
173 timeslot, callback, errorCallback),
174 std::bind(&Producer::handleNack, this, _1, _2,
Yingdi Yu48967a62016-03-11 22:04:14 -0800175 timeslot, callback, errorCallback),
Yingdi Yu79ce2392016-03-10 10:21:55 -0800176 std::bind(&Producer::handleTimeout, this, _1,
177 timeslot, callback, errorCallback));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700178}
179
180void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800181Producer::handleCoveringKey(const Interest& interest, const Data& data,
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700182 const system_clock::TimePoint& timeslot,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800183 const ProducerEKeyCallback& callback,
184 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700185{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800186 uint64_t timeCount = toUnixTimestamp(timeslot).count();
187 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
188
189 Name interestName = interest.getName();
190 Name keyName = data.getName();
191
192 system_clock::TimePoint begin = time::fromIsoString(keyName.get(START_TS_INDEX).toUri());
193 system_clock::TimePoint end = time::fromIsoString(keyName.get(END_TS_INDEX).toUri());
194
195 if (timeslot >= end) {
196 // if received E-KEY covers some earlier period, try to retrieve an E-KEY covering later one.
197 keyRequest.repeatAttempts[interestName] = 0;
198
199 Exclude timeRange = interest.getSelectors().getExclude();
200 timeRange.excludeBefore(keyName.get(START_TS_INDEX));
201
202 sendKeyInterest(Interest(interestName).setExclude(timeRange).setChildSelector(1),
203 timeslot, callback, errorCallback);
204 }
205 else {
206 // if received E-KEY covers the content key, encrypt the content
207 Buffer encryptionKey(data.getContent().value(), data.getContent().value_size());
208 // if everything is correct, save the E-KEY as the current key
209 if (encryptContentKey(encryptionKey, keyName, timeslot, callback, errorCallback)) {
210 m_ekeyInfo[interestName].beginTimeslot = begin;
211 m_ekeyInfo[interestName].endTimeslot = end;
212 m_ekeyInfo[interestName].keyBits = encryptionKey;
213 }
214 }
215}
216
217void
218Producer::handleTimeout(const Interest& interest,
219 const system_clock::TimePoint& timeslot,
220 const ProducerEKeyCallback& callback,
221 const ErrorCallBack& errorCallback)
222{
223 uint64_t timeCount = toUnixTimestamp(timeslot).count();
224 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
225
226 Name interestName = interest.getName();
227 if (keyRequest.repeatAttempts[interestName] < m_maxRepeatAttempts) {
228 // increase retrial count
229 keyRequest.repeatAttempts[interestName]++;
230 sendKeyInterest(interest, timeslot, callback, errorCallback);
231 }
232 else {
Yingdi Yu48967a62016-03-11 22:04:14 -0800233 // treat eventual timeout as a NACK
234 handleNack(interest, lp::Nack(), timeslot, callback, errorCallback);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800235 }
236}
237
238void
239Producer::handleNack(const Interest& interest,
240 const lp::Nack& nack,
241 const system_clock::TimePoint& timeslot,
Yingdi Yu48967a62016-03-11 22:04:14 -0800242 const ProducerEKeyCallback& callback,
243 const ErrorCallBack& errorCallback)
Yingdi Yu79ce2392016-03-10 10:21:55 -0800244{
Yingdi Yu48967a62016-03-11 22:04:14 -0800245 // we run out of options...
Yingdi Yu79ce2392016-03-10 10:21:55 -0800246 uint64_t timeCount = toUnixTimestamp(timeslot).count();
247 updateKeyRequest(m_keyRequests.at(timeCount), timeCount, callback);
248}
249
250void
251Producer::updateKeyRequest(KeyRequest& keyRequest, uint64_t timeCount,
252 const ProducerEKeyCallback& callback)
253{
254 keyRequest.interestCount--;
255 if (keyRequest.interestCount == 0 && callback) {
256 callback(keyRequest.encryptedKeys);
257 m_keyRequests.erase(timeCount);
258 }
259}
260
261bool
262Producer::encryptContentKey(const Buffer& encryptionKey, const Name& eKeyName,
263 const system_clock::TimePoint& timeslot,
264 const ProducerEKeyCallback& callback,
265 const ErrorCallBack& errorCallBack)
266{
267 uint64_t timeCount = toUnixTimestamp(timeslot).count();
268 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
269
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700270 Name keyName = m_namespace;
271 keyName.append(NAME_COMPONENT_C_KEY);
272 keyName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
273
274 Buffer contentKey = m_db.getContentKey(timeslot);
275
276 Data cKeyData;
277 cKeyData.setName(keyName);
278 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800279 try {
280 algo::encryptData(cKeyData, contentKey.buf(), contentKey.size(), eKeyName,
281 encryptionKey.buf(), encryptionKey.size(), params);
282 }
283 catch (algo::Error& e) {
284 errorCallBack(ErrorCode::EncryptionFailure, e.what());
285 return false;
286 }
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700287 m_keychain.sign(cKeyData);
288 keyRequest.encryptedKeys.push_back(cKeyData);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800289 updateKeyRequest(keyRequest, timeCount, callback);
290 return true;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700291}
292
293} // namespace gep
Yingdi Yu266badb2016-03-09 18:58:27 -0800294} // namespace ndn