blob: 26b9a73812bbed2220d69ea45cbc6c43b6646941 [file] [log] [blame]
Alexander Afanasyev1a21e102018-06-13 20:33:21 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2018, Regents of the University of California
4 *
5 * NAC library is free software: you can redistribute it and/or modify it under the
6 * terms of the GNU Lesser General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later version.
8 *
9 * NAC library is distributed in the hope that it will be useful, but WITHOUT ANY
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
12 *
13 * You should have received copies of the GNU General Public License and GNU Lesser
14 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
15 * <http://www.gnu.org/licenses/>.
16 *
17 * See AUTHORS.md for complete list of NAC library authors and contributors.
18 */
19
20#include "encryptor.hpp"
21
22#include <ndn-cxx/util/logger.hpp>
23#include <boost/lexical_cast.hpp>
24
25namespace ndn {
26namespace nac {
27
28NDN_LOG_INIT(nac.Encryptor);
29
30const size_t N_RETRIES = 3;
31
32Encryptor::Encryptor(const Name& accessPrefix,
33 const Name& ckPrefix, SigningInfo ckDataSigningInfo,
34 const ErrorCallback& onFailure,
35 Validator& validator, KeyChain& keyChain, Face& face)
36 : m_accessPrefix{accessPrefix}
37 , m_ckPrefix{ckPrefix}
38 , m_ckBits{AES_KEY_SIZE}
39 , m_ckDataSigningInfo{std::move(ckDataSigningInfo)}
40 , m_isKekRetrievalInProgress(false)
41 , m_ckRegId{nullptr}
42 , m_keyChain{keyChain}
43 , m_face{face}
44{
45 regenerateCk(onFailure);
46
47 auto serveFromIms = [this] (const Name& prefix, const Interest& interest) {
48 auto data = m_ims.find(interest);
49 if (data != nullptr) {
50 NDN_LOG_DEBUG("Serving " << data->getName() << " from InMemoryStorage");
51 m_face.put(*data);
52 }
53 else {
54 NDN_LOG_DEBUG("Didn't find CK data for " << interest.getName());
55 // send NACK?
56 }
57 };
58
59 auto handleError = [] (const Name& prefix, const std::string& msg) {
60 NDN_LOG_ERROR("Failed to register prefix " << prefix << ": " << msg);
61 };
62
63 m_ckRegId = m_face.setInterestFilter(Name(ckPrefix).append(CK), serveFromIms, handleError);
64}
65
66Encryptor::~Encryptor()
67{
68 m_face.unsetInterestFilter(m_ckRegId);
69 if (m_kekPendingInterest != nullptr) {
70 m_face.removePendingInterest(m_kekPendingInterest);
71 }
72}
73
74void
75Encryptor::regenerateCk(const ErrorCallback& onFailure)
76{
77 m_ckName = m_ckPrefix;
78 m_ckName
79 .append(CK)
80 .appendVersion(); // version = ID of CK
81 random::generateSecureBytes(m_ckBits.data(), m_ckBits.size());
82
83 NDN_LOG_DEBUG("Generating new CK: " << m_ckName);
84
85 // one implication: if CK updated before KEK fetched, KDK for the old CK will not be published
86 if (!m_kek) {
87 m_isKekRetrievalInProgress = true;
88 fetchKekAndPublishCkData([&] {
89 NDN_LOG_DEBUG("KEK retrieved and published");
90 m_isKekRetrievalInProgress = false;
91 },
92 [&] (const ErrorCode&, const std::string& msg) {
93 NDN_LOG_ERROR("Failed to retrieved KEK: " + msg);
94 m_isKekRetrievalInProgress = false;
95 },
96 N_RETRIES);
97 }
98 else {
99 makeAndPublishCkData(onFailure);
100 }
101}
102
103EncryptedContent
104Encryptor::encrypt(const uint8_t* data, size_t size)
105{
106 // Generate initial vector
107 auto iv = make_shared<Buffer>(AES_IV_SIZE);
108 random::generateSecureBytes(iv->data(), iv->size());
109
110 OBufferStream os;
111 security::transform::bufferSource(data, size)
112 >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
113 CipherOperator::ENCRYPT,
114 m_ckBits.data(), m_ckBits.size(), iv->data(), iv->size())
115 >> security::transform::streamSink(os);
116
117 EncryptedContent content;
118 content.setIv(iv);
119 content.setPayload(os.buf());
120 content.setKeyLocator(m_ckName);
121
122 return content;
123}
124
125void
126Encryptor::fetchKekAndPublishCkData(const std::function<void()>& onReady,
127 const ErrorCallback& onFailure,
128 size_t nTriesLeft)
129{
130 // interest for <access-prefix>/KEK to retrieve <access-prefix>/KEK/<key-id> KekData
131
132 NDN_LOG_DEBUG("Fetching KEK " << Name(m_accessPrefix).append(KEK));
133
134 BOOST_ASSERT(m_kekPendingInterest == nullptr);
135 m_kekPendingInterest =
136 m_face.expressInterest(Interest(Name(m_accessPrefix).append(KEK))
137 .setMustBeFresh(true)
138 .setCanBePrefix(true),
139 [=] (const Interest&, const Data& kek) {
140 m_kekPendingInterest = nullptr;
141 // @todo verify if the key is legit
142 m_kek = kek;
143 makeAndPublishCkData(onFailure);
144 // otherwise, failure has been already declared
145 },
146 [=] (const Interest& i, const lp::Nack& nack) {
147 m_kekPendingInterest = nullptr;
148 onFailure(ErrorCode::KekRetrievalFailure,
149 "Retrieval of KEK [" + i.getName().toUri() + "] failed. "
150 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
151 },
152 [=] (const Interest& i) {
153 m_kekPendingInterest = nullptr;
154 if (nTriesLeft > 1) {
155 fetchKekAndPublishCkData(onReady, onFailure, nTriesLeft - 1);
156 }
157 else {
158 onFailure(ErrorCode::KekRetrievalTimeout,
159 "Retrieval of KEK [" + i.getName().toUri() + "] timed out");
160 }
161 });
162}
163
164void
165Encryptor::makeAndPublishCkData(const ErrorCallback& onFailure)
166{
167 try {
168 PublicKey kek;
169 kek.loadPkcs8(m_kek->getContent().value(), m_kek->getContent().value_size());
170
171 EncryptedContent content;
172 content.setPayload(kek.encrypt(m_ckBits.data(), m_ckBits.size()));
173
174 auto ckData = make_shared<Data>(Name(m_ckName).append(ENCRYPTED_BY).append(m_kek->getName()));
175 ckData->setContent(content.wireEncode());
176 // FreshnessPeriod can serve as a soft access control for revoking access
177 ckData->setFreshnessPeriod(DEFAULT_CK_FRESHNESS_PERIOD);
178 m_keyChain.sign(*ckData, m_ckDataSigningInfo);
179 m_ims.insert(*ckData);
180
181 NDN_LOG_DEBUG("Publishing CK data: " << ckData->getName());
182 }
183 catch (const std::runtime_error& error) {
184 onFailure(ErrorCode::EncryptionFailure, "Failed to encrypt generated CK with KEK " + m_kek->getName().toUri());
185 }
186}
187
188} // namespace nac
189} // namespace ndn