Loading...
Searching...
No Matches
decryptor.cpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2022, 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 "decryptor.hpp"
21
22#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>
26#include <ndn-cxx/util/logger.hpp>
27
28#include <boost/lexical_cast.hpp>
29
30namespace ndn::nac {
31
32NDN_LOG_INIT(nac.Decryptor);
33
34constexpr size_t N_RETRIES = 3;
35
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
46{
47 for (auto& i : m_cks) {
48 if (i.second.pendingInterest) {
49 i.second.pendingInterest->cancel();
50 for (const auto& p : i.second.pendingDecrypts) {
52 "Cancel pending decrypt as ContentKey is being destroyed");
53 }
54 }
55 }
56}
57
58void
59Decryptor::decrypt(const Block& encryptedContent,
60 const DecryptSuccessCallback& onSuccess,
61 const ErrorCallback& onFailure)
62{
63 EncryptedContent ec(encryptedContent);
64 if (!ec.hasKeyLocator()) {
65 NDN_LOG_INFO("Missing required KeyLocator in the supplied EncryptedContent block");
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");
73 "Missing required InitialVector in the supplied EncryptedContent block");
74 }
75
76 auto [ck, isNew] = m_cks.emplace(ec.getKeyLocator(), ContentKey{});
77
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;
103 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) {
109 ck->second.pendingInterest = std::nullopt;
110 // TODO: verify that the key is legit
111 auto [kdkPrefix, kdkIdentity, kdkKeyName] =
112 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) {
131 ck->second.pendingInterest = std::nullopt;
133 "Retrieval of CK [" + i.getName().toUri() + "] failed. "
134 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
135 },
136 [=] (const Interest& i) {
137 ck->second.pendingInterest = std::nullopt;
138 if (nTriesLeft > 1) {
139 fetchCk(ck, onFailure, nTriesLeft - 1);
140 }
141 else {
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
165 ck->second.pendingInterest = m_face.expressInterest(Interest(kdkName).setMustBeFresh(true),
166 [=] (const Interest&, const Data& kdkData) {
167 ck->second.pendingInterest = std::nullopt;
168 // TODO: verify that the key is legit
169
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) {
178 ck->second.pendingInterest = std::nullopt;
180 "Retrieval of KDK [" + i.getName().toUri() + "] failed. "
181 "Got NACK (" + boost::lexical_cast<std::string>(nack.getReason()) + ")");
182 },
183 [=] (const Interest& i) {
184 ck->second.pendingInterest = std::nullopt;
185 if (nTriesLeft > 1) {
186 fetchKdk(ck, kdkPrefix, ckData, onFailure, nTriesLeft - 1);
187 }
188 else {
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());
203 auto secret = m_keyChain.getTpm().decrypt(content.getPayloadKey().value_bytes(),
204 m_credentialsKey.getName());
205 if (secret == nullptr) {
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
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
230 auto ckBits = m_internalKeyChain.getTpm().decrypt(content.getPayload().value_bytes(), kdkKeyName);
231 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()) {
251 NDN_THROW(Error("Expecting Initialization Vector in the encrypted content, but it is not present"));
252 }
253
254 OBufferStream os;
255 security::transform::bufferSource(content.getPayload().value_bytes())
256 >> security::transform::blockCipher(BlockCipherAlgorithm::AES_CBC,
257 CipherOperator::DECRYPT,
258 ckBits, content.getIv().value_bytes())
259 >> security::transform::streamSink(os);
260
261 onSuccess(os.buf());
262}
263
264} // namespace ndn::nac
Decryptor(const Key &credentialsKey, Validator &validator, KeyChain &keyChain, Face &face)
Constructor.
Definition decryptor.cpp:36
void decrypt(const Block &encryptedContent, const DecryptSuccessCallback &onSuccess, const ErrorCallback &onFailure)
Asynchronously decrypt encryptedContent.
Definition decryptor.cpp:59
std::function< void(ConstBufferPtr)> DecryptSuccessCallback
Definition decryptor.hpp:40
const Name & getKeyLocator() const
bool hasIv() const noexcept
std::tuple< Name, Name, Name > extractKdkInfoFromCkName(const Name &ckDataName, const Name &ckName, const ErrorCallback &onFailure)
Extract KDK information from name of CK data packet name.
Definition common.cpp:37
std::function< void(const ErrorCode &, const std::string &)> ErrorCallback
Definition common.hpp:117
constexpr size_t N_RETRIES
Definition decryptor.cpp:34
const name::Component ENCRYPTED_BY
Definition common.hpp:82