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 
30 namespace ndn::nac {
31 
32 NDN_LOG_INIT(nac.Decryptor);
33 
34 constexpr size_t N_RETRIES = 3;
35 
36 Decryptor::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 
58 void
59 Decryptor::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");
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 
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 
91 void
92 Decryptor::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 
148 void
149 Decryptor::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 
195 bool
196 Decryptor::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) {
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
217  "Failed to decrypt KDK [" + kdkData.getName().toUri() + "]: " + e.what());
218  return false;
219  }
220 }
221 
222 void
223 Decryptor::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 
245 void
246 Decryptor::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
@ EncryptedContent
Definition: common.hpp:74
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