NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
private-key.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2022 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
32 #include "ndn-cxx/util/random.hpp"
33 #include "ndn-cxx/util/scope.hpp"
34 
35 #include <boost/lexical_cast.hpp>
36 #include <cstring>
37 
38 #define ENSURE_PRIVATE_KEY_LOADED(key) \
39  do { \
40  if ((key) == nullptr) \
41  NDN_THROW(Error("Private key has not been loaded yet")); \
42  } while (false)
43 
44 #define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
45  do { \
46  if ((key) != nullptr) \
47  NDN_THROW(Error("Private key has already been loaded")); \
48  } while (false)
49 
50 namespace ndn {
51 namespace security {
52 namespace transform {
53 
54 class PrivateKey::Impl : noncopyable
55 {
56 public:
58  {
59  EVP_PKEY_free(key);
60  }
61 
62 public:
63  EVP_PKEY* key = nullptr;
64 };
65 
67  : m_impl(make_unique<Impl>())
68 {
69 }
70 
71 PrivateKey::~PrivateKey() = default;
72 
73 KeyType
75 {
76  if (!m_impl->key)
77  return KeyType::NONE;
78 
79  switch (detail::getEvpPkeyType(m_impl->key)) {
80  case EVP_PKEY_RSA:
81  return KeyType::RSA;
82  case EVP_PKEY_EC:
83  return KeyType::EC;
84  case EVP_PKEY_HMAC:
85  return KeyType::HMAC;
86  default:
87  return KeyType::NONE;
88  }
89 }
90 
91 size_t
93 {
94  switch (getKeyType()) {
95  case KeyType::RSA:
96  case KeyType::EC:
97  return static_cast<size_t>(EVP_PKEY_bits(m_impl->key));
98  case KeyType::HMAC: {
99  size_t nBytes = 0;
100  EVP_PKEY_get_raw_private_key(m_impl->key, nullptr, &nBytes);
101  return nBytes * 8;
102  }
103  default:
104  return 0;
105  }
106 }
107 
110 {
111  if (getKeyType() != KeyType::HMAC)
112  NDN_THROW(Error("Digest is not supported for key type " +
113  boost::lexical_cast<std::string>(getKeyType())));
114 
115  size_t len = 0;
116  const uint8_t* buf = EVP_PKEY_get0_hmac(m_impl->key, &len);
117  if (buf == nullptr)
118  NDN_THROW(Error("Failed to obtain raw key pointer"));
119  if (len * 8 != getKeySize())
120  NDN_THROW(Error("Key length mismatch"));
121 
122  OBufferStream os;
123  bufferSource(make_span(buf, len)) >> digestFilter(algo) >> streamSink(os);
124  return os.buf();
125 }
126 
127 void
128 PrivateKey::loadRaw(KeyType type, span<const uint8_t> buf)
129 {
130  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
131 
132  int pkeyType;
133  switch (type) {
134  case KeyType::HMAC:
135  pkeyType = EVP_PKEY_HMAC;
136  break;
137  default:
138  NDN_THROW(std::invalid_argument("Unsupported key type " + boost::lexical_cast<std::string>(type)));
139  }
140 
141  m_impl->key = EVP_PKEY_new_raw_private_key(pkeyType, nullptr, buf.data(), buf.size());
142  if (m_impl->key == nullptr)
143  NDN_THROW(Error("Failed to load private key"));
144 }
145 
146 void
147 PrivateKey::loadPkcs1(span<const uint8_t> buf)
148 {
149  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
150 
151  auto ptr = buf.data();
152  if (d2i_AutoPrivateKey(&m_impl->key, &ptr, static_cast<long>(buf.size())) == nullptr)
153  NDN_THROW(Error("Failed to load private key"));
154 }
155 
156 void
157 PrivateKey::loadPkcs1(std::istream& is)
158 {
159  OBufferStream os;
160  streamSource(is) >> streamSink(os);
161  loadPkcs1(*os.buf());
162 }
163 
164 void
165 PrivateKey::loadPkcs1Base64(span<const uint8_t> buf)
166 {
167  OBufferStream os;
168  bufferSource(buf) >> base64Decode() >> streamSink(os);
169  loadPkcs1(*os.buf());
170 }
171 
172 void
174 {
175  OBufferStream os;
176  streamSource(is) >> base64Decode() >> streamSink(os);
177  loadPkcs1(*os.buf());
178 }
179 
180 void
181 PrivateKey::loadPkcs8(span<const uint8_t> buf, const char* pw, size_t pwLen)
182 {
183  BOOST_ASSERT(std::strlen(pw) == pwLen);
184  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
185 
186  detail::Bio membio(BIO_s_mem());
187  if (!membio.write(buf))
188  NDN_THROW(Error("Failed to copy buffer"));
189 
190  if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
191  NDN_THROW(Error("Failed to load private key"));
192 }
193 
194 static inline int
195 passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
196 {
197  BOOST_ASSERT(size >= 0);
198  auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
199  return (*cb)(buf, static_cast<size_t>(size), rwflag);
200 }
201 
202 void
203 PrivateKey::loadPkcs8(span<const uint8_t> buf, PasswordCallback pwCallback)
204 {
205  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
206 
207  detail::Bio membio(BIO_s_mem());
208  if (!membio.write(buf))
209  NDN_THROW(Error("Failed to copy buffer"));
210 
211  if (pwCallback)
212  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
213  else
214  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
215 
216  if (m_impl->key == nullptr)
217  NDN_THROW(Error("Failed to load private key"));
218 }
219 
220 void
221 PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
222 {
223  OBufferStream os;
224  streamSource(is) >> streamSink(os);
225  loadPkcs8(*os.buf(), pw, pwLen);
226 }
227 
228 void
229 PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
230 {
231  OBufferStream os;
232  streamSource(is) >> streamSink(os);
233  loadPkcs8(*os.buf(), std::move(pwCallback));
234 }
235 
236 void
237 PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, const char* pw, size_t pwLen)
238 {
239  OBufferStream os;
240  bufferSource(buf) >> base64Decode() >> streamSink(os);
241  loadPkcs8(*os.buf(), pw, pwLen);
242 }
243 
244 void
245 PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, PasswordCallback pwCallback)
246 {
247  OBufferStream os;
248  bufferSource(buf) >> base64Decode() >> streamSink(os);
249  loadPkcs8(*os.buf(), std::move(pwCallback));
250 }
251 
252 void
253 PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
254 {
255  OBufferStream os;
256  streamSource(is) >> base64Decode() >> streamSink(os);
257  loadPkcs8(*os.buf(), pw, pwLen);
258 }
259 
260 void
261 PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
262 {
263  OBufferStream os;
264  streamSource(is) >> base64Decode() >> streamSink(os);
265  loadPkcs8(*os.buf(), std::move(pwCallback));
266 }
267 
268 void
269 PrivateKey::savePkcs1(std::ostream& os) const
270 {
271  bufferSource(*toPkcs1()) >> streamSink(os);
272 }
273 
274 void
275 PrivateKey::savePkcs1Base64(std::ostream& os) const
276 {
277  bufferSource(*toPkcs1()) >> base64Encode() >> streamSink(os);
278 }
279 
280 void
281 PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
282 {
283  bufferSource(*toPkcs8(pw, pwLen)) >> streamSink(os);
284 }
285 
286 void
287 PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
288 {
289  bufferSource(*toPkcs8(std::move(pwCallback))) >> streamSink(os);
290 }
291 
292 void
293 PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
294 {
295  bufferSource(*toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
296 }
297 
298 void
299 PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
300 {
301  bufferSource(*toPkcs8(std::move(pwCallback))) >> base64Encode() >> streamSink(os);
302 }
303 
306 {
307  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
308 
309  uint8_t* pkcs8 = nullptr;
310  int len = i2d_PUBKEY(m_impl->key, &pkcs8);
311  if (len < 0)
312  NDN_THROW(Error("Failed to derive public key"));
313 
314  auto result = make_shared<Buffer>(pkcs8, len);
315  OPENSSL_free(pkcs8);
316 
317  return result;
318 }
319 
321 PrivateKey::decrypt(span<const uint8_t> cipherText) const
322 {
323  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
324 
325  int keyType = detail::getEvpPkeyType(m_impl->key);
326  switch (keyType) {
327  case EVP_PKEY_NONE:
328  NDN_THROW(Error("Failed to determine key type"));
329  case EVP_PKEY_RSA:
330  return rsaDecrypt(cipherText);
331  default:
332  NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
333  }
334 }
335 
336 void*
337 PrivateKey::getEvpPkey() const
338 {
339  return m_impl->key;
340 }
341 
343 PrivateKey::toPkcs1() const
344 {
345  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
346 
347  detail::Bio membio(BIO_s_mem());
348  if (!i2d_PrivateKey_bio(membio, m_impl->key))
349  NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
350 
351  auto buffer = make_shared<Buffer>(BIO_pending(membio));
352  if (!membio.read(*buffer))
353  NDN_THROW(Error("Read error during PKCS #1 conversion"));
354 
355  return buffer;
356 }
357 
359 PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
360 {
361  BOOST_ASSERT(std::strlen(pw) == pwLen);
362  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
363 
364  detail::Bio membio(BIO_s_mem());
365  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
366  nullptr, const_cast<char*>(pw)))
367  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
368 
369  auto buffer = make_shared<Buffer>(BIO_pending(membio));
370  if (!membio.read(*buffer))
371  NDN_THROW(Error("Read error during PKCS #8 conversion"));
372 
373  return buffer;
374 }
375 
377 PrivateKey::toPkcs8(PasswordCallback pwCallback) const
378 {
379  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
380 
381  detail::Bio membio(BIO_s_mem());
382  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
383  &passwordCallbackWrapper, &pwCallback))
384  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
385 
386  auto buffer = make_shared<Buffer>(BIO_pending(membio));
387  if (!membio.read(*buffer))
388  NDN_THROW(Error("Read error during PKCS #8 conversion"));
389 
390  return buffer;
391 }
392 
394 PrivateKey::rsaDecrypt(span<const uint8_t> cipherText) const
395 {
396  detail::EvpPkeyCtx ctx(m_impl->key);
397 
398  if (EVP_PKEY_decrypt_init(ctx) <= 0)
399  NDN_THROW(Error("Failed to initialize decryption context"));
400 
401  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
402  NDN_THROW(Error("Failed to set padding"));
403 
404  size_t outlen = 0;
405  // Determine buffer length
406  if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText.data(), cipherText.size()) <= 0)
407  NDN_THROW(Error("Failed to estimate output length"));
408 
409  auto out = make_shared<Buffer>(outlen);
410  if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText.data(), cipherText.size()) <= 0)
411  NDN_THROW(Error("Failed to decrypt ciphertext"));
412 
413  out->resize(outlen);
414  return out;
415 }
416 
417 unique_ptr<PrivateKey>
418 PrivateKey::generateRsaKey(uint32_t keySize)
419 {
420  detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
421 
422  if (EVP_PKEY_keygen_init(kctx) <= 0)
423  NDN_THROW(PrivateKey::Error("Failed to initialize RSA keygen context"));
424 
425  if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
426  NDN_THROW(PrivateKey::Error("Failed to set RSA key length"));
427 
428  auto privateKey = make_unique<PrivateKey>();
429  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
430  NDN_THROW(PrivateKey::Error("Failed to generate RSA key"));
431 
432  return privateKey;
433 }
434 
435 unique_ptr<PrivateKey>
436 PrivateKey::generateEcKey(uint32_t keySize)
437 {
438  EC_KEY* eckey = nullptr;
439  switch (keySize) {
440  case 224:
441  eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
442  break;
443  case 256:
444  eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
445  break;
446  case 384:
447  eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
448  break;
449  case 521:
450  eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
451  break;
452  default:
453  NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
454  }
455  if (eckey == nullptr) {
456  NDN_THROW(Error("Failed to set EC curve"));
457  }
458 
459  auto guard = make_scope_exit([eckey] { EC_KEY_free(eckey); });
460  if (EC_KEY_generate_key(eckey) != 1) {
461  NDN_THROW(Error("Failed to generate EC key"));
462  }
463 
464  auto privateKey = make_unique<PrivateKey>();
465  privateKey->m_impl->key = EVP_PKEY_new();
466  if (privateKey->m_impl->key == nullptr)
467  NDN_THROW(Error("Failed to create EVP_PKEY"));
468  if (EVP_PKEY_set1_EC_KEY(privateKey->m_impl->key, eckey) != 1)
469  NDN_THROW(Error("Failed to assign EC key"));
470 
471  return privateKey;
472 }
473 
474 unique_ptr<PrivateKey>
475 PrivateKey::generateHmacKey(uint32_t keySize)
476 {
477  std::vector<uint8_t> rawKey(keySize / 8);
479 
480  auto privateKey = make_unique<PrivateKey>();
481  try {
482  privateKey->loadRaw(KeyType::HMAC, rawKey);
483  }
484  catch (const PrivateKey::Error&) {
485  NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
486  }
487 
488  return privateKey;
489 }
490 
491 unique_ptr<PrivateKey>
492 generatePrivateKey(const KeyParams& keyParams)
493 {
494  switch (keyParams.getKeyType()) {
495  case KeyType::RSA: {
496  const auto& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
497  return PrivateKey::generateRsaKey(rsaParams.getKeySize());
498  }
499  case KeyType::EC: {
500  const auto& ecParams = static_cast<const EcKeyParams&>(keyParams);
501  return PrivateKey::generateEcKey(ecParams.getKeySize());
502  }
503  case KeyType::HMAC: {
504  const auto& hmacParams = static_cast<const HmacKeyParams&>(keyParams);
505  return PrivateKey::generateHmacKey(hmacParams.getKeySize());
506  }
507  default:
508  NDN_THROW(std::invalid_argument("Unsupported key type " +
509  boost::lexical_cast<std::string>(keyParams.getKeyType())));
510  }
511 }
512 
513 } // namespace transform
514 } // namespace security
515 } // namespace ndn
NDN_CXX_NODISCARD bool read(span< uint8_t > buf) const noexcept
Copyright (c) 2011-2015 Regents of the University of California.
Unknown or unsupported key type.
SimpleSymmetricKeyParams is a template for symmetric keys with only one parameter: size...
Definition: key-params.hpp:256
#define ENSURE_PRIVATE_KEY_NOT_LOADED(key)
Definition: private-key.cpp:44
std::string to_string(const T &val)
Definition: backports.hpp:86
size_t getKeySize() const
Returns the size of the private key in bits.
Definition: private-key.cpp:92
void loadPkcs1(span< const uint8_t > buf)
Load the private key in PKCS#1 format from a buffer buf.
unique_ptr< Transform > base64Decode(bool expectNewlineEvery64Bytes)
RSA key, supports sign/verify and encrypt/decrypt operations.
static int passwordCallbackWrapper(char *buf, int size, int rwflag, void *u)
int getEvpPkeyType(const EVP_PKEY *key)
void savePkcs8(std::ostream &os, const char *pw, size_t pwLen) const
Save the private key in encrypted PKCS#8 format into a stream os.
#define ENSURE_PRIVATE_KEY_LOADED(key)
Definition: private-key.cpp:38
void generateSecureBytes(span< uint8_t > buf)
Fill buffer with cryptographically secure random bytes.
Definition: random.cpp:45
void savePkcs8Base64(std::ostream &os, const char *pw, size_t pwLen) const
Save the private key in base64-encoded encrypted PKCS#8 format into a stream os.
#define NDN_THROW(e)
Definition: exception.hpp:61
HMAC key, supports sign/verify operations.
PrivateKey()
Creates an empty private key instance.
Definition: private-key.cpp:66
std::function< int(char *buf, size_t bufSize, bool shouldConfirm)> PasswordCallback
Callback for application to handle password input.
Definition: private-key.hpp:55
KeyType
The type of a cryptographic key.
NDN_CXX_NODISCARD bool write(span< const uint8_t > buf) noexcept
void loadRaw(KeyType type, span< const uint8_t > buf)
Load a raw private key from a buffer buf.
scope_exit< EF > make_scope_exit(EF action)
void loadPkcs8(span< const uint8_t > buf, const char *pw, size_t pwLen)
Load the private key in encrypted PKCS#8 format from a buffer buf with passphrase pw...
ConstBufferPtr decrypt(span< const uint8_t > cipherText) const
unique_ptr< Sink > streamSink(std::ostream &os)
Definition: stream-sink.cpp:53
Elliptic Curve key (e.g. for ECDSA), supports sign/verify operations.
unique_ptr< Transform > digestFilter(DigestAlgorithm algo)
ConstBufferPtr getKeyDigest(DigestAlgorithm algo) const
Returns a digest of the private key.
KeyType getKeyType() const
Definition: key-params.hpp:48
void loadPkcs8Base64(span< const uint8_t > buf, const char *pw, size_t pwLen)
Load the private key in base64-encoded encrypted PKCS#8 format from a buffer buf with passphrase pw...
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
friend unique_ptr< PrivateKey > generatePrivateKey(const KeyParams &)
Generate a private key according to keyParams.
Base class for key parameters.
Definition: key-params.hpp:35
ConstBufferPtr derivePublicKey() const
implements an output stream that constructs ndn::Buffer
span_constexpr std::size_t size(span< T, Extent > const &spn)
Definition: span-lite.hpp:1535
SimplePublicKeyParams is a template for public keys with only one parameter: size.
Definition: key-params.hpp:149
KeyType getKeyType() const
Returns the type of the private key.
Definition: private-key.cpp:74
void savePkcs1Base64(std::ostream &os) const
Save the private key in base64-encoded PKCS#1 format into a stream os.
void loadPkcs1Base64(span< const uint8_t > buf)
Load the private key in base64-encoded PKCS#1 format from a buffer buf.
void savePkcs1(std::ostream &os) const
Save the private key in PKCS#1 format into a stream os.
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:139
unique_ptr< Transform > base64Encode(bool needBreak)