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