blob: 13721342ddd69daea3f44fdf31085062eca2f6ad [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 Pesavento2e5b7b12022-09-19 23:30:44 -040028#include <boost/lexical_cast.hpp>
29
Davide Pesaventobde084f2022-04-17 00:21:35 -040030namespace ndn::nac {
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040031
32NDN_LOG_INIT(nac.Decryptor);
33
Davide Pesavento2e5b7b12022-09-19 23:30:44 -040034constexpr size_t N_RETRIES = 3;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040035
36Decryptor::Decryptor(const Key& credentialsKey, Validator& validator, KeyChain& keyChain, Face& face)
37 : m_credentialsKey(credentialsKey)
38 // , m_validator(validator)
39 , m_face(face)
40 , m_keyChain(keyChain)
41 , m_internalKeyChain("pib-memory:", "tpm-memory:")
42{
43}
44
45Decryptor::~Decryptor()
46{
47 for (auto& i : m_cks) {
Davide Pesaventod51d9602019-07-20 23:33:06 -040048 if (i.second.pendingInterest) {
49 i.second.pendingInterest->cancel();
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040050 for (const auto& p : i.second.pendingDecrypts) {
Davide Pesaventod51d9602019-07-20 23:33:06 -040051 p.onFailure(ErrorCode::CkRetrievalFailure,
52 "Cancel pending decrypt as ContentKey is being destroyed");
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040053 }
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040054 }
55 }
56}
57
58void
Davide Pesaventod51d9602019-07-20 23:33:06 -040059Decryptor::decrypt(const Block& encryptedContent,
60 const DecryptSuccessCallback& onSuccess,
61 const ErrorCallback& onFailure)
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040062{
63 EncryptedContent ec(encryptedContent);
64 if (!ec.hasKeyLocator()) {
65 NDN_LOG_INFO("Missing required KeyLocator in the supplied EncryptedContent block");
66 return onFailure(ErrorCode::MissingRequiredKeyLocator,
67 "Missing required KeyLocator in the supplied EncryptedContent block");
68 }
69
70 if (!ec.hasIv()) {
71 NDN_LOG_INFO("Missing required InitialVector in the supplied EncryptedContent block");
72 return onFailure(ErrorCode::MissingRequiredKeyLocator,
73 "Missing required InitialVector in the supplied EncryptedContent block");
74 }
75
Davide Pesaventobde084f2022-04-17 00:21:35 -040076 auto [ck, isNew] = m_cks.emplace(ec.getKeyLocator(), ContentKey{});
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -040077
78 if (ck->second.isRetrieved) {
79 doDecrypt(ec, ck->second.bits, onSuccess, onFailure);
80 }
81 else {
82 NDN_LOG_DEBUG("CK " << ec.getKeyLocator() << " not yet available, adding decrypt to the pending queue");
83 ck->second.pendingDecrypts.push_back({ec, onSuccess, onFailure});
84 }
85
86 if (isNew) {
87 fetchCk(ck, onFailure, N_RETRIES);
88 }
89}
90
91void
92Decryptor::fetchCk(ContentKeys::iterator ck, const ErrorCallback& onFailure, size_t nTriesLeft)
93{
94 // full name of CK is
95
96 // <whatever-prefix>/CK/<ck-id> /ENCRYPTED-BY /<kek-prefix>/KEK/<key-id>
97 // \ / \ /
98 // ----------- ------------- ----------- -----------
99 // \/ \/
100 // from the encrypted data unknown (name in retrieved CK is used to determine KDK)
101
102 const Name& ckName = ck->first;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400103 NDN_LOG_DEBUG("Fetching CK " << ckName);
104
105 ck->second.pendingInterest = m_face.expressInterest(Interest(ckName)
106 .setMustBeFresh(false) // ?
107 .setCanBePrefix(true),
108 [=] (const Interest& ckInterest, const Data& ckData) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400109 ck->second.pendingInterest = std::nullopt;
Davide Pesavento714dba02022-03-17 20:46:28 -0400110 // TODO: verify that the key is legit
Davide Pesaventobde084f2022-04-17 00:21:35 -0400111 auto [kdkPrefix, kdkIdentity, kdkKeyName] =
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400112 extractKdkInfoFromCkName(ckData.getName(), ckInterest.getName(), onFailure);
113 if (kdkPrefix.empty()) {
114 return; // error has been already reported
115 }
116
117 // check if KDK already exists (there is a corresponding
118 auto kdkIdentityIt = m_internalKeyChain.getPib().getIdentities().find(kdkIdentity);
119 if (kdkIdentityIt != m_internalKeyChain.getPib().getIdentities().end()) {
120 auto kdkKeyIt = (*kdkIdentityIt).getKeys().find(kdkKeyName);
121 if (kdkKeyIt != (*kdkIdentityIt).getKeys().end()) {
122 // KDK was already fetched and imported
123 NDN_LOG_DEBUG("KDK " << kdkKeyName << " already exists, directly using it to decrypt CK");
124 return decryptCkAndProcessPendingDecrypts(ck, ckData, kdkKeyName, onFailure);
125 }
126 }
127
128 fetchKdk(ck, kdkPrefix, ckData, onFailure, N_RETRIES);
129 },
130 [=] (const Interest& i, const lp::Nack& nack) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400131 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400132 onFailure(ErrorCode::CkRetrievalFailure,
133 "Retrieval of CK [" + i.getName().toUri() + "] failed. "
134 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
135 },
136 [=] (const Interest& i) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400137 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400138 if (nTriesLeft > 1) {
139 fetchCk(ck, onFailure, nTriesLeft - 1);
140 }
141 else {
142 onFailure(ErrorCode::CkRetrievalTimeout,
143 "Retrieval of CK [" + i.getName().toUri() + "] timed out");
144 }
145 });
146}
147
148void
149Decryptor::fetchKdk(ContentKeys::iterator ck, const Name& kdkPrefix, const Data& ckData,
150 const ErrorCallback& onFailure, size_t nTriesLeft)
151{
152 // <kdk-prefix>/KDK/<kdk-id> /ENCRYPTED-BY /<credential-identity>/KEY/<key-id>
153 // \ / \ /
154 // ----------- ------------- --------------- ---------------
155 // \/ \/
156 // from the CK data from configuration
157
158 Name kdkName = kdkPrefix;
159 kdkName
160 .append(ENCRYPTED_BY)
161 .append(m_credentialsKey.getName());
162
163 NDN_LOG_DEBUG("Fetching KDK " << kdkName);
164
Davide Pesavento714dba02022-03-17 20:46:28 -0400165 ck->second.pendingInterest = m_face.expressInterest(Interest(kdkName).setMustBeFresh(true),
Davide Pesaventod51d9602019-07-20 23:33:06 -0400166 [=] (const Interest&, const Data& kdkData) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400167 ck->second.pendingInterest = std::nullopt;
Davide Pesavento714dba02022-03-17 20:46:28 -0400168 // TODO: verify that the key is legit
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400169
170 bool isOk = decryptAndImportKdk(kdkData, onFailure);
171 if (!isOk)
172 return;
173 decryptCkAndProcessPendingDecrypts(ck, ckData,
174 kdkPrefix.getPrefix(-2).append("KEY").append(kdkPrefix.get(-1)), // a bit hacky
175 onFailure);
176 },
177 [=] (const Interest& i, const lp::Nack& nack) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400178 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400179 onFailure(ErrorCode::KdkRetrievalFailure,
180 "Retrieval of KDK [" + i.getName().toUri() + "] failed. "
181 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
182 },
183 [=] (const Interest& i) {
Davide Pesaventobde084f2022-04-17 00:21:35 -0400184 ck->second.pendingInterest = std::nullopt;
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400185 if (nTriesLeft > 1) {
186 fetchKdk(ck, kdkPrefix, ckData, onFailure, nTriesLeft - 1);
187 }
188 else {
189 onFailure(ErrorCode::KdkRetrievalTimeout,
190 "Retrieval of KDK [" + i.getName().toUri() + "] timed out");
191 }
192 });
193}
194
195bool
196Decryptor::decryptAndImportKdk(const Data& kdkData, const ErrorCallback& onFailure)
197{
198 try {
199 NDN_LOG_DEBUG("Decrypting and importing KDK " << kdkData.getName());
200 EncryptedContent content(kdkData.getContent().blockFromValue());
201
202 SafeBag safeBag(content.getPayload().blockFromValue());
Davide Pesavento714dba02022-03-17 20:46:28 -0400203 auto secret = m_keyChain.getTpm().decrypt(content.getPayloadKey().value_bytes(),
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400204 m_credentialsKey.getName());
205 if (secret == nullptr) {
206 onFailure(ErrorCode::TpmKeyNotFound,
207 "Could not decrypt secret, " + m_credentialsKey.getName().toUri() + " not found in TPM");
208 return false;
209 }
210
211 m_internalKeyChain.importSafeBag(safeBag, reinterpret_cast<const char*>(secret->data()), secret->size());
212 return true;
213 }
214 catch (const std::runtime_error& e) {
215 // can be tlv::Error, pib::Error, tpm::Error, and bunch of other runtime-derived errors
216 onFailure(ErrorCode::KdkDecryptionFailure,
217 "Failed to decrypt KDK [" + kdkData.getName().toUri() + "]: " + e.what());
218 return false;
219 }
220}
221
222void
223Decryptor::decryptCkAndProcessPendingDecrypts(ContentKeys::iterator ck, const Data& ckData, const Name& kdkKeyName,
224 const ErrorCallback& onFailure)
225{
226 NDN_LOG_DEBUG("Decrypting CK data " << ckData.getName());
227
228 EncryptedContent content(ckData.getContent().blockFromValue());
229
Davide Pesavento714dba02022-03-17 20:46:28 -0400230 auto ckBits = m_internalKeyChain.getTpm().decrypt(content.getPayload().value_bytes(), kdkKeyName);
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400231 if (ckBits == nullptr) {
232 onFailure(ErrorCode::TpmKeyNotFound, "Could not decrypt secret, " + kdkKeyName.toUri() + " not found in TPM");
233 return;
234 }
235
236 ck->second.bits = *ckBits;
237 ck->second.isRetrieved = true;
238
239 for (const auto& item : ck->second.pendingDecrypts) {
240 doDecrypt(item.encryptedContent, ck->second.bits, item.onSuccess, item.onFailure);
241 }
242 ck->second.pendingDecrypts.clear();
243}
244
245void
246Decryptor::doDecrypt(const EncryptedContent& content, const Buffer& ckBits,
247 const DecryptSuccessCallback& onSuccess,
248 const ErrorCallback& onFailure)
249{
250 if (!content.hasIv()) {
Davide Pesaventoc2649492020-12-22 21:43:35 -0500251 NDN_THROW(Error("Expecting Initialization Vector in the encrypted content, but it is not present"));
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400252 }
253
254 OBufferStream os;
Davide Pesavento714dba02022-03-17 20:46:28 -0400255 security::transform::bufferSource(content.getPayload().value_bytes())
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400256 >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
257 CipherOperator::DECRYPT,
Davide Pesavento714dba02022-03-17 20:46:28 -0400258 ckBits, content.getIv().value_bytes())
Alexander Afanasyevff3ee9f2018-06-13 20:33:30 -0400259 >> security::transform::streamSink(os);
260
261 onSuccess(os.buf());
262}
263
Davide Pesaventobde084f2022-04-17 00:21:35 -0400264} // namespace ndn::nac