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-2020 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
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(buf, len) >> digestFilter(algo) >> streamSink(os);
151  return os.buf();
152 }
153 
154 void
155 PrivateKey::loadRaw(KeyType type, const uint8_t* buf, size_t size)
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, size);
171 #else
172  EVP_PKEY_new_mac_key(pkeyType, nullptr, buf, static_cast<int>(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 = size * 8;
179 #endif
180 }
181 
182 void
183 PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
184 {
185  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
187 
188  if (d2i_AutoPrivateKey(&m_impl->key, &buf, static_cast<long>(size)) == nullptr)
189  NDN_THROW(Error("Failed to load private key"));
190 }
191 
192 void
193 PrivateKey::loadPkcs1(std::istream& is)
194 {
195  OBufferStream os;
196  streamSource(is) >> streamSink(os);
197  this->loadPkcs1(os.buf()->data(), os.buf()->size());
198 }
199 
200 void
201 PrivateKey::loadPkcs1Base64(const uint8_t* buf, size_t size)
202 {
203  OBufferStream os;
204  bufferSource(buf, size) >> base64Decode() >> streamSink(os);
205  this->loadPkcs1(os.buf()->data(), os.buf()->size());
206 }
207 
208 void
210 {
211  OBufferStream os;
212  streamSource(is) >> base64Decode() >> streamSink(os);
213  this->loadPkcs1(os.buf()->data(), os.buf()->size());
214 }
215 
216 void
217 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
218 {
219  BOOST_ASSERT(std::strlen(pw) == pwLen);
220  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
222 
223  detail::Bio membio(BIO_s_mem());
224  if (!membio.write(buf, size))
225  NDN_THROW(Error("Failed to copy buffer"));
226 
227  if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
228  NDN_THROW(Error("Failed to load private key"));
229 }
230 
231 static inline int
232 passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
233 {
234  BOOST_ASSERT(size >= 0);
235  auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
236  return (*cb)(buf, static_cast<size_t>(size), rwflag);
237 }
238 
239 void
240 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
241 {
242  ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
244 
245  detail::Bio membio(BIO_s_mem());
246  if (!membio.write(buf, size))
247  NDN_THROW(Error("Failed to copy buffer"));
248 
249  if (pwCallback)
250  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
251  else
252  m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
253 
254  if (m_impl->key == nullptr)
255  NDN_THROW(Error("Failed to load private key"));
256 }
257 
258 void
259 PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
260 {
261  OBufferStream os;
262  streamSource(is) >> streamSink(os);
263  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pw, pwLen);
264 }
265 
266 void
267 PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
268 {
269  OBufferStream os;
270  streamSource(is) >> streamSink(os);
271  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pwCallback);
272 }
273 
274 void
275 PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
276 {
277  OBufferStream os;
278  bufferSource(buf, size) >> base64Decode() >> streamSink(os);
279  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pw, pwLen);
280 }
281 
282 void
283 PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
284 {
285  OBufferStream os;
286  bufferSource(buf, size) >> base64Decode() >> streamSink(os);
287  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pwCallback);
288 }
289 
290 void
291 PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
292 {
293  OBufferStream os;
294  streamSource(is) >> base64Decode() >> streamSink(os);
295  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pw, pwLen);
296 }
297 
298 void
299 PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
300 {
301  OBufferStream os;
302  streamSource(is) >> base64Decode() >> streamSink(os);
303  this->loadPkcs8(os.buf()->data(), os.buf()->size(), pwCallback);
304 }
305 
306 void
307 PrivateKey::savePkcs1(std::ostream& os) const
308 {
309  bufferSource(*this->toPkcs1()) >> streamSink(os);
310 }
311 
312 void
313 PrivateKey::savePkcs1Base64(std::ostream& os) const
314 {
315  bufferSource(*this->toPkcs1()) >> base64Encode() >> streamSink(os);
316 }
317 
318 void
319 PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
320 {
321  bufferSource(*this->toPkcs8(pw, pwLen)) >> streamSink(os);
322 }
323 
324 void
325 PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
326 {
327  bufferSource(*this->toPkcs8(pwCallback)) >> streamSink(os);
328 }
329 
330 void
331 PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
332 {
333  bufferSource(*this->toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
334 }
335 
336 void
337 PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
338 {
339  bufferSource(*this->toPkcs8(pwCallback)) >> base64Encode() >> streamSink(os);
340 }
341 
344 {
345  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
346 
347  uint8_t* pkcs8 = nullptr;
348  int len = i2d_PUBKEY(m_impl->key, &pkcs8);
349  if (len < 0)
350  NDN_THROW(Error("Failed to derive public key"));
351 
352  auto result = make_shared<Buffer>(pkcs8, len);
353  OPENSSL_free(pkcs8);
354 
355  return result;
356 }
357 
359 PrivateKey::decrypt(const uint8_t* cipherText, size_t cipherLen) const
360 {
361  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
362 
363  int keyType = detail::getEvpPkeyType(m_impl->key);
364  switch (keyType) {
365  case EVP_PKEY_NONE:
366  NDN_THROW(Error("Failed to determine key type"));
367  case EVP_PKEY_RSA:
368  return rsaDecrypt(cipherText, cipherLen);
369  default:
370  NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
371  }
372 }
373 
374 void*
375 PrivateKey::getEvpPkey() const
376 {
377  return m_impl->key;
378 }
379 
381 PrivateKey::toPkcs1() const
382 {
383  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
385 
386  detail::Bio membio(BIO_s_mem());
387  if (!i2d_PrivateKey_bio(membio, m_impl->key))
388  NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
389 
390  auto buffer = make_shared<Buffer>(BIO_pending(membio));
391  if (!membio.read(buffer->data(), buffer->size()))
392  NDN_THROW(Error("Read error during PKCS #1 conversion"));
393 
394  return buffer;
395 }
396 
398 PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
399 {
400  BOOST_ASSERT(std::strlen(pw) == pwLen);
401  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
403 
404  detail::Bio membio(BIO_s_mem());
405  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
406  nullptr, const_cast<char*>(pw)))
407  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
408 
409  auto buffer = make_shared<Buffer>(BIO_pending(membio));
410  if (!membio.read(buffer->data(), buffer->size()))
411  NDN_THROW(Error("Read error during PKCS #8 conversion"));
412 
413  return buffer;
414 }
415 
417 PrivateKey::toPkcs8(PasswordCallback pwCallback) const
418 {
419  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
421 
422  detail::Bio membio(BIO_s_mem());
423  if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
424  &passwordCallbackWrapper, &pwCallback))
425  NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
426 
427  auto buffer = make_shared<Buffer>(BIO_pending(membio));
428  if (!membio.read(buffer->data(), buffer->size()))
429  NDN_THROW(Error("Read error during PKCS #8 conversion"));
430 
431  return buffer;
432 }
433 
435 PrivateKey::rsaDecrypt(const uint8_t* cipherText, size_t cipherLen) const
436 {
437  detail::EvpPkeyCtx ctx(m_impl->key);
438 
439  if (EVP_PKEY_decrypt_init(ctx) <= 0)
440  NDN_THROW(Error("Failed to initialize decryption context"));
441 
442  if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
443  NDN_THROW(Error("Failed to set padding"));
444 
445  size_t outlen = 0;
446  // Determine buffer length
447  if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText, cipherLen) <= 0)
448  NDN_THROW(Error("Failed to estimate output length"));
449 
450  auto out = make_shared<Buffer>(outlen);
451  if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText, cipherLen) <= 0)
452  NDN_THROW(Error("Failed to decrypt ciphertext"));
453 
454  out->resize(outlen);
455  return out;
456 }
457 
458 unique_ptr<PrivateKey>
459 PrivateKey::generateRsaKey(uint32_t keySize)
460 {
461  detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
462 
463  if (EVP_PKEY_keygen_init(kctx) <= 0)
464  NDN_THROW(PrivateKey::Error("Failed to initialize RSA keygen context"));
465 
466  if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
467  NDN_THROW(PrivateKey::Error("Failed to set RSA key length"));
468 
469  auto privateKey = make_unique<PrivateKey>();
470  if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
471  NDN_THROW(PrivateKey::Error("Failed to generate RSA key"));
472 
473  return privateKey;
474 }
475 
476 unique_ptr<PrivateKey>
477 PrivateKey::generateEcKey(uint32_t keySize)
478 {
479  EC_KEY* eckey = nullptr;
480  switch (keySize) {
481  case 224:
482  eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
483  break;
484  case 256:
485  eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
486  break;
487  case 384:
488  eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
489  break;
490  case 521:
491  eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
492  break;
493  default:
494  NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
495  }
496  if (eckey == nullptr) {
497  NDN_THROW(Error("Failed to set EC curve"));
498  }
499 
500  auto guard = make_scope_exit([eckey] { EC_KEY_free(eckey); });
501 
502 #if OPENSSL_VERSION_NUMBER < 0x1010000fL
503  EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
504 #endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
505 
506  if (EC_KEY_generate_key(eckey) != 1) {
507  NDN_THROW(Error("Failed to generate EC key"));
508  }
509 
510  auto privateKey = make_unique<PrivateKey>();
511  privateKey->m_impl->key = EVP_PKEY_new();
512  if (privateKey->m_impl->key == nullptr)
513  NDN_THROW(Error("Failed to create EVP_PKEY"));
514  if (EVP_PKEY_set1_EC_KEY(privateKey->m_impl->key, eckey) != 1)
515  NDN_THROW(Error("Failed to assign EC key"));
516 
517  return privateKey;
518 }
519 
520 unique_ptr<PrivateKey>
521 PrivateKey::generateHmacKey(uint32_t keySize)
522 {
523  std::vector<uint8_t> rawKey(keySize / 8);
524  random::generateSecureBytes(rawKey.data(), rawKey.size());
525 
526  auto privateKey = make_unique<PrivateKey>();
527  try {
528  privateKey->loadRaw(KeyType::HMAC, rawKey.data(), rawKey.size());
529  }
530  catch (const PrivateKey::Error&) {
531  NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
532  }
533 
534  return privateKey;
535 }
536 
537 unique_ptr<PrivateKey>
538 generatePrivateKey(const KeyParams& keyParams)
539 {
540  switch (keyParams.getKeyType()) {
541  case KeyType::RSA: {
542  const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
543  return PrivateKey::generateRsaKey(rsaParams.getKeySize());
544  }
545  case KeyType::EC: {
546  const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(keyParams);
547  return PrivateKey::generateEcKey(ecParams.getKeySize());
548  }
549  case KeyType::HMAC: {
550  const HmacKeyParams& hmacParams = static_cast<const HmacKeyParams&>(keyParams);
551  return PrivateKey::generateHmacKey(hmacParams.getKeySize());
552  }
553  default:
554  NDN_THROW(std::invalid_argument("Unsupported key type " +
555  boost::lexical_cast<std::string>(keyParams.getKeyType())));
556  }
557 }
558 
559 } // namespace transform
560 } // namespace security
561 } // namespace ndn
Definition: data.cpp:26
Unknown or unsupported key type.
void loadPkcs8(const uint8_t *buf, size_t size, const char *pw, size_t pwLen)
Load the private key in encrypted PKCS#8 format from a buffer buf with passphrase pw...
void loadPkcs1Base64(const uint8_t *buf, size_t size)
Load the private key in base64-encoded PKCS#1 format from a buffer buf.
function< int(char *buf, size_t bufSize, bool shouldConfirm)> PasswordCallback
Callback for application to handle password input.
Definition: private-key.hpp:55
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:101
size_t getKeySize() const
Returns the size of the private key in bits.
void loadPkcs1(const uint8_t *buf, size_t size)
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)
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 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:82
KeyType
The type of a cryptographic key.
void loadPkcs8Base64(const uint8_t *buf, size_t size, const char *pw, size_t pwLen)
Load the private key in base64-encoded encrypted PKCS#8 format from a buffer buf with passphrase pw...
unique_ptr< Sink > streamSink(std::ostream &os)
Definition: stream-sink.cpp:53
ConstBufferPtr decrypt(const uint8_t *cipherText, size_t cipherLen) const
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
static void opensslInitAlgorithms()
Definition: private-key.cpp:55
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
void generateSecureBytes(uint8_t *bytes, size_t size)
Fill bytes of size with cryptographically secure random bytes.
Definition: random.cpp:45
friend unique_ptr< PrivateKey > generatePrivateKey(const KeyParams &)
Generate a private key according to keyParams.
Base class for key parameters.
Definition: key-params.hpp:35
uint32_t getKeySize() const
Definition: key-params.hpp:176
ConstBufferPtr derivePublicKey() const
implements an output stream that constructs ndn::Buffer
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:90
void savePkcs1Base64(std::ostream &os) const
Save the private key in base64-encoded PKCS#1 format into a stream os.
void loadRaw(KeyType type, const uint8_t *buf, size_t size)
Load a raw private key 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:126
unique_ptr< Transform > base64Encode(bool needBreak)