blob: 0becbe25089e0e37338609e90dbe971647efe24d [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/*
Davide Pesavento714dba02022-03-17 20:46:28 -04003 * Copyright (c) 2014-2022, 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
Davide Pesaventoc2649492020-12-22 21:43:35 -050022#include <ndn-cxx/security/transform/block-cipher.hpp>
23#include <ndn-cxx/security/transform/buffer-source.hpp>
24#include <ndn-cxx/security/transform/stream-sink.hpp>
25#include <ndn-cxx/util/exception.hpp>
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040026#include <ndn-cxx/util/logger.hpp>
27
Davide Pesaventobde084f2022-04-17 00:21:35 -040028namespace ndn::nac {
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040029
30NDN_LOG_INIT(nac.Decryptor);
31
32const size_t N_RETRIES = 3;
33
34Decryptor::Decryptor(const Key& credentialsKey, Validator& validator, KeyChain& keyChain, Face& face)
35 : m_credentialsKey(credentialsKey)
36 // , m_validator(validator)
37 , m_face(face)
38 , m_keyChain(keyChain)
39 , m_internalKeyChain("pib-memory:", "tpm-memory:")
40{
41}
42
43Decryptor::~Decryptor()
44{
45 for (auto& i : m_cks) {
Davide Pesaventod51d9602019-07-20 23:33:06 -040046 if (i.second.pendingInterest) {
47 i.second.pendingInterest->cancel();
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040048 for (const auto& p : i.second.pendingDecrypts) {
Davide Pesaventod51d9602019-07-20 23:33:06 -040049 p.onFailure(ErrorCode::CkRetrievalFailure,
50 "Cancel pending decrypt as ContentKey is being destroyed");
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040051 }
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040052 }
53 }
54}
55
56void
Davide Pesaventod51d9602019-07-20 23:33:06 -040057Decryptor::decrypt(const Block& encryptedContent,
58 const DecryptSuccessCallback& onSuccess,
59 const ErrorCallback& onFailure)
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040060{
61 EncryptedContent ec(encryptedContent);
62 if (!ec.hasKeyLocator()) {
63 NDN_LOG_INFO("Missing required KeyLocator in the supplied EncryptedContent block");
64 return onFailure(ErrorCode::MissingRequiredKeyLocator,
65 "Missing required KeyLocator in the supplied EncryptedContent block");
66 }
67
68 if (!ec.hasIv()) {
69 NDN_LOG_INFO("Missing required InitialVector in the supplied EncryptedContent block");
70 return onFailure(ErrorCode::MissingRequiredKeyLocator,
71 "Missing required InitialVector in the supplied EncryptedContent block");
72 }
73
Davide Pesaventobde084f2022-04-17 00:21:35 -040074 auto [ck, isNew] = m_cks.emplace(ec.getKeyLocator(), ContentKey{});
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040075
76 if (ck->second.isRetrieved) {
77 doDecrypt(ec, ck->second.bits, onSuccess, onFailure);
78 }
79 else {
80 NDN_LOG_DEBUG("CK " << ec.getKeyLocator() << " not yet available, adding decrypt to the pending queue");
81 ck->second.pendingDecrypts.push_back({ec, onSuccess, onFailure});
82 }
83
84 if (isNew) {
85 fetchCk(ck, onFailure, N_RETRIES);
86 }
87}
88
89void
90Decryptor::fetchCk(ContentKeys::iterator ck, const ErrorCallback& onFailure, size_t nTriesLeft)
91{
92 // full name of CK is
93
94 // <whatever-prefix>/CK/<ck-id> /ENCRYPTED-BY /<kek-prefix>/KEK/<key-id>
95 // \ / \ /
96 // ----------- ------------- ----------- -----------
97 // \/ \/
98 // from the encrypted data unknown (name in retrieved CK is used to determine KDK)
99
100 const Name& ckName = ck->first;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400101 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 Pesaventobde084f2022-04-17 00:21:35 -0400107 ck->second.pendingInterest = std::nullopt;
Davide Pesavento714dba02022-03-17 20:46:28 -0400108 // TODO: verify that the key is legit
Davide Pesaventobde084f2022-04-17 00:21:35 -0400109 auto [kdkPrefix, kdkIdentity, kdkKeyName] =
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400110 extractKdkInfoFromCkName(ckData.getName(), ckInterest.getName(), onFailure);
111 if (kdkPrefix.empty()) {
112 return; // error has been already reported
113 }
114
115 // check if KDK already exists (there is a corresponding
116 auto kdkIdentityIt = m_internalKeyChain.getPib().getIdentities().find(kdkIdentity);
117 if (kdkIdentityIt != m_internalKeyChain.getPib().getIdentities().end()) {
118 auto kdkKeyIt = (*kdkIdentityIt).getKeys().find(kdkKeyName);
119 if (kdkKeyIt != (*kdkIdentityIt).getKeys().end()) {
120 // KDK was already fetched and imported
121 NDN_LOG_DEBUG("KDK " << kdkKeyName << " already exists, directly using it to decrypt CK");
122 return decryptCkAndProcessPendingDecrypts(ck, ckData, kdkKeyName, onFailure);
123 }
124 }
125
126 fetchKdk(ck, kdkPrefix, ckData, onFailure, N_RETRIES);
127 },
128 [=] (const Interest& i, const lp::Nack& nack) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400129 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400130 onFailure(ErrorCode::CkRetrievalFailure,
131 "Retrieval of CK [" + i.getName().toUri() + "] failed. "
132 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
133 },
134 [=] (const Interest& i) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400135 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400136 if (nTriesLeft > 1) {
137 fetchCk(ck, onFailure, nTriesLeft - 1);
138 }
139 else {
140 onFailure(ErrorCode::CkRetrievalTimeout,
141 "Retrieval of CK [" + i.getName().toUri() + "] timed out");
142 }
143 });
144}
145
146void
147Decryptor::fetchKdk(ContentKeys::iterator ck, const Name& kdkPrefix, const Data& ckData,
148 const ErrorCallback& onFailure, size_t nTriesLeft)
149{
150 // <kdk-prefix>/KDK/<kdk-id> /ENCRYPTED-BY /<credential-identity>/KEY/<key-id>
151 // \ / \ /
152 // ----------- ------------- --------------- ---------------
153 // \/ \/
154 // from the CK data from configuration
155
156 Name kdkName = kdkPrefix;
157 kdkName
158 .append(ENCRYPTED_BY)
159 .append(m_credentialsKey.getName());
160
161 NDN_LOG_DEBUG("Fetching KDK " << kdkName);
162
Davide Pesavento714dba02022-03-17 20:46:28 -0400163 ck->second.pendingInterest = m_face.expressInterest(Interest(kdkName).setMustBeFresh(true),
Davide Pesaventod51d9602019-07-20 23:33:06 -0400164 [=] (const Interest&, const Data& kdkData) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400165 ck->second.pendingInterest = std::nullopt;
Davide Pesavento714dba02022-03-17 20:46:28 -0400166 // TODO: verify that the key is legit
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400167
168 bool isOk = decryptAndImportKdk(kdkData, onFailure);
169 if (!isOk)
170 return;
171 decryptCkAndProcessPendingDecrypts(ck, ckData,
172 kdkPrefix.getPrefix(-2).append("KEY").append(kdkPrefix.get(-1)), // a bit hacky
173 onFailure);
174 },
175 [=] (const Interest& i, const lp::Nack& nack) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400176 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400177 onFailure(ErrorCode::KdkRetrievalFailure,
178 "Retrieval of KDK [" + i.getName().toUri() + "] failed. "
179 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
180 },
181 [=] (const Interest& i) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400182 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400183 if (nTriesLeft > 1) {
184 fetchKdk(ck, kdkPrefix, ckData, onFailure, nTriesLeft - 1);
185 }
186 else {
187 onFailure(ErrorCode::KdkRetrievalTimeout,
188 "Retrieval of KDK [" + i.getName().toUri() + "] timed out");
189 }
190 });
191}
192
193bool
194Decryptor::decryptAndImportKdk(const Data& kdkData, const ErrorCallback& onFailure)
195{
196 try {
197 NDN_LOG_DEBUG("Decrypting and importing KDK " << kdkData.getName());
198 EncryptedContent content(kdkData.getContent().blockFromValue());
199
200 SafeBag safeBag(content.getPayload().blockFromValue());
Davide Pesavento714dba02022-03-17 20:46:28 -0400201 auto secret = m_keyChain.getTpm().decrypt(content.getPayloadKey().value_bytes(),
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400202 m_credentialsKey.getName());
203 if (secret == nullptr) {
204 onFailure(ErrorCode::TpmKeyNotFound,
205 "Could not decrypt secret, " + m_credentialsKey.getName().toUri() + " not found in TPM");
206 return false;
207 }
208
209 m_internalKeyChain.importSafeBag(safeBag, reinterpret_cast<const char*>(secret->data()), secret->size());
210 return true;
211 }
212 catch (const std::runtime_error& e) {
213 // can be tlv::Error, pib::Error, tpm::Error, and bunch of other runtime-derived errors
214 onFailure(ErrorCode::KdkDecryptionFailure,
215 "Failed to decrypt KDK [" + kdkData.getName().toUri() + "]: " + e.what());
216 return false;
217 }
218}
219
220void
221Decryptor::decryptCkAndProcessPendingDecrypts(ContentKeys::iterator ck, const Data& ckData, const Name& kdkKeyName,
222 const ErrorCallback& onFailure)
223{
224 NDN_LOG_DEBUG("Decrypting CK data " << ckData.getName());
225
226 EncryptedContent content(ckData.getContent().blockFromValue());
227
Davide Pesavento714dba02022-03-17 20:46:28 -0400228 auto ckBits = m_internalKeyChain.getTpm().decrypt(content.getPayload().value_bytes(), kdkKeyName);
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400229 if (ckBits == nullptr) {
230 onFailure(ErrorCode::TpmKeyNotFound, "Could not decrypt secret, " + kdkKeyName.toUri() + " not found in TPM");
231 return;
232 }
233
234 ck->second.bits = *ckBits;
235 ck->second.isRetrieved = true;
236
237 for (const auto& item : ck->second.pendingDecrypts) {
238 doDecrypt(item.encryptedContent, ck->second.bits, item.onSuccess, item.onFailure);
239 }
240 ck->second.pendingDecrypts.clear();
241}
242
243void
244Decryptor::doDecrypt(const EncryptedContent& content, const Buffer& ckBits,
245 const DecryptSuccessCallback& onSuccess,
246 const ErrorCallback& onFailure)
247{
248 if (!content.hasIv()) {
Davide Pesaventoc2649492020-12-22 21:43:35 -0500249 NDN_THROW(Error("Expecting Initialization Vector in the encrypted content, but it is not present"));
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400250 }
251
252 OBufferStream os;
Davide Pesavento714dba02022-03-17 20:46:28 -0400253 security::transform::bufferSource(content.getPayload().value_bytes())
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400254 >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
255 CipherOperator::DECRYPT,
Davide Pesavento714dba02022-03-17 20:46:28 -0400256 ckBits, content.getIv().value_bytes())
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400257 >> security::transform::streamSink(os);
258
259 onSuccess(os.buf());
260}
261
Davide Pesaventobde084f2022-04-17 00:21:35 -0400262} // namespace ndn::nac