blob: 81c13495ff35b62672ba77a1ef7d2be094dbecdf [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>
20 */
21
22#include "producer.hpp"
23#include "random-number-generator.hpp"
24#include "algo/encryptor.hpp"
25#include "algo/aes.hpp"
26
27namespace ndn {
28namespace gep {
29
30using time::system_clock;
31
32static const int startTs = -2;
33static const int endTs = -1;
34
35/**
36 @brief Method to round the provided @p timeslot to the nearest whole
37 hour, so that we can store content keys uniformly (by start of the hour).
38*/
39static const system_clock::TimePoint
40getRoundedTimeslot(const system_clock::TimePoint& timeslot) {
41 return time::fromUnixTimestamp(
42 (time::toUnixTimestamp(timeslot) / 3600000) * 3600000);
43}
44
45Producer::Producer(const Name& prefix, const Name& dataType,
46 Face& face, const std::string& dbPath, uint8_t repeatAttempts)
47 : m_face(face),
48 m_db(dbPath),
49 m_maxRepeatAttempts(repeatAttempts)
50{
51 Name fixedPrefix = prefix;
52 Name fixedDataType = dataType;
53 KeyInfo keyInfo;
54 /**
55 Fill m_ekeyInfo vector with all permutations of dataType, including the 'E-KEY'
56 component of the name. This will be used in DataProducer::createContentKey to
57 send interests without reconstructing names every time.
58 */
59 fixedPrefix.append(NAME_COMPONENT_READ);
60 while (!fixedDataType.empty()) {
61 Name nodeName = fixedPrefix;
62 nodeName.append(fixedDataType);
63 nodeName.append(NAME_COMPONENT_E_KEY);
64
65 m_ekeyInfo[nodeName] = keyInfo;
66 fixedDataType = fixedDataType.getPrefix(-1);
67 }
68 fixedPrefix.append(dataType);
69 m_namespace = prefix;
70 m_namespace.append(NAME_COMPONENT_SAMPLE);
71 m_namespace.append(dataType);
72}
73
74Name
75Producer::createContentKey(const system_clock::TimePoint& timeslot,
76 const ProducerEKeyCallback& callback)
77{
78 const system_clock::TimePoint hourSlot = getRoundedTimeslot(timeslot);
79
80 // Create content key name.
81 Name contentKeyName = m_namespace;
82 contentKeyName.append(NAME_COMPONENT_C_KEY);
83 contentKeyName.append(time::toIsoString(hourSlot));
84
85 Buffer contentKeyBits;
86 if (m_db.hasContentKey(timeslot)) {
Prashanth Swaminathanb2105902015-08-20 14:28:54 -070087 return contentKeyName;
88 }
89
90 RandomNumberGenerator rng;
91 AesKeyParams aesParams(128);
92 contentKeyBits = algo::Aes::generateKey(rng, aesParams).getKeyBits();
93 m_db.addContentKey(timeslot, contentKeyBits);
94
95 uint64_t timeCount = toUnixTimestamp(timeslot).count();
96 m_keyRequests.insert({timeCount, KeyRequest(m_ekeyInfo.size())});
97 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
98
99 Exclude timeRange;
100 timeRange.excludeAfter(name::Component(time::toIsoString(timeslot)));
101 // Send interests for all nodes in tree.
102 std::unordered_map<Name, KeyInfo>::iterator it;
103 for (it = m_ekeyInfo.begin(); it != m_ekeyInfo.end(); ++it) {
104 const KeyInfo& keyInfo = it->second;
105 keyRequest.repeatAttempts.insert({it->first, 0});
106 if (timeslot < keyInfo.beginTimeslot || timeslot >= keyInfo.endTimeslot) {
107 sendKeyInterest(it->first, timeslot, keyRequest, callback, timeRange);
108 }
109 else {
110 Name eKeyName(it->first);
111 eKeyName.append(time::toIsoString(keyInfo.beginTimeslot));
112 eKeyName.append(time::toIsoString(keyInfo.endTimeslot));
113 encryptContentKey(keyRequest, keyInfo.keyBits, eKeyName, timeslot, callback);
114 }
115 }
116
117 return contentKeyName;
118}
119
120void
121Producer::produce(Data& data, const system_clock::TimePoint& timeslot,
122 const uint8_t* content, size_t contentLen)
123{
124 Buffer contentKey;
125
126 Name contentKeyName = createContentKey(timeslot, nullptr);
127 contentKey = m_db.getContentKey(timeslot);
128
129 Name dataName = m_namespace;
130 dataName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
131
132 data.setName(dataName);
133 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
134 algo::encryptData(data, content, contentLen, contentKeyName,
135 contentKey.buf(), contentKey.size(), params);
136 m_keychain.sign(data);
137}
138
139void
140Producer::sendKeyInterest(const Name& name, const system_clock::TimePoint& timeslot,
141 KeyRequest& keyRequest,
142 const ProducerEKeyCallback& callback,
143 const Exclude& timeRange)
144{
145 auto onkey = std::bind(&Producer::handleCoveringKey, this, _1, _2,
146 std::cref(timeslot), std::ref(keyRequest), callback);
147 auto timeout = std::bind(&Producer::handleTimeout, this, _1,
148 std::cref(timeslot), std::ref(keyRequest), callback);
149
150 Selectors selector;
151 selector.setExclude(timeRange);
152 selector.setChildSelector(1);
153
154 Interest keyInterest(name);
155 keyInterest.setSelectors(selector);
156
157 m_face.expressInterest(keyInterest, onkey, timeout);
158}
159
160void
161Producer::encryptContentKey(KeyRequest& keyRequest, const Buffer& encryptionKey,
162 const Name& eKeyName,
163 const system_clock::TimePoint& timeslot,
164 const ProducerEKeyCallback& callback)
165{
166 Name keyName = m_namespace;
167 keyName.append(NAME_COMPONENT_C_KEY);
168 keyName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
169
170 Buffer contentKey = m_db.getContentKey(timeslot);
171
172 Data cKeyData;
173 cKeyData.setName(keyName);
174 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
175 algo::encryptData(cKeyData, contentKey.buf(), contentKey.size(), eKeyName,
176 encryptionKey.buf(), encryptionKey.size(), params);
177 m_keychain.sign(cKeyData);
178 keyRequest.encryptedKeys.push_back(cKeyData);
179
180 keyRequest.interestCount--;
181 if (keyRequest.interestCount == 0 && callback) {
182 callback(keyRequest.encryptedKeys);
183 m_keyRequests.erase(toUnixTimestamp(timeslot).count());
184 }
185}
186
187void
188Producer::handleCoveringKey(const Interest& interest, Data& data,
189 const system_clock::TimePoint& timeslot,
190 KeyRequest& keyRequest,
191 const ProducerEKeyCallback& callback)
192{
193 Name interestName = interest.getName();
194 Name keyName = data.getName();
195
196 system_clock::TimePoint begin = time::fromIsoString(keyName.get(startTs).toUri());
197 system_clock::TimePoint end = time::fromIsoString(keyName.get(endTs).toUri());
198
199 if (timeslot >= end) {
200 Exclude timeRange = interest.getSelectors().getExclude();
201 timeRange.excludeBefore(keyName.get(startTs));
202 keyRequest.repeatAttempts[interestName] = 0;
203 sendKeyInterest(interestName, timeslot, keyRequest, callback, timeRange);
204 return;
205 }
206
207 const Block keyBlock = data.getContent();
208 Buffer encryptionKey(keyBlock.value(), keyBlock.value_size());
209 m_ekeyInfo[interestName].beginTimeslot = begin;
210 m_ekeyInfo[interestName].endTimeslot = end;
211 m_ekeyInfo[interestName].keyBits = encryptionKey;
212
213 encryptContentKey(keyRequest, encryptionKey, keyName, timeslot, callback);
214}
215
216void
217Producer::handleTimeout(const Interest& interest,
218 const system_clock::TimePoint& timeslot,
219 KeyRequest& keyRequest,
220 const ProducerEKeyCallback& callback)
221{
222 Name interestName = interest.getName();
223
224 if (keyRequest.repeatAttempts[interestName] < m_maxRepeatAttempts) {
225 keyRequest.repeatAttempts[interestName]++;
226 sendKeyInterest(interestName, timeslot, keyRequest, callback,
227 interest.getSelectors().getExclude());
228 }
229 else {
230 keyRequest.interestCount--;
231 }
232
233 if (keyRequest.interestCount == 0 && callback) {
234 callback(keyRequest.encryptedKeys);
235 m_keyRequests.erase(toUnixTimestamp(timeslot).count());
236 }
237}
238
239} // namespace gep
Yingdi Yu266badb2016-03-09 18:58:27 -0800240} // namespace ndn