blob: 54c39180f9b19c6ec4ee3f261848c4bd4de91fa6 [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
37/**
38 @brief Method to round the provided @p timeslot to the nearest whole
39 hour, so that we can store content keys uniformly (by start of the hour).
40*/
41static const system_clock::TimePoint
42getRoundedTimeslot(const system_clock::TimePoint& timeslot) {
43 return time::fromUnixTimestamp(
44 (time::toUnixTimestamp(timeslot) / 3600000) * 3600000);
45}
46
47Producer::Producer(const Name& prefix, const Name& dataType,
48 Face& face, const std::string& dbPath, uint8_t repeatAttempts)
49 : m_face(face),
50 m_db(dbPath),
51 m_maxRepeatAttempts(repeatAttempts)
52{
53 Name fixedPrefix = prefix;
54 Name fixedDataType = dataType;
55 KeyInfo keyInfo;
56 /**
57 Fill m_ekeyInfo vector with all permutations of dataType, including the 'E-KEY'
58 component of the name. This will be used in DataProducer::createContentKey to
59 send interests without reconstructing names every time.
60 */
61 fixedPrefix.append(NAME_COMPONENT_READ);
62 while (!fixedDataType.empty()) {
63 Name nodeName = fixedPrefix;
64 nodeName.append(fixedDataType);
65 nodeName.append(NAME_COMPONENT_E_KEY);
66
67 m_ekeyInfo[nodeName] = keyInfo;
68 fixedDataType = fixedDataType.getPrefix(-1);
69 }
70 fixedPrefix.append(dataType);
71 m_namespace = prefix;
72 m_namespace.append(NAME_COMPONENT_SAMPLE);
73 m_namespace.append(dataType);
74}
75
76Name
77Producer::createContentKey(const system_clock::TimePoint& timeslot,
Yingdi Yu79ce2392016-03-10 10:21:55 -080078 const ProducerEKeyCallback& callback,
79 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070080{
81 const system_clock::TimePoint hourSlot = getRoundedTimeslot(timeslot);
82
83 // Create content key name.
84 Name contentKeyName = m_namespace;
85 contentKeyName.append(NAME_COMPONENT_C_KEY);
86 contentKeyName.append(time::toIsoString(hourSlot));
87
88 Buffer contentKeyBits;
Yingdi Yu79ce2392016-03-10 10:21:55 -080089
90 // Check if we have created the content key before.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070091 if (m_db.hasContentKey(timeslot)) {
Yingdi Yu79ce2392016-03-10 10:21:55 -080092 // We have created the content key, return its name directly.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070093 return contentKeyName;
94 }
95
Yingdi Yu79ce2392016-03-10 10:21:55 -080096 // We haven't created the content key, create one and add it into the database.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070097 RandomNumberGenerator rng;
98 AesKeyParams aesParams(128);
99 contentKeyBits = algo::Aes::generateKey(rng, aesParams).getKeyBits();
100 m_db.addContentKey(timeslot, contentKeyBits);
101
Yingdi Yu79ce2392016-03-10 10:21:55 -0800102 // Now we need to retrieve the E-KEYs for content key encryption.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700103 uint64_t timeCount = toUnixTimestamp(timeslot).count();
104 m_keyRequests.insert({timeCount, KeyRequest(m_ekeyInfo.size())});
105 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
106
Yingdi Yu79ce2392016-03-10 10:21:55 -0800107 // Check if current E-KEYs can cover the content key.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700108 Exclude timeRange;
109 timeRange.excludeAfter(name::Component(time::toIsoString(timeslot)));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700110 std::unordered_map<Name, KeyInfo>::iterator it;
111 for (it = m_ekeyInfo.begin(); it != m_ekeyInfo.end(); ++it) {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800112 // for each current E-KEY
113 if (timeslot < it->second.beginTimeslot || timeslot >= it->second.endTimeslot) {
114 // current E-KEY cannot cover the content key, retrieve one.
115 keyRequest.repeatAttempts[it->first] = 0;
116 sendKeyInterest(Interest(it->first).setExclude(timeRange).setChildSelector(1),
117 timeslot, callback, errorCallback);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700118 }
119 else {
Yingdi Yu79ce2392016-03-10 10:21:55 -0800120 // current E-KEY can cover the content key, encrypt the content key directly.
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700121 Name eKeyName(it->first);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800122 eKeyName.append(time::toIsoString(it->second.beginTimeslot));
123 eKeyName.append(time::toIsoString(it->second.endTimeslot));
124 encryptContentKey(it->second.keyBits, eKeyName, timeslot, callback, errorCallback);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700125 }
126 }
127
128 return contentKeyName;
129}
130
131void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800132Producer::defaultErrorCallBack(const ErrorCode& code, const std::string& msg)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700133{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800134 // do nothing.
135}
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700136
Yingdi Yu79ce2392016-03-10 10:21:55 -0800137void
138Producer::produce(Data& data, const system_clock::TimePoint& timeslot,
139 const uint8_t* content, size_t contentLen,
140 const ErrorCallBack& errorCallBack)
141{
142 // Get a content key
143 Name contentKeyName = createContentKey(timeslot, nullptr, errorCallBack);
144 Buffer contentKey = m_db.getContentKey(timeslot);
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700145
Yingdi Yu79ce2392016-03-10 10:21:55 -0800146 // Produce data
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700147 Name dataName = m_namespace;
148 dataName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700149 data.setName(dataName);
150 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
151 algo::encryptData(data, content, contentLen, contentKeyName,
152 contentKey.buf(), contentKey.size(), params);
153 m_keychain.sign(data);
154}
155
156void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800157Producer::sendKeyInterest(const Interest& interest,
158 const system_clock::TimePoint& timeslot,
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700159 const ProducerEKeyCallback& callback,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800160 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700161{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800162 m_face.expressInterest(interest,
163 std::bind(&Producer::handleCoveringKey, this, _1, _2,
164 timeslot, callback, errorCallback),
165 std::bind(&Producer::handleNack, this, _1, _2,
166 timeslot, callback),
167 std::bind(&Producer::handleTimeout, this, _1,
168 timeslot, callback, errorCallback));
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700169}
170
171void
Yingdi Yu79ce2392016-03-10 10:21:55 -0800172Producer::handleCoveringKey(const Interest& interest, const Data& data,
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700173 const system_clock::TimePoint& timeslot,
Yingdi Yu79ce2392016-03-10 10:21:55 -0800174 const ProducerEKeyCallback& callback,
175 const ErrorCallBack& errorCallback)
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700176{
Yingdi Yu79ce2392016-03-10 10:21:55 -0800177 uint64_t timeCount = toUnixTimestamp(timeslot).count();
178 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
179
180 Name interestName = interest.getName();
181 Name keyName = data.getName();
182
183 system_clock::TimePoint begin = time::fromIsoString(keyName.get(START_TS_INDEX).toUri());
184 system_clock::TimePoint end = time::fromIsoString(keyName.get(END_TS_INDEX).toUri());
185
186 if (timeslot >= end) {
187 // if received E-KEY covers some earlier period, try to retrieve an E-KEY covering later one.
188 keyRequest.repeatAttempts[interestName] = 0;
189
190 Exclude timeRange = interest.getSelectors().getExclude();
191 timeRange.excludeBefore(keyName.get(START_TS_INDEX));
192
193 sendKeyInterest(Interest(interestName).setExclude(timeRange).setChildSelector(1),
194 timeslot, callback, errorCallback);
195 }
196 else {
197 // if received E-KEY covers the content key, encrypt the content
198 Buffer encryptionKey(data.getContent().value(), data.getContent().value_size());
199 // if everything is correct, save the E-KEY as the current key
200 if (encryptContentKey(encryptionKey, keyName, timeslot, callback, errorCallback)) {
201 m_ekeyInfo[interestName].beginTimeslot = begin;
202 m_ekeyInfo[interestName].endTimeslot = end;
203 m_ekeyInfo[interestName].keyBits = encryptionKey;
204 }
205 }
206}
207
208void
209Producer::handleTimeout(const Interest& interest,
210 const system_clock::TimePoint& timeslot,
211 const ProducerEKeyCallback& callback,
212 const ErrorCallBack& errorCallback)
213{
214 uint64_t timeCount = toUnixTimestamp(timeslot).count();
215 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
216
217 Name interestName = interest.getName();
218 if (keyRequest.repeatAttempts[interestName] < m_maxRepeatAttempts) {
219 // increase retrial count
220 keyRequest.repeatAttempts[interestName]++;
221 sendKeyInterest(interest, timeslot, callback, errorCallback);
222 }
223 else {
224 // no more retrial
225 updateKeyRequest(keyRequest, timeCount, callback);
226 }
227}
228
229void
230Producer::handleNack(const Interest& interest,
231 const lp::Nack& nack,
232 const system_clock::TimePoint& timeslot,
233 const ProducerEKeyCallback& callback)
234{
235 uint64_t timeCount = toUnixTimestamp(timeslot).count();
236 updateKeyRequest(m_keyRequests.at(timeCount), timeCount, callback);
237}
238
239void
240Producer::updateKeyRequest(KeyRequest& keyRequest, uint64_t timeCount,
241 const ProducerEKeyCallback& callback)
242{
243 keyRequest.interestCount--;
244 if (keyRequest.interestCount == 0 && callback) {
245 callback(keyRequest.encryptedKeys);
246 m_keyRequests.erase(timeCount);
247 }
248}
249
250bool
251Producer::encryptContentKey(const Buffer& encryptionKey, const Name& eKeyName,
252 const system_clock::TimePoint& timeslot,
253 const ProducerEKeyCallback& callback,
254 const ErrorCallBack& errorCallBack)
255{
256 uint64_t timeCount = toUnixTimestamp(timeslot).count();
257 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
258
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700259 Name keyName = m_namespace;
260 keyName.append(NAME_COMPONENT_C_KEY);
261 keyName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
262
263 Buffer contentKey = m_db.getContentKey(timeslot);
264
265 Data cKeyData;
266 cKeyData.setName(keyName);
267 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800268 try {
269 algo::encryptData(cKeyData, contentKey.buf(), contentKey.size(), eKeyName,
270 encryptionKey.buf(), encryptionKey.size(), params);
271 }
272 catch (algo::Error& e) {
273 errorCallBack(ErrorCode::EncryptionFailure, e.what());
274 return false;
275 }
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700276 m_keychain.sign(cKeyData);
277 keyRequest.encryptedKeys.push_back(cKeyData);
Yingdi Yu79ce2392016-03-10 10:21:55 -0800278 updateKeyRequest(keyRequest, timeCount, callback);
279 return true;
Prashanth Swaminathanb2105902015-08-20 14:28:54 -0700280}
281
282} // namespace gep
Yingdi Yu266badb2016-03-09 18:58:27 -0800283} // namespace ndn