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-2023 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 
29 #include "ndn-cxx/security/impl/openssl-helper.hpp"
32 #include "ndn-cxx/util/random.hpp"
33 #if OPENSSL_VERSION_NUMBER < 0x30000000L
34 #include "ndn-cxx/util/scope.hpp"
35 #endif
36 
37 #include <boost/lexical_cast.hpp>
38 #include <cstring>
39 
40 #include <openssl/ec.h>
41 #include <openssl/pem.h>
42 #include <openssl/rsa.h>
43 #include <openssl/x509.h>
44 
45 #define ENSURE_PRIVATE_KEY_LOADED(key) \
46  do { \
47  if ((key) == nullptr) \
48  NDN_THROW(Error("Private key has not been loaded yet")); \
49  } while (false)
50 
51 #define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
52  do { \
53  if ((key) != nullptr) \
54  NDN_THROW(Error("Private key has already been loaded")); \
55  } while (false)
56 
57 namespace ndn::security::transform {
58 
59 class PrivateKey::Impl : noncopyable
60 {
61 public:
62  ~Impl()
63  {
64  EVP_PKEY_free(key);
65  }
66 
67 public:
68  EVP_PKEY* key = nullptr;
69 };
70 
72  : m_impl(make_unique<Impl>())
73 {
74 }
75 
76 PrivateKey::~PrivateKey() = default;
77 
78 KeyType
80 {
81  if (!m_impl->key)
82  return KeyType::NONE;
83 
84  switch (detail::getEvpPkeyType(m_impl->key)) {
85  case EVP_PKEY_RSA:
86  return KeyType::RSA;
87  case EVP_PKEY_EC:
88  return KeyType::EC;
89  case EVP_PKEY_HMAC:
90  return KeyType::HMAC;
91  default:
92  return KeyType::NONE;
93  }
94 }
95 
96 size_t
98 {
99  switch (getKeyType()) {
100  case KeyType::RSA:
101  case KeyType::EC:
102  return static_cast<size_t>(EVP_PKEY_bits(m_impl->key));
103  case KeyType::HMAC: {
104  size_t nBytes = 0;
105  EVP_PKEY_get_raw_private_key(m_impl->key, nullptr, &nBytes);
106  return nBytes * 8;
107  }
108  default:
109  return 0;
110  }
111 }
112 
115 {
116  if (getKeyType() != KeyType::HMAC)
117  NDN_THROW(Error("Digest is not supported for key type " +
118  boost::lexical_cast<std::string>(getKeyType())));
119 
120  size_t len = 0;
121  if (EVP_PKEY_get_raw_private_key(m_impl->key, nullptr, &len) != 1)
122  NDN_THROW(Error("Failed to get raw key length"));
123  Buffer digest(len);
124  if (EVP_PKEY_get_raw_private_key(m_impl->key, digest.data(), &len) != 1)
125  NDN_THROW(Error("Failed to get raw key"));
126 
127  OBufferStream os;
128  bufferSource(digest) >> digestFilter(algo) >> streamSink(os);
129  return os.buf();
130 }
131 
132 void
133 PrivateKey::loadRaw(KeyType type, span<const uint8_t> buf)
134 {
135  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
136 
137  int pkeyType;
138  switch (type) {
139  case KeyType::HMAC:
140  pkeyType = EVP_PKEY_HMAC;
141  break;
142  default:
143  NDN_THROW(std::invalid_argument("Unsupported key type " + boost::lexical_cast<std::string>(type)));
144  }
145 
146  m_impl->key = EVP_PKEY_new_raw_private_key(pkeyType, nullptr, buf.data(), buf.size());
147  if (m_impl->key == nullptr)
148  NDN_THROW(Error("Failed to load private key"));
149 }
150 
151 void
152 PrivateKey::loadPkcs1(span<const uint8_t> buf)
153 {
154  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
155 
156  auto ptr = buf.data();
157  if (d2i_AutoPrivateKey(&m_impl->key, &ptr, static_cast<long>(buf.size())) == nullptr)
158  NDN_THROW(Error("Failed to load private key"));
159 }
160 
161 void
162 PrivateKey::loadPkcs1(std::istream& is)
163 {
164  OBufferStream os;
165  streamSource(is) >> streamSink(os);
166  loadPkcs1(*os.buf());
167 }
168 
169 void
170 PrivateKey::loadPkcs1Base64(span<const uint8_t> buf)
171 {
172  OBufferStream os;
173  bufferSource(buf) >> base64Decode() >> streamSink(os);
174  loadPkcs1(*os.buf());
175 }
176 
177 void
179 {
180  OBufferStream os;
181  streamSource(is) >> base64Decode() >> streamSink(os);
182  loadPkcs1(*os.buf());
183 }
184 
185 void
186 PrivateKey::loadPkcs8(span<const uint8_t> buf, const char* pw, size_t pwLen)
187 {
188  BOOST_ASSERT(std::strlen(pw) == pwLen);
189  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
190 
191  detail::Bio membio(BIO_s_mem());
192  if (!membio.write(buf))
193  NDN_THROW(Error("Failed to copy buffer"));
194 
195  if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
196  NDN_THROW(Error("Failed to load private key"));
197 }
198 
199 static inline int
200 passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
201 {
202  BOOST_ASSERT(size >= 0);
203  auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
204  return (*cb)(buf, static_cast<size_t>(size), rwflag);
205 }
206 
207 void
208 PrivateKey::loadPkcs8(span<const uint8_t> buf, PasswordCallback pwCallback)
209 {
210  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
211 
212  detail::Bio membio(BIO_s_mem());
213  if (!membio.write(buf))
214  NDN_THROW(Error("Failed to copy buffer"));
215 
216  if (pwCallback)
217  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
218  else
219  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
220 
221  if (m_impl->key == nullptr)
222  NDN_THROW(Error("Failed to load private key"));
223 }
224 
225 void
226 PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
227 {
228  OBufferStream os;
229  streamSource(is) >> streamSink(os);
230  loadPkcs8(*os.buf(), pw, pwLen);
231 }
232 
233 void
234 PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
235 {
236  OBufferStream os;
237  streamSource(is) >> streamSink(os);
238  loadPkcs8(*os.buf(), std::move(pwCallback));
239 }
240 
241 void
242 PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, const char* pw, size_t pwLen)
243 {
244  OBufferStream os;
245  bufferSource(buf) >> base64Decode() >> streamSink(os);
246  loadPkcs8(*os.buf(), pw, pwLen);
247 }
248 
249 void
250 PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, PasswordCallback pwCallback)
251 {
252  OBufferStream os;
253  bufferSource(buf) >> base64Decode() >> streamSink(os);
254  loadPkcs8(*os.buf(), std::move(pwCallback));
255 }
256 
257 void
258 PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
259 {
260  OBufferStream os;
261  streamSource(is) >> base64Decode() >> streamSink(os);
262  loadPkcs8(*os.buf(), pw, pwLen);
263 }
264 
265 void
266 PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
267 {
268  OBufferStream os;
269  streamSource(is) >> base64Decode() >> streamSink(os);
270  loadPkcs8(*os.buf(), std::move(pwCallback));
271 }
272 
273 void
274 PrivateKey::savePkcs1(std::ostream& os) const
275 {
276  bufferSource(*toPkcs1()) >> streamSink(os);
277 }
278 
279 void
280 PrivateKey::savePkcs1Base64(std::ostream& os) const
281 {
282  bufferSource(*toPkcs1()) >> base64Encode() >> streamSink(os);
283 }
284 
285 void
286 PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
287 {
288  bufferSource(*toPkcs8(pw, pwLen)) >> streamSink(os);
289 }
290 
291 void
292 PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
293 {
294  bufferSource(*toPkcs8(std::move(pwCallback))) >> streamSink(os);
295 }
296 
297 void
298 PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
299 {
300  bufferSource(*toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
301 }
302 
303 void
304 PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
305 {
306  bufferSource(*toPkcs8(std::move(pwCallback))) >> base64Encode() >> streamSink(os);
307 }
308 
311 {
312  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
313 
314  uint8_t* pkcs8 = nullptr;
315  int len = i2d_PUBKEY(m_impl->key, &pkcs8);
316  if (len < 0)
317  NDN_THROW(Error("Failed to derive public key"));
318 
319  auto result = make_shared<Buffer>(pkcs8, len);
320  OPENSSL_free(pkcs8);
321 
322  return result;
323 }
324 
326 PrivateKey::decrypt(span<const uint8_t> cipherText) const
327 {
328  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
329 
330  int keyType = detail::getEvpPkeyType(m_impl->key);
331  switch (keyType) {
332  case EVP_PKEY_NONE:
333  NDN_THROW(Error("Failed to determine key type"));
334  case EVP_PKEY_RSA:
335  return rsaDecrypt(cipherText);
336  default:
337  NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
338  }
339 }
340 
341 void*
342 PrivateKey::getEvpPkey() const
343 {
344  return m_impl->key;
345 }
346 
348 PrivateKey::toPkcs1() const
349 {
350  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
351 
352  detail::Bio membio(BIO_s_mem());
353  if (!i2d_PrivateKey_bio(membio, m_impl->key))
354  NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
355 
356  auto buffer = make_shared<Buffer>(BIO_pending(membio));
357  if (!membio.read(*buffer))
358  NDN_THROW(Error("Read error during PKCS #1 conversion"));
359 
360  return buffer;
361 }
362 
364 PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
365 {
366  BOOST_ASSERT(std::strlen(pw) == pwLen);
367  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
368 
369  detail::Bio membio(BIO_s_mem());
370  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
371  nullptr, const_cast<char*>(pw)))
372  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
373 
374  auto buffer = make_shared<Buffer>(BIO_pending(membio));
375  if (!membio.read(*buffer))
376  NDN_THROW(Error("Read error during PKCS #8 conversion"));
377 
378  return buffer;
379 }
380 
382 PrivateKey::toPkcs8(PasswordCallback pwCallback) const
383 {
384  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
385 
386  detail::Bio membio(BIO_s_mem());
387  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
388  &passwordCallbackWrapper, &pwCallback))
389  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
390 
391  auto buffer = make_shared<Buffer>(BIO_pending(membio));
392  if (!membio.read(*buffer))
393  NDN_THROW(Error("Read error during PKCS #8 conversion"));
394 
395  return buffer;
396 }
397 
399 PrivateKey::rsaDecrypt(span<const uint8_t> cipherText) const
400 {
401  detail::EvpPkeyCtx ctx(m_impl->key);
402 
403  if (EVP_PKEY_decrypt_init(ctx) <= 0)
404  NDN_THROW(Error("Failed to initialize decryption context"));
405 
406  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
407  NDN_THROW(Error("Failed to set padding"));
408 
409  size_t outlen = 0;
410  // Determine buffer length
411  if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText.data(), cipherText.size()) <= 0)
412  NDN_THROW(Error("Failed to estimate output length"));
413 
414  auto out = make_shared<Buffer>(outlen);
415  if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText.data(), cipherText.size()) <= 0)
416  NDN_THROW(Error("Failed to decrypt ciphertext"));
417 
418  out->resize(outlen);
419  return out;
420 }
421 
422 unique_ptr<PrivateKey>
423 PrivateKey::generateRsaKey(uint32_t keySize)
424 {
425  auto privateKey = make_unique<PrivateKey>();
426  BOOST_ASSERT(privateKey->m_impl->key == nullptr);
427 
428 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
429  privateKey->m_impl->key = EVP_RSA_gen(keySize);
430 
431  if (privateKey->m_impl->key == nullptr)
432  NDN_THROW(Error("Failed to generate RSA key"));
433 #else // OPENSSL_VERSION_NUMBER
434  detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
435 
436  if (EVP_PKEY_keygen_init(kctx) <= 0)
437  NDN_THROW(Error("Failed to initialize RSA keygen context"));
438 
439  if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
440  NDN_THROW(Error("Failed to set RSA key length"));
441 
442  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
443  NDN_THROW(Error("Failed to generate RSA key"));
444 #endif // OPENSSL_VERSION_NUMBER
445 
446  return privateKey;
447 }
448 
449 unique_ptr<PrivateKey>
450 PrivateKey::generateEcKey(uint32_t keySize)
451 {
452  auto privateKey = make_unique<PrivateKey>();
453  BOOST_ASSERT(privateKey->m_impl->key == nullptr);
454 
455 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
456  switch (keySize) {
457  case 224:
458  case 256:
459  case 384:
460  case 521:
461  privateKey->m_impl->key = EVP_EC_gen(("P-" + to_string(keySize)).data());
462  break;
463  default:
464  NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
465  }
466 
467  if (privateKey->m_impl->key == nullptr)
468  NDN_THROW(Error("Failed to generate EC key"));
469 #else // OPENSSL_VERSION_NUMBER
470  EC_KEY* eckey = nullptr;
471  switch (keySize) {
472  case 224:
473  eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
474  break;
475  case 256:
476  eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
477  break;
478  case 384:
479  eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
480  break;
481  case 521:
482  eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
483  break;
484  default:
485  NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
486  }
487  if (eckey == nullptr) {
488  NDN_THROW(Error("Failed to set EC curve"));
489  }
490 
491  auto guard = make_scope_exit([eckey] { EC_KEY_free(eckey); });
492  if (EC_KEY_generate_key(eckey) != 1) {
493  NDN_THROW(Error("Failed to generate EC key"));
494  }
495 
496  privateKey->m_impl->key = EVP_PKEY_new();
497  if (privateKey->m_impl->key == nullptr)
498  NDN_THROW(Error("Failed to create EVP_PKEY"));
499  if (EVP_PKEY_set1_EC_KEY(privateKey->m_impl->key, eckey) != 1)
500  NDN_THROW(Error("Failed to assign EC key"));
501 #endif // OPENSSL_VERSION_NUMBER
502 
503  return privateKey;
504 }
505 
506 unique_ptr<PrivateKey>
507 PrivateKey::generateHmacKey(uint32_t keySize)
508 {
509  std::vector<uint8_t> rawKey(keySize / 8);
511 
512  auto privateKey = make_unique<PrivateKey>();
513  try {
514  privateKey->loadRaw(KeyType::HMAC, rawKey);
515  }
516  catch (const PrivateKey::Error&) {
517  NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
518  }
519 
520  return privateKey;
521 }
522 
523 unique_ptr<PrivateKey>
524 generatePrivateKey(const KeyParams& keyParams)
525 {
526  switch (keyParams.getKeyType()) {
527  case KeyType::RSA: {
528  const auto& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
529  return PrivateKey::generateRsaKey(rsaParams.getKeySize());
530  }
531  case KeyType::EC: {
532  const auto& ecParams = static_cast<const EcKeyParams&>(keyParams);
533  return PrivateKey::generateEcKey(ecParams.getKeySize());
534  }
535  case KeyType::HMAC: {
536  const auto& hmacParams = static_cast<const HmacKeyParams&>(keyParams);
537  return PrivateKey::generateHmacKey(hmacParams.getKeySize());
538  }
539  default:
540  NDN_THROW(std::invalid_argument("Unsupported key type " +
541  boost::lexical_cast<std::string>(keyParams.getKeyType())));
542  }
543 }
544 
545 } // namespace ndn::security::transform
General-purpose automatically managed/resized buffer.
Definition: buffer.hpp:43
Base class for key parameters.
Definition: key-params.hpp:36
KeyType getKeyType() const
Definition: key-params.hpp:48
An output stream that writes to a Buffer.
std::shared_ptr< Buffer > buf()
Return a shared pointer to the underlying buffer.
SimplePublicKeyParams is a template for public keys with only one parameter: size.
Definition: key-params.hpp:150
SimpleSymmetricKeyParams is a template for symmetric keys with only one parameter: size.
Definition: key-params.hpp:257
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.
void savePkcs1(std::ostream &os) const
Save the private key in PKCS#1 format into a stream os.
std::function< int(char *buf, size_t bufSize, bool shouldConfirm)> PasswordCallback
Callback for application to handle password input.
Definition: private-key.hpp:55
ConstBufferPtr getKeyDigest(DigestAlgorithm algo) const
Returns a digest of the private key.
void loadRaw(KeyType type, span< const uint8_t > buf)
Load a raw private key from a buffer buf.
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.
void loadPkcs1Base64(span< const uint8_t > buf)
Load the private key in base64-encoded PKCS#1 format from a buffer buf.
void savePkcs1Base64(std::ostream &os) const
Save the private key in base64-encoded PKCS#1 format into a stream os.
void loadPkcs1(span< const uint8_t > buf)
Load the private key in PKCS#1 format from a buffer buf.
ConstBufferPtr derivePublicKey() const
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.
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.
ConstBufferPtr decrypt(span< const uint8_t > cipherText) const
KeyType getKeyType() const
Returns the type of the private key.
Definition: private-key.cpp:79
PrivateKey()
Creates an empty private key instance.
Definition: private-key.cpp:71
size_t getKeySize() const
Returns the size of the private key in bits.
Definition: private-key.cpp:97
#define NDN_THROW(e)
Definition: exception.hpp:56
std::string to_string(const errinfo_stacktrace &x)
Definition: exception.cpp:30
void generateSecureBytes(span< uint8_t > buf)
Fill buffer with cryptographically secure random bytes.
Definition: random.cpp:47
unique_ptr< Transform > digestFilter(DigestAlgorithm algo)
unique_ptr< Sink > streamSink(std::ostream &os)
Definition: stream-sink.cpp:51
unique_ptr< PrivateKey > generatePrivateKey(const KeyParams &keyParams)
Generate a private key according to keyParams.
unique_ptr< Transform > base64Encode(bool needBreak)
unique_ptr< Transform > base64Decode(bool expectNewlineEvery64Bytes)
KeyType
The type of a cryptographic key.
@ EC
Elliptic Curve key (e.g. for ECDSA), supports sign/verify operations.
@ RSA
RSA key, supports sign/verify and encrypt/decrypt operations.
@ NONE
Unknown or unsupported key type.
@ HMAC
HMAC key, supports sign/verify operations.
std::shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:140
#define ENSURE_PRIVATE_KEY_LOADED(key)
Definition: private-key.cpp:45
#define ENSURE_PRIVATE_KEY_NOT_LOADED(key)
Definition: private-key.cpp:51