blob: f112fa6ae9887db0b775de57d45bd027c2f8cac1 [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)) {
87 contentKeyBits = m_db.getContentKey(timeslot);
88 return contentKeyName;
89 }
90
91 RandomNumberGenerator rng;
92 AesKeyParams aesParams(128);
93 contentKeyBits = algo::Aes::generateKey(rng, aesParams).getKeyBits();
94 m_db.addContentKey(timeslot, contentKeyBits);
95
96 uint64_t timeCount = toUnixTimestamp(timeslot).count();
97 m_keyRequests.insert({timeCount, KeyRequest(m_ekeyInfo.size())});
98 KeyRequest& keyRequest = m_keyRequests.at(timeCount);
99
100 Exclude timeRange;
101 timeRange.excludeAfter(name::Component(time::toIsoString(timeslot)));
102 // Send interests for all nodes in tree.
103 std::unordered_map<Name, KeyInfo>::iterator it;
104 for (it = m_ekeyInfo.begin(); it != m_ekeyInfo.end(); ++it) {
105 const KeyInfo& keyInfo = it->second;
106 keyRequest.repeatAttempts.insert({it->first, 0});
107 if (timeslot < keyInfo.beginTimeslot || timeslot >= keyInfo.endTimeslot) {
108 sendKeyInterest(it->first, timeslot, keyRequest, callback, timeRange);
109 }
110 else {
111 Name eKeyName(it->first);
112 eKeyName.append(time::toIsoString(keyInfo.beginTimeslot));
113 eKeyName.append(time::toIsoString(keyInfo.endTimeslot));
114 encryptContentKey(keyRequest, keyInfo.keyBits, eKeyName, timeslot, callback);
115 }
116 }
117
118 return contentKeyName;
119}
120
121void
122Producer::produce(Data& data, const system_clock::TimePoint& timeslot,
123 const uint8_t* content, size_t contentLen)
124{
125 Buffer contentKey;
126
127 Name contentKeyName = createContentKey(timeslot, nullptr);
128 contentKey = m_db.getContentKey(timeslot);
129
130 Name dataName = m_namespace;
131 dataName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
132
133 data.setName(dataName);
134 algo::EncryptParams params(tlv::AlgorithmAesCbc, 16);
135 algo::encryptData(data, content, contentLen, contentKeyName,
136 contentKey.buf(), contentKey.size(), params);
137 m_keychain.sign(data);
138}
139
140void
141Producer::sendKeyInterest(const Name& name, const system_clock::TimePoint& timeslot,
142 KeyRequest& keyRequest,
143 const ProducerEKeyCallback& callback,
144 const Exclude& timeRange)
145{
146 auto onkey = std::bind(&Producer::handleCoveringKey, this, _1, _2,
147 std::cref(timeslot), std::ref(keyRequest), callback);
148 auto timeout = std::bind(&Producer::handleTimeout, this, _1,
149 std::cref(timeslot), std::ref(keyRequest), callback);
150
151 Selectors selector;
152 selector.setExclude(timeRange);
153 selector.setChildSelector(1);
154
155 Interest keyInterest(name);
156 keyInterest.setSelectors(selector);
157
158 m_face.expressInterest(keyInterest, onkey, timeout);
159}
160
161void
162Producer::encryptContentKey(KeyRequest& keyRequest, const Buffer& encryptionKey,
163 const Name& eKeyName,
164 const system_clock::TimePoint& timeslot,
165 const ProducerEKeyCallback& callback)
166{
167 Name keyName = m_namespace;
168 keyName.append(NAME_COMPONENT_C_KEY);
169 keyName.append(time::toIsoString(getRoundedTimeslot(timeslot)));
170
171 Buffer contentKey = m_db.getContentKey(timeslot);
172
173 Data cKeyData;
174 cKeyData.setName(keyName);
175 algo::EncryptParams params(tlv::AlgorithmRsaOaep);
176 algo::encryptData(cKeyData, contentKey.buf(), contentKey.size(), eKeyName,
177 encryptionKey.buf(), encryptionKey.size(), params);
178 m_keychain.sign(cKeyData);
179 keyRequest.encryptedKeys.push_back(cKeyData);
180
181 keyRequest.interestCount--;
182 if (keyRequest.interestCount == 0 && callback) {
183 callback(keyRequest.encryptedKeys);
184 m_keyRequests.erase(toUnixTimestamp(timeslot).count());
185 }
186}
187
188void
189Producer::handleCoveringKey(const Interest& interest, Data& data,
190 const system_clock::TimePoint& timeslot,
191 KeyRequest& keyRequest,
192 const ProducerEKeyCallback& callback)
193{
194 Name interestName = interest.getName();
195 Name keyName = data.getName();
196
197 system_clock::TimePoint begin = time::fromIsoString(keyName.get(startTs).toUri());
198 system_clock::TimePoint end = time::fromIsoString(keyName.get(endTs).toUri());
199
200 if (timeslot >= end) {
201 Exclude timeRange = interest.getSelectors().getExclude();
202 timeRange.excludeBefore(keyName.get(startTs));
203 keyRequest.repeatAttempts[interestName] = 0;
204 sendKeyInterest(interestName, timeslot, keyRequest, callback, timeRange);
205 return;
206 }
207
208 const Block keyBlock = data.getContent();
209 Buffer encryptionKey(keyBlock.value(), keyBlock.value_size());
210 m_ekeyInfo[interestName].beginTimeslot = begin;
211 m_ekeyInfo[interestName].endTimeslot = end;
212 m_ekeyInfo[interestName].keyBits = encryptionKey;
213
214 encryptContentKey(keyRequest, encryptionKey, keyName, timeslot, callback);
215}
216
217void
218Producer::handleTimeout(const Interest& interest,
219 const system_clock::TimePoint& timeslot,
220 KeyRequest& keyRequest,
221 const ProducerEKeyCallback& callback)
222{
223 Name interestName = interest.getName();
224
225 if (keyRequest.repeatAttempts[interestName] < m_maxRepeatAttempts) {
226 keyRequest.repeatAttempts[interestName]++;
227 sendKeyInterest(interestName, timeslot, keyRequest, callback,
228 interest.getSelectors().getExclude());
229 }
230 else {
231 keyRequest.interestCount--;
232 }
233
234 if (keyRequest.interestCount == 0 && callback) {
235 callback(keyRequest.encryptedKeys);
236 m_keyRequests.erase(toUnixTimestamp(timeslot).count());
237 }
238}
239
240} // namespace gep
241} // namespace ndn