blob: 80ccb9385697611869a4440ca2b0299a27282c90 [file] [log] [blame]
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventod51d9602019-07-20 23:33:06 -04002/*
3 * Copyright (c) 2014-2019, Regents of the University of California
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -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 "decryptor.hpp"
21
22#include <ndn-cxx/util/logger.hpp>
23
24namespace ndn {
25namespace nac {
26
27NDN_LOG_INIT(nac.Decryptor);
28
29const size_t N_RETRIES = 3;
30
31Decryptor::Decryptor(const Key& credentialsKey, Validator& validator, KeyChain& keyChain, Face& face)
32 : m_credentialsKey(credentialsKey)
33 // , m_validator(validator)
34 , m_face(face)
35 , m_keyChain(keyChain)
36 , m_internalKeyChain("pib-memory:", "tpm-memory:")
37{
38}
39
40Decryptor::~Decryptor()
41{
42 for (auto& i : m_cks) {
Davide Pesaventod51d9602019-07-20 23:33:06 -040043 if (i.second.pendingInterest) {
44 i.second.pendingInterest->cancel();
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040045 for (const auto& p : i.second.pendingDecrypts) {
Davide Pesaventod51d9602019-07-20 23:33:06 -040046 p.onFailure(ErrorCode::CkRetrievalFailure,
47 "Cancel pending decrypt as ContentKey is being destroyed");
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040048 }
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040049 }
50 }
51}
52
53void
Davide Pesaventod51d9602019-07-20 23:33:06 -040054Decryptor::decrypt(const Block& encryptedContent,
55 const DecryptSuccessCallback& onSuccess,
56 const ErrorCallback& onFailure)
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040057{
58 EncryptedContent ec(encryptedContent);
59 if (!ec.hasKeyLocator()) {
60 NDN_LOG_INFO("Missing required KeyLocator in the supplied EncryptedContent block");
61 return onFailure(ErrorCode::MissingRequiredKeyLocator,
62 "Missing required KeyLocator in the supplied EncryptedContent block");
63 }
64
65 if (!ec.hasIv()) {
66 NDN_LOG_INFO("Missing required InitialVector in the supplied EncryptedContent block");
67 return onFailure(ErrorCode::MissingRequiredKeyLocator,
68 "Missing required InitialVector in the supplied EncryptedContent block");
69 }
70
71 ContentKeys::iterator ck;
72 bool isNew = false;
73 std::tie(ck, isNew) = m_cks.emplace(ec.getKeyLocator(), ContentKey{});
74
75 if (ck->second.isRetrieved) {
76 doDecrypt(ec, ck->second.bits, onSuccess, onFailure);
77 }
78 else {
79 NDN_LOG_DEBUG("CK " << ec.getKeyLocator() << " not yet available, adding decrypt to the pending queue");
80 ck->second.pendingDecrypts.push_back({ec, onSuccess, onFailure});
81 }
82
83 if (isNew) {
84 fetchCk(ck, onFailure, N_RETRIES);
85 }
86}
87
88void
89Decryptor::fetchCk(ContentKeys::iterator ck, const ErrorCallback& onFailure, size_t nTriesLeft)
90{
91 // full name of CK is
92
93 // <whatever-prefix>/CK/<ck-id> /ENCRYPTED-BY /<kek-prefix>/KEK/<key-id>
94 // \ / \ /
95 // ----------- ------------- ----------- -----------
96 // \/ \/
97 // from the encrypted data unknown (name in retrieved CK is used to determine KDK)
98
99 const Name& ckName = ck->first;
100
101 NDN_LOG_DEBUG("Fetching CK " << ckName);
102
103 ck->second.pendingInterest = m_face.expressInterest(Interest(ckName)
104 .setMustBeFresh(false) // ?
105 .setCanBePrefix(true),
106 [=] (const Interest& ckInterest, const Data& ckData) {
Davide Pesaventod51d9602019-07-20 23:33:06 -0400107 ck->second.pendingInterest = nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400108 // @TODO verify if the key is legit
109 Name kdkPrefix, kdkIdentity, kdkKeyName;
110 std::tie(kdkPrefix, kdkIdentity, kdkKeyName) =
111 extractKdkInfoFromCkName(ckData.getName(), ckInterest.getName(), onFailure);
112 if (kdkPrefix.empty()) {
113 return; // error has been already reported
114 }
115
116 // check if KDK already exists (there is a corresponding
117 auto kdkIdentityIt = m_internalKeyChain.getPib().getIdentities().find(kdkIdentity);
118 if (kdkIdentityIt != m_internalKeyChain.getPib().getIdentities().end()) {
119 auto kdkKeyIt = (*kdkIdentityIt).getKeys().find(kdkKeyName);
120 if (kdkKeyIt != (*kdkIdentityIt).getKeys().end()) {
121 // KDK was already fetched and imported
122 NDN_LOG_DEBUG("KDK " << kdkKeyName << " already exists, directly using it to decrypt CK");
123 return decryptCkAndProcessPendingDecrypts(ck, ckData, kdkKeyName, onFailure);
124 }
125 }
126
127 fetchKdk(ck, kdkPrefix, ckData, onFailure, N_RETRIES);
128 },
129 [=] (const Interest& i, const lp::Nack& nack) {
Davide Pesaventod51d9602019-07-20 23:33:06 -0400130 ck->second.pendingInterest = nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400131 onFailure(ErrorCode::CkRetrievalFailure,
132 "Retrieval of CK [" + i.getName().toUri() + "] failed. "
133 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
134 },
135 [=] (const Interest& i) {
Davide Pesaventod51d9602019-07-20 23:33:06 -0400136 ck->second.pendingInterest = nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400137 if (nTriesLeft > 1) {
138 fetchCk(ck, onFailure, nTriesLeft - 1);
139 }
140 else {
141 onFailure(ErrorCode::CkRetrievalTimeout,
142 "Retrieval of CK [" + i.getName().toUri() + "] timed out");
143 }
144 });
145}
146
147void
148Decryptor::fetchKdk(ContentKeys::iterator ck, const Name& kdkPrefix, const Data& ckData,
149 const ErrorCallback& onFailure, size_t nTriesLeft)
150{
151 // <kdk-prefix>/KDK/<kdk-id> /ENCRYPTED-BY /<credential-identity>/KEY/<key-id>
152 // \ / \ /
153 // ----------- ------------- --------------- ---------------
154 // \/ \/
155 // from the CK data from configuration
156
157 Name kdkName = kdkPrefix;
158 kdkName
159 .append(ENCRYPTED_BY)
160 .append(m_credentialsKey.getName());
161
162 NDN_LOG_DEBUG("Fetching KDK " << kdkName);
163
164 ck->second.pendingInterest = m_face.expressInterest(Interest(kdkName)
165 .setMustBeFresh(true)
166 .setCanBePrefix(false),
Davide Pesaventod51d9602019-07-20 23:33:06 -0400167 [=] (const Interest&, const Data& kdkData) {
168 ck->second.pendingInterest = nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400169 // @TODO verify if the key is legit
170
171 bool isOk = decryptAndImportKdk(kdkData, onFailure);
172 if (!isOk)
173 return;
174 decryptCkAndProcessPendingDecrypts(ck, ckData,
175 kdkPrefix.getPrefix(-2).append("KEY").append(kdkPrefix.get(-1)), // a bit hacky
176 onFailure);
177 },
178 [=] (const Interest& i, const lp::Nack& nack) {
Davide Pesaventod51d9602019-07-20 23:33:06 -0400179 ck->second.pendingInterest = nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400180 onFailure(ErrorCode::KdkRetrievalFailure,
181 "Retrieval of KDK [" + i.getName().toUri() + "] failed. "
182 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
183 },
184 [=] (const Interest& i) {
Davide Pesaventod51d9602019-07-20 23:33:06 -0400185 ck->second.pendingInterest = nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400186 if (nTriesLeft > 1) {
187 fetchKdk(ck, kdkPrefix, ckData, onFailure, nTriesLeft - 1);
188 }
189 else {
190 onFailure(ErrorCode::KdkRetrievalTimeout,
191 "Retrieval of KDK [" + i.getName().toUri() + "] timed out");
192 }
193 });
194}
195
196bool
197Decryptor::decryptAndImportKdk(const Data& kdkData, const ErrorCallback& onFailure)
198{
199 try {
200 NDN_LOG_DEBUG("Decrypting and importing KDK " << kdkData.getName());
201 EncryptedContent content(kdkData.getContent().blockFromValue());
202
203 SafeBag safeBag(content.getPayload().blockFromValue());
204 auto secret = m_keyChain.getTpm().decrypt(content.getPayloadKey().value(),
205 content.getPayloadKey().value_size(),
206 m_credentialsKey.getName());
207 if (secret == nullptr) {
208 onFailure(ErrorCode::TpmKeyNotFound,
209 "Could not decrypt secret, " + m_credentialsKey.getName().toUri() + " not found in TPM");
210 return false;
211 }
212
213 m_internalKeyChain.importSafeBag(safeBag, reinterpret_cast<const char*>(secret->data()), secret->size());
214 return true;
215 }
216 catch (const std::runtime_error& e) {
217 // can be tlv::Error, pib::Error, tpm::Error, and bunch of other runtime-derived errors
218 onFailure(ErrorCode::KdkDecryptionFailure,
219 "Failed to decrypt KDK [" + kdkData.getName().toUri() + "]: " + e.what());
220 return false;
221 }
222}
223
224void
225Decryptor::decryptCkAndProcessPendingDecrypts(ContentKeys::iterator ck, const Data& ckData, const Name& kdkKeyName,
226 const ErrorCallback& onFailure)
227{
228 NDN_LOG_DEBUG("Decrypting CK data " << ckData.getName());
229
230 EncryptedContent content(ckData.getContent().blockFromValue());
231
232 auto ckBits = m_internalKeyChain.getTpm().decrypt(content.getPayload().value(), content.getPayload().value_size(),
233 kdkKeyName);
234 if (ckBits == nullptr) {
235 onFailure(ErrorCode::TpmKeyNotFound, "Could not decrypt secret, " + kdkKeyName.toUri() + " not found in TPM");
236 return;
237 }
238
239 ck->second.bits = *ckBits;
240 ck->second.isRetrieved = true;
241
242 for (const auto& item : ck->second.pendingDecrypts) {
243 doDecrypt(item.encryptedContent, ck->second.bits, item.onSuccess, item.onFailure);
244 }
245 ck->second.pendingDecrypts.clear();
246}
247
248void
249Decryptor::doDecrypt(const EncryptedContent& content, const Buffer& ckBits,
250 const DecryptSuccessCallback& onSuccess,
251 const ErrorCallback& onFailure)
252{
253 if (!content.hasIv()) {
254 BOOST_THROW_EXCEPTION(Error("Expecting Initial Vector in the encrypted content, but it is not present"));
255 }
256
257 OBufferStream os;
258 security::transform::bufferSource(content.getPayload().value(), content.getPayload().value_size())
259 >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
260 CipherOperator::DECRYPT,
261 ckBits.data(), ckBits.size(),
262 content.getIv().value(), content.getIv().value_size())
263 >> security::transform::streamSink(os);
264
265 onSuccess(os.buf());
266}
267
268} // namespace nac
269} // namespace ndn