blob: 53501a9648e73a005c3891aa4965b64b93d54540 [file] [log] [blame]
Yingdi Yu202a2e92015-07-12 16:49:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Luca Keidel941fd8c2017-07-24 15:21:22 +02002/*
Davide Pesavento35c63792022-01-17 02:06:03 -05003 * Copyright (c) 2013-2022 Regents of the University of California.
Yingdi Yu202a2e92015-07-12 16:49:25 -07004 *
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
Davide Pesavento7e780642018-11-24 15:51:34 -050022#include "ndn-cxx/security/transform/private-key.hpp"
23#include "ndn-cxx/security/transform/base64-decode.hpp"
24#include "ndn-cxx/security/transform/base64-encode.hpp"
25#include "ndn-cxx/security/transform/buffer-source.hpp"
laqinfan56a812d2019-06-03 15:33:58 -050026#include "ndn-cxx/security/transform/digest-filter.hpp"
Davide Pesavento7e780642018-11-24 15:51:34 -050027#include "ndn-cxx/security/transform/stream-sink.hpp"
28#include "ndn-cxx/security/transform/stream-source.hpp"
Junxiao Shi24c5a002018-12-12 04:47:15 +000029#include "ndn-cxx/security/impl/openssl-helper.hpp"
Davide Pesavento7e780642018-11-24 15:51:34 -050030#include "ndn-cxx/security/key-params.hpp"
31#include "ndn-cxx/encoding/buffer-stream.hpp"
Laqin Fan0fe72ea2019-05-22 16:42:59 -050032#include "ndn-cxx/util/random.hpp"
Davide Pesavento4999b2e2022-04-23 00:32:25 -040033#if OPENSSL_VERSION_NUMBER < 0x30000000L
Davide Pesavento97ee8112020-08-11 21:38:34 -040034#include "ndn-cxx/util/scope.hpp"
Davide Pesavento4999b2e2022-04-23 00:32:25 -040035#endif
Yingdi Yu202a2e92015-07-12 16:49:25 -070036
Davide Pesaventof45fa212017-09-14 17:23:56 -040037#include <boost/lexical_cast.hpp>
Davide Pesaventoe1789892017-02-26 15:50:52 -050038#include <cstring>
Yingdi Yu202a2e92015-07-12 16:49:25 -070039
Davide Pesavento80d671f2022-06-08 04:04:52 -040040#include <openssl/ec.h>
41#include <openssl/pem.h>
42#include <openssl/rsa.h>
43#include <openssl/x509.h>
44
Yingdi Yu202a2e92015-07-12 16:49:25 -070045#define ENSURE_PRIVATE_KEY_LOADED(key) \
46 do { \
Davide Pesaventof45fa212017-09-14 17:23:56 -040047 if ((key) == nullptr) \
Davide Pesavento923ba442019-02-12 22:00:38 -050048 NDN_THROW(Error("Private key has not been loaded yet")); \
Yingdi Yu202a2e92015-07-12 16:49:25 -070049 } while (false)
50
Davide Pesaventof45fa212017-09-14 17:23:56 -040051#define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
52 do { \
53 if ((key) != nullptr) \
Davide Pesavento923ba442019-02-12 22:00:38 -050054 NDN_THROW(Error("Private key has already been loaded")); \
Davide Pesaventof45fa212017-09-14 17:23:56 -040055 } while (false)
56
Yingdi Yu202a2e92015-07-12 16:49:25 -070057namespace ndn {
58namespace security {
59namespace transform {
60
Laqin Fan0fe72ea2019-05-22 16:42:59 -050061class PrivateKey::Impl : noncopyable
Yingdi Yu202a2e92015-07-12 16:49:25 -070062{
63public:
Yingdi Yu202a2e92015-07-12 16:49:25 -070064 ~Impl()
65 {
66 EVP_PKEY_free(key);
67 }
68
69public:
Laqin Fan0fe72ea2019-05-22 16:42:59 -050070 EVP_PKEY* key = nullptr;
Yingdi Yu202a2e92015-07-12 16:49:25 -070071};
72
73PrivateKey::PrivateKey()
Davide Pesaventof45fa212017-09-14 17:23:56 -040074 : m_impl(make_unique<Impl>())
Yingdi Yu202a2e92015-07-12 16:49:25 -070075{
76}
77
78PrivateKey::~PrivateKey() = default;
79
Davide Pesavento06f1bdf2017-09-16 18:59:15 -040080KeyType
81PrivateKey::getKeyType() const
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;
Laqin Fan0fe72ea2019-05-22 16:42:59 -050091 case EVP_PKEY_HMAC:
92 return KeyType::HMAC;
Davide Pesavento06f1bdf2017-09-16 18:59:15 -040093 default:
94 return KeyType::NONE;
95 }
96}
97
Davide Pesavento1bac1112019-06-13 21:48:46 -040098size_t
99PrivateKey::getKeySize() const
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: {
Davide Pesavento1bac1112019-06-13 21:48:46 -0400106 size_t nBytes = 0;
107 EVP_PKEY_get_raw_private_key(m_impl->key, nullptr, &nBytes);
108 return nBytes * 8;
Davide Pesavento1bac1112019-06-13 21:48:46 -0400109 }
110 default:
111 return 0;
112 }
113}
114
laqinfan56a812d2019-06-03 15:33:58 -0500115ConstBufferPtr
116PrivateKey::getKeyDigest(DigestAlgorithm algo) const
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
laqinfan56a812d2019-06-03 15:33:58 -0500122 size_t len = 0;
Davide Pesavento273ea012022-02-20 17:50:02 -0500123 const uint8_t* buf = EVP_PKEY_get0_hmac(m_impl->key, &len);
laqinfan56a812d2019-06-03 15:33:58 -0500124 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;
Davide Pesavento765abc92021-12-27 00:44:04 -0500130 bufferSource(make_span(buf, len)) >> digestFilter(algo) >> streamSink(os);
laqinfan56a812d2019-06-03 15:33:58 -0500131 return os.buf();
132}
133
Yingdi Yu202a2e92015-07-12 16:49:25 -0700134void
Davide Pesavento765abc92021-12-27 00:44:04 -0500135PrivateKey::loadRaw(KeyType type, span<const uint8_t> buf)
laqinfan48f97242019-06-03 15:33:58 -0500136{
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
Davide Pesavento273ea012022-02-20 17:50:02 -0500148 m_impl->key = EVP_PKEY_new_raw_private_key(pkeyType, nullptr, buf.data(), buf.size());
laqinfan48f97242019-06-03 15:33:58 -0500149 if (m_impl->key == nullptr)
150 NDN_THROW(Error("Failed to load private key"));
laqinfan48f97242019-06-03 15:33:58 -0500151}
152
153void
Davide Pesavento765abc92021-12-27 00:44:04 -0500154PrivateKey::loadPkcs1(span<const uint8_t> buf)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700155{
Davide Pesaventof45fa212017-09-14 17:23:56 -0400156 ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700157
Davide Pesavento765abc92021-12-27 00:44:04 -0500158 auto ptr = buf.data();
159 if (d2i_AutoPrivateKey(&m_impl->key, &ptr, static_cast<long>(buf.size())) == nullptr)
Davide Pesavento923ba442019-02-12 22:00:38 -0500160 NDN_THROW(Error("Failed to load private key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700161}
162
163void
164PrivateKey::loadPkcs1(std::istream& is)
165{
166 OBufferStream os;
167 streamSource(is) >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500168 loadPkcs1(*os.buf());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700169}
170
171void
Davide Pesavento765abc92021-12-27 00:44:04 -0500172PrivateKey::loadPkcs1Base64(span<const uint8_t> buf)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700173{
174 OBufferStream os;
Davide Pesavento765abc92021-12-27 00:44:04 -0500175 bufferSource(buf) >> base64Decode() >> streamSink(os);
176 loadPkcs1(*os.buf());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700177}
178
179void
180PrivateKey::loadPkcs1Base64(std::istream& is)
181{
182 OBufferStream os;
183 streamSource(is) >> base64Decode() >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500184 loadPkcs1(*os.buf());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700185}
186
187void
Davide Pesavento765abc92021-12-27 00:44:04 -0500188PrivateKey::loadPkcs8(span<const uint8_t> buf, const char* pw, size_t pwLen)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700189{
190 BOOST_ASSERT(std::strlen(pw) == pwLen);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400191 ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700192
Davide Pesaventof45fa212017-09-14 17:23:56 -0400193 detail::Bio membio(BIO_s_mem());
Davide Pesavento765abc92021-12-27 00:44:04 -0500194 if (!membio.write(buf))
Davide Pesavento923ba442019-02-12 22:00:38 -0500195 NDN_THROW(Error("Failed to copy buffer"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700196
Davide Pesaventof45fa212017-09-14 17:23:56 -0400197 if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
Davide Pesavento923ba442019-02-12 22:00:38 -0500198 NDN_THROW(Error("Failed to load private key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700199}
200
201static inline int
Davide Pesaventof45fa212017-09-14 17:23:56 -0400202passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700203{
Luca Keidel941fd8c2017-07-24 15:21:22 +0200204 BOOST_ASSERT(size >= 0);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700205 auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200206 return (*cb)(buf, static_cast<size_t>(size), rwflag);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700207}
208
209void
Davide Pesavento765abc92021-12-27 00:44:04 -0500210PrivateKey::loadPkcs8(span<const uint8_t> buf, PasswordCallback pwCallback)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700211{
Davide Pesaventof45fa212017-09-14 17:23:56 -0400212 ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200213
Davide Pesaventof45fa212017-09-14 17:23:56 -0400214 detail::Bio membio(BIO_s_mem());
Davide Pesavento765abc92021-12-27 00:44:04 -0500215 if (!membio.write(buf))
Davide Pesavento923ba442019-02-12 22:00:38 -0500216 NDN_THROW(Error("Failed to copy buffer"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700217
218 if (pwCallback)
Davide Pesaventof45fa212017-09-14 17:23:56 -0400219 m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700220 else
Davide Pesaventof45fa212017-09-14 17:23:56 -0400221 m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700222
Davide Pesaventof45fa212017-09-14 17:23:56 -0400223 if (m_impl->key == nullptr)
Davide Pesavento923ba442019-02-12 22:00:38 -0500224 NDN_THROW(Error("Failed to load private key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700225}
226
227void
228PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
229{
230 OBufferStream os;
231 streamSource(is) >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500232 loadPkcs8(*os.buf(), pw, pwLen);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700233}
234
235void
236PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
237{
238 OBufferStream os;
239 streamSource(is) >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500240 loadPkcs8(*os.buf(), std::move(pwCallback));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700241}
242
243void
Davide Pesavento765abc92021-12-27 00:44:04 -0500244PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, const char* pw, size_t pwLen)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700245{
246 OBufferStream os;
Davide Pesavento765abc92021-12-27 00:44:04 -0500247 bufferSource(buf) >> base64Decode() >> streamSink(os);
248 loadPkcs8(*os.buf(), pw, pwLen);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700249}
250
251void
Davide Pesavento765abc92021-12-27 00:44:04 -0500252PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, PasswordCallback pwCallback)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700253{
254 OBufferStream os;
Davide Pesavento765abc92021-12-27 00:44:04 -0500255 bufferSource(buf) >> base64Decode() >> streamSink(os);
256 loadPkcs8(*os.buf(), std::move(pwCallback));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700257}
258
259void
260PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
261{
262 OBufferStream os;
263 streamSource(is) >> base64Decode() >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500264 loadPkcs8(*os.buf(), pw, pwLen);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700265}
266
267void
268PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
269{
270 OBufferStream os;
271 streamSource(is) >> base64Decode() >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500272 loadPkcs8(*os.buf(), std::move(pwCallback));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700273}
274
275void
276PrivateKey::savePkcs1(std::ostream& os) const
277{
Davide Pesavento765abc92021-12-27 00:44:04 -0500278 bufferSource(*toPkcs1()) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700279}
280
281void
282PrivateKey::savePkcs1Base64(std::ostream& os) const
283{
Davide Pesavento765abc92021-12-27 00:44:04 -0500284 bufferSource(*toPkcs1()) >> base64Encode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700285}
286
287void
288PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
289{
Davide Pesavento765abc92021-12-27 00:44:04 -0500290 bufferSource(*toPkcs8(pw, pwLen)) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700291}
292
293void
294PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
295{
Davide Pesavento765abc92021-12-27 00:44:04 -0500296 bufferSource(*toPkcs8(std::move(pwCallback))) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700297}
298
299void
300PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
301{
Davide Pesavento765abc92021-12-27 00:44:04 -0500302 bufferSource(*toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700303}
304
305void
306PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
307{
Davide Pesavento765abc92021-12-27 00:44:04 -0500308 bufferSource(*toPkcs8(std::move(pwCallback))) >> base64Encode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700309}
310
311ConstBufferPtr
312PrivateKey::derivePublicKey() const
313{
314 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
315
316 uint8_t* pkcs8 = nullptr;
317 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400318 if (len < 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500319 NDN_THROW(Error("Failed to derive public key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700320
321 auto result = make_shared<Buffer>(pkcs8, len);
322 OPENSSL_free(pkcs8);
323
324 return result;
325}
326
327ConstBufferPtr
Davide Pesavento765abc92021-12-27 00:44:04 -0500328PrivateKey::decrypt(span<const uint8_t> cipherText) const
Yingdi Yu202a2e92015-07-12 16:49:25 -0700329{
330 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
331
Davide Pesaventoc21979d2017-09-16 14:52:17 -0400332 int keyType = detail::getEvpPkeyType(m_impl->key);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400333 switch (keyType) {
334 case EVP_PKEY_NONE:
Davide Pesavento923ba442019-02-12 22:00:38 -0500335 NDN_THROW(Error("Failed to determine key type"));
Luca Keidel941fd8c2017-07-24 15:21:22 +0200336 case EVP_PKEY_RSA:
Davide Pesavento765abc92021-12-27 00:44:04 -0500337 return rsaDecrypt(cipherText);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200338 default:
Davide Pesavento923ba442019-02-12 22:00:38 -0500339 NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700340 }
341}
342
343void*
344PrivateKey::getEvpPkey() const
345{
346 return m_impl->key;
347}
348
349ConstBufferPtr
350PrivateKey::toPkcs1() const
351{
352 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
353
Davide Pesaventof45fa212017-09-14 17:23:56 -0400354 detail::Bio membio(BIO_s_mem());
355 if (!i2d_PrivateKey_bio(membio, m_impl->key))
Davide Pesavento923ba442019-02-12 22:00:38 -0500356 NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700357
Davide Pesaventof45fa212017-09-14 17:23:56 -0400358 auto buffer = make_shared<Buffer>(BIO_pending(membio));
Davide Pesavento765abc92021-12-27 00:44:04 -0500359 if (!membio.read(*buffer))
Davide Pesavento140e9cb2019-08-19 00:02:05 -0400360 NDN_THROW(Error("Read error during PKCS #1 conversion"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700361
362 return buffer;
363}
364
365ConstBufferPtr
366PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
367{
Yingdi Yu202a2e92015-07-12 16:49:25 -0700368 BOOST_ASSERT(std::strlen(pw) == pwLen);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200369 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700370
Davide Pesaventof45fa212017-09-14 17:23:56 -0400371 detail::Bio membio(BIO_s_mem());
Davide Pesaventoee77ca02018-10-10 01:45:24 -0400372 if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
Davide Pesaventof45fa212017-09-14 17:23:56 -0400373 nullptr, const_cast<char*>(pw)))
Davide Pesavento923ba442019-02-12 22:00:38 -0500374 NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700375
Davide Pesaventof45fa212017-09-14 17:23:56 -0400376 auto buffer = make_shared<Buffer>(BIO_pending(membio));
Davide Pesavento765abc92021-12-27 00:44:04 -0500377 if (!membio.read(*buffer))
Davide Pesavento140e9cb2019-08-19 00:02:05 -0400378 NDN_THROW(Error("Read error during PKCS #8 conversion"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700379
380 return buffer;
381}
382
383ConstBufferPtr
384PrivateKey::toPkcs8(PasswordCallback pwCallback) const
385{
386 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
387
Davide Pesaventof45fa212017-09-14 17:23:56 -0400388 detail::Bio membio(BIO_s_mem());
Davide Pesaventoee77ca02018-10-10 01:45:24 -0400389 if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
Davide Pesaventof45fa212017-09-14 17:23:56 -0400390 &passwordCallbackWrapper, &pwCallback))
Davide Pesavento923ba442019-02-12 22:00:38 -0500391 NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700392
Davide Pesaventof45fa212017-09-14 17:23:56 -0400393 auto buffer = make_shared<Buffer>(BIO_pending(membio));
Davide Pesavento765abc92021-12-27 00:44:04 -0500394 if (!membio.read(*buffer))
Davide Pesavento140e9cb2019-08-19 00:02:05 -0400395 NDN_THROW(Error("Read error during PKCS #8 conversion"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700396
397 return buffer;
398}
399
400ConstBufferPtr
Davide Pesavento765abc92021-12-27 00:44:04 -0500401PrivateKey::rsaDecrypt(span<const uint8_t> cipherText) const
Yingdi Yu202a2e92015-07-12 16:49:25 -0700402{
403 detail::EvpPkeyCtx ctx(m_impl->key);
404
Davide Pesaventof45fa212017-09-14 17:23:56 -0400405 if (EVP_PKEY_decrypt_init(ctx) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500406 NDN_THROW(Error("Failed to initialize decryption context"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700407
Davide Pesaventof45fa212017-09-14 17:23:56 -0400408 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500409 NDN_THROW(Error("Failed to set padding"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700410
411 size_t outlen = 0;
412 // Determine buffer length
Davide Pesavento765abc92021-12-27 00:44:04 -0500413 if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText.data(), cipherText.size()) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500414 NDN_THROW(Error("Failed to estimate output length"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700415
416 auto out = make_shared<Buffer>(outlen);
Davide Pesavento765abc92021-12-27 00:44:04 -0500417 if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText.data(), cipherText.size()) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500418 NDN_THROW(Error("Failed to decrypt ciphertext"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700419
420 out->resize(outlen);
421 return out;
422}
423
Davide Pesaventof45fa212017-09-14 17:23:56 -0400424unique_ptr<PrivateKey>
425PrivateKey::generateRsaKey(uint32_t keySize)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700426{
Davide Pesavento4999b2e2022-04-23 00:32:25 -0400427 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
Yingdi Yu202a2e92015-07-12 16:49:25 -0700436 detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
437
Davide Pesaventof45fa212017-09-14 17:23:56 -0400438 if (EVP_PKEY_keygen_init(kctx) <= 0)
Davide Pesavento4999b2e2022-04-23 00:32:25 -0400439 NDN_THROW(Error("Failed to initialize RSA keygen context"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700440
Davide Pesaventof45fa212017-09-14 17:23:56 -0400441 if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
Davide Pesavento4999b2e2022-04-23 00:32:25 -0400442 NDN_THROW(Error("Failed to set RSA key length"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700443
Davide Pesaventof45fa212017-09-14 17:23:56 -0400444 if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
Davide Pesavento4999b2e2022-04-23 00:32:25 -0400445 NDN_THROW(Error("Failed to generate RSA key"));
446#endif // OPENSSL_VERSION_NUMBER
Yingdi Yu202a2e92015-07-12 16:49:25 -0700447
448 return privateKey;
449}
450
Davide Pesaventof45fa212017-09-14 17:23:56 -0400451unique_ptr<PrivateKey>
452PrivateKey::generateEcKey(uint32_t keySize)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700453{
Davide Pesavento4999b2e2022-04-23 00:32:25 -0400454 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
Junxiao Shifbb69902020-04-24 08:39:18 +0000472 EC_KEY* eckey = nullptr;
Yingdi Yu202a2e92015-07-12 16:49:25 -0700473 switch (keySize) {
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400474 case 224:
Junxiao Shifbb69902020-04-24 08:39:18 +0000475 eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400476 break;
477 case 256:
Junxiao Shifbb69902020-04-24 08:39:18 +0000478 eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400479 break;
480 case 384:
Junxiao Shifbb69902020-04-24 08:39:18 +0000481 eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400482 break;
483 case 521:
Junxiao Shifbb69902020-04-24 08:39:18 +0000484 eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400485 break;
486 default:
Davide Pesavento923ba442019-02-12 22:00:38 -0500487 NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700488 }
Davide Pesavento97ee8112020-08-11 21:38:34 -0400489 if (eckey == nullptr) {
Junxiao Shifbb69902020-04-24 08:39:18 +0000490 NDN_THROW(Error("Failed to set EC curve"));
Davide Pesavento97ee8112020-08-11 21:38:34 -0400491 }
Yingdi Yu202a2e92015-07-12 16:49:25 -0700492
Davide Pesavento97ee8112020-08-11 21:38:34 -0400493 auto guard = make_scope_exit([eckey] { EC_KEY_free(eckey); });
Junxiao Shifbb69902020-04-24 08:39:18 +0000494 if (EC_KEY_generate_key(eckey) != 1) {
495 NDN_THROW(Error("Failed to generate EC key"));
496 }
Yingdi Yu202a2e92015-07-12 16:49:25 -0700497
Junxiao Shifbb69902020-04-24 08:39:18 +0000498 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"));
Davide Pesavento4999b2e2022-04-23 00:32:25 -0400503#endif // OPENSSL_VERSION_NUMBER
Yingdi Yu202a2e92015-07-12 16:49:25 -0700504
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500505 return privateKey;
506}
507
508unique_ptr<PrivateKey>
509PrivateKey::generateHmacKey(uint32_t keySize)
510{
511 std::vector<uint8_t> rawKey(keySize / 8);
Davide Pesavento765abc92021-12-27 00:44:04 -0500512 random::generateSecureBytes(rawKey);
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500513
514 auto privateKey = make_unique<PrivateKey>();
laqinfan48f97242019-06-03 15:33:58 -0500515 try {
Davide Pesavento765abc92021-12-27 00:44:04 -0500516 privateKey->loadRaw(KeyType::HMAC, rawKey);
laqinfan48f97242019-06-03 15:33:58 -0500517 }
518 catch (const PrivateKey::Error&) {
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500519 NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
laqinfan48f97242019-06-03 15:33:58 -0500520 }
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500521
Yingdi Yu202a2e92015-07-12 16:49:25 -0700522 return privateKey;
523}
524
525unique_ptr<PrivateKey>
526generatePrivateKey(const KeyParams& keyParams)
527{
528 switch (keyParams.getKeyType()) {
529 case KeyType::RSA: {
Davide Pesavento765abc92021-12-27 00:44:04 -0500530 const auto& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400531 return PrivateKey::generateRsaKey(rsaParams.getKeySize());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700532 }
533 case KeyType::EC: {
Davide Pesavento765abc92021-12-27 00:44:04 -0500534 const auto& ecParams = static_cast<const EcKeyParams&>(keyParams);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400535 return PrivateKey::generateEcKey(ecParams.getKeySize());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700536 }
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500537 case KeyType::HMAC: {
Davide Pesavento765abc92021-12-27 00:44:04 -0500538 const auto& hmacParams = static_cast<const HmacKeyParams&>(keyParams);
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500539 return PrivateKey::generateHmacKey(hmacParams.getKeySize());
540 }
Yingdi Yu202a2e92015-07-12 16:49:25 -0700541 default:
laqinfan48f97242019-06-03 15:33:58 -0500542 NDN_THROW(std::invalid_argument("Unsupported key type " +
Davide Pesavento923ba442019-02-12 22:00:38 -0500543 boost::lexical_cast<std::string>(keyParams.getKeyType())));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700544 }
545}
546
547} // namespace transform
548} // namespace security
549} // namespace ndn