blob: 68ac6eb67ce969633237fed77be51258fdd2868e [file] [log] [blame]
Alexander Afanasyev1a21e102018-06-13 20:33:21 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento899b2782019-04-29 20:00:03 -04002/*
3 * Copyright (c) 2014-2019, Regents of the University of California
Alexander Afanasyev1a21e102018-06-13 20:33:21 -04004 *
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)
Alexander Afanasyevda366d82018-06-29 18:18:02 -040041 , m_onFailure(onFailure)
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040042 , m_keyChain{keyChain}
43 , m_face{face}
Alexander Afanasyevda366d82018-06-29 18:18:02 -040044 , m_scheduler{face.getIoService()}
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040045{
Alexander Afanasyevda366d82018-06-29 18:18:02 -040046 regenerateCk();
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040047
Davide Pesaventod51d9602019-07-20 23:33:06 -040048 auto serveFromIms = [this] (const Name&, const Interest& interest) {
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040049 auto data = m_ims.find(interest);
50 if (data != nullptr) {
51 NDN_LOG_DEBUG("Serving " << data->getName() << " from InMemoryStorage");
52 m_face.put(*data);
53 }
54 else {
55 NDN_LOG_DEBUG("Didn't find CK data for " << interest.getName());
56 // send NACK?
57 }
58 };
59
60 auto handleError = [] (const Name& prefix, const std::string& msg) {
61 NDN_LOG_ERROR("Failed to register prefix " << prefix << ": " << msg);
62 };
63
Davide Pesaventod51d9602019-07-20 23:33:06 -040064 m_ckReg = m_face.setInterestFilter(Name(ckPrefix).append(CK), serveFromIms, handleError);
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040065}
66
67Encryptor::~Encryptor()
68{
Davide Pesaventod51d9602019-07-20 23:33:06 -040069 m_kekPendingInterest.cancel();
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040070}
71
72void
Alexander Afanasyevda366d82018-06-29 18:18:02 -040073Encryptor::retryFetchingKek()
74{
75 if (m_isKekRetrievalInProgress) {
76 return;
77 }
78
79 NDN_LOG_DEBUG("Retrying fetching of KEK");
80 m_isKekRetrievalInProgress = true;
81 fetchKekAndPublishCkData([&] {
82 NDN_LOG_DEBUG("KEK retrieved and published");
83 m_isKekRetrievalInProgress = false;
84 },
85 [=] (const ErrorCode& code, const std::string& msg) {
86 NDN_LOG_ERROR("Failed to retrieved KEK: " + msg);
87 m_isKekRetrievalInProgress = false;
88 m_onFailure(code, msg);
89 },
90 N_RETRIES);
91}
92
93void
94Encryptor::regenerateCk()
Alexander Afanasyev1a21e102018-06-13 20:33:21 -040095{
96 m_ckName = m_ckPrefix;
97 m_ckName
98 .append(CK)
99 .appendVersion(); // version = ID of CK
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400100 NDN_LOG_DEBUG("Generating new CK: " << m_ckName);
Alexander Afanasyevda366d82018-06-29 18:18:02 -0400101 random::generateSecureBytes(m_ckBits.data(), m_ckBits.size());
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400102
103 // one implication: if CK updated before KEK fetched, KDK for the old CK will not be published
104 if (!m_kek) {
Alexander Afanasyevda366d82018-06-29 18:18:02 -0400105 retryFetchingKek();
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400106 }
107 else {
Alexander Afanasyevda366d82018-06-29 18:18:02 -0400108 makeAndPublishCkData(m_onFailure);
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400109 }
110}
111
112EncryptedContent
113Encryptor::encrypt(const uint8_t* data, size_t size)
114{
115 // Generate initial vector
116 auto iv = make_shared<Buffer>(AES_IV_SIZE);
117 random::generateSecureBytes(iv->data(), iv->size());
118
119 OBufferStream os;
120 security::transform::bufferSource(data, size)
121 >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
122 CipherOperator::ENCRYPT,
123 m_ckBits.data(), m_ckBits.size(), iv->data(), iv->size())
124 >> security::transform::streamSink(os);
125
126 EncryptedContent content;
127 content.setIv(iv);
128 content.setPayload(os.buf());
129 content.setKeyLocator(m_ckName);
130
131 return content;
132}
133
134void
135Encryptor::fetchKekAndPublishCkData(const std::function<void()>& onReady,
136 const ErrorCallback& onFailure,
137 size_t nTriesLeft)
138{
Davide Pesaventod51d9602019-07-20 23:33:06 -0400139 // interest for <access-prefix>/KEK to retrieve <access-prefix>/KEK/<key-id> KekData
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400140
141 NDN_LOG_DEBUG("Fetching KEK " << Name(m_accessPrefix).append(KEK));
142
Davide Pesaventod51d9602019-07-20 23:33:06 -0400143 auto kekInterest = Interest(Name(m_accessPrefix).append(KEK))
144 .setCanBePrefix(true)
145 .setMustBeFresh(true);
146 m_kekPendingInterest = m_face.expressInterest(kekInterest,
147 [=] (const Interest&, const Data& kek) {
148 // @todo verify if the key is legit
149 m_kek = kek;
150 if (makeAndPublishCkData(onFailure)) {
151 onReady();
152 }
153 // otherwise, failure has been already declared
154 },
155 [=] (const Interest& i, const lp::Nack& nack) {
156 if (nTriesLeft > 1) {
157 m_scheduler.schedule(RETRY_DELAY_AFTER_NACK, [=] {
158 fetchKekAndPublishCkData(onReady, onFailure, nTriesLeft - 1);
159 });
160 }
161 else {
162 onFailure(ErrorCode::KekRetrievalFailure, "Retrieval of KEK [" + i.getName().toUri() +
163 "] failed. Got NACK with reason " + boost::lexical_cast<std::string>(nack.getReason()));
164 NDN_LOG_DEBUG("Scheduling retry from NACK");
165 m_scheduler.schedule(RETRY_DELAY_KEK_RETRIEVAL, [this] { retryFetchingKek(); });
166 }
167 },
168 [=] (const Interest& i) {
169 if (nTriesLeft > 1) {
170 fetchKekAndPublishCkData(onReady, onFailure, nTriesLeft - 1);
171 }
172 else {
173 onFailure(ErrorCode::KekRetrievalTimeout,
174 "Retrieval of KEK [" + i.getName().toUri() + "] timed out");
175 NDN_LOG_DEBUG("Scheduling retry after all timeouts");
176 m_scheduler.schedule(RETRY_DELAY_KEK_RETRIEVAL, [this] { retryFetchingKek(); });
177 }
178 });
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400179}
180
Alexander Afanasyevc9934282018-07-17 18:41:36 -0400181bool
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400182Encryptor::makeAndPublishCkData(const ErrorCallback& onFailure)
183{
184 try {
185 PublicKey kek;
186 kek.loadPkcs8(m_kek->getContent().value(), m_kek->getContent().value_size());
187
188 EncryptedContent content;
189 content.setPayload(kek.encrypt(m_ckBits.data(), m_ckBits.size()));
190
191 auto ckData = make_shared<Data>(Name(m_ckName).append(ENCRYPTED_BY).append(m_kek->getName()));
192 ckData->setContent(content.wireEncode());
193 // FreshnessPeriod can serve as a soft access control for revoking access
194 ckData->setFreshnessPeriod(DEFAULT_CK_FRESHNESS_PERIOD);
195 m_keyChain.sign(*ckData, m_ckDataSigningInfo);
196 m_ims.insert(*ckData);
197
198 NDN_LOG_DEBUG("Publishing CK data: " << ckData->getName());
Alexander Afanasyevc9934282018-07-17 18:41:36 -0400199 return true;
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400200 }
Davide Pesaventod51d9602019-07-20 23:33:06 -0400201 catch (const std::runtime_error&) {
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400202 onFailure(ErrorCode::EncryptionFailure, "Failed to encrypt generated CK with KEK " + m_kek->getName().toUri());
Alexander Afanasyevc9934282018-07-17 18:41:36 -0400203 return false;
Alexander Afanasyev1a21e102018-06-13 20:33:21 -0400204 }
205}
206
207} // namespace nac
208} // namespace ndn