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