blob: d0300a1f2f8111cbe5ae8f4f96f9e685bcdc35ba [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 Pesavento765abc92021-12-27 00:44:04 -05003 * Copyright (c) 2013-2021 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 Pesavento97ee8112020-08-11 21:38:34 -040033#include "ndn-cxx/util/scope.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070034
Davide Pesaventof45fa212017-09-14 17:23:56 -040035#include <boost/lexical_cast.hpp>
Davide Pesaventoe1789892017-02-26 15:50:52 -050036#include <cstring>
Yingdi Yu202a2e92015-07-12 16:49:25 -070037
38#define ENSURE_PRIVATE_KEY_LOADED(key) \
39 do { \
Davide Pesaventof45fa212017-09-14 17:23:56 -040040 if ((key) == nullptr) \
Davide Pesavento923ba442019-02-12 22:00:38 -050041 NDN_THROW(Error("Private key has not been loaded yet")); \
Yingdi Yu202a2e92015-07-12 16:49:25 -070042 } while (false)
43
Davide Pesaventof45fa212017-09-14 17:23:56 -040044#define ENSURE_PRIVATE_KEY_NOT_LOADED(key) \
45 do { \
46 if ((key) != nullptr) \
Davide Pesavento923ba442019-02-12 22:00:38 -050047 NDN_THROW(Error("Private key has already been loaded")); \
Davide Pesaventof45fa212017-09-14 17:23:56 -040048 } while (false)
49
Yingdi Yu202a2e92015-07-12 16:49:25 -070050namespace ndn {
51namespace security {
52namespace transform {
53
Luca Keidel941fd8c2017-07-24 15:21:22 +020054static void
55opensslInitAlgorithms()
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
Laqin Fan0fe72ea2019-05-22 16:42:59 -050066class PrivateKey::Impl : noncopyable
Yingdi Yu202a2e92015-07-12 16:49:25 -070067{
68public:
Yingdi Yu202a2e92015-07-12 16:49:25 -070069 ~Impl()
70 {
71 EVP_PKEY_free(key);
72 }
73
74public:
Laqin Fan0fe72ea2019-05-22 16:42:59 -050075 EVP_PKEY* key = nullptr;
Davide Pesavento1bac1112019-06-13 21:48:46 -040076
77#if OPENSSL_VERSION_NUMBER < 0x1010100fL
78 size_t keySize = 0; // in bits, used only for HMAC
79#endif
Yingdi Yu202a2e92015-07-12 16:49:25 -070080};
81
82PrivateKey::PrivateKey()
Davide Pesaventof45fa212017-09-14 17:23:56 -040083 : m_impl(make_unique<Impl>())
Yingdi Yu202a2e92015-07-12 16:49:25 -070084{
85}
86
87PrivateKey::~PrivateKey() = default;
88
Davide Pesavento06f1bdf2017-09-16 18:59:15 -040089KeyType
90PrivateKey::getKeyType() const
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;
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500100 case EVP_PKEY_HMAC:
101 return KeyType::HMAC;
Davide Pesavento06f1bdf2017-09-16 18:59:15 -0400102 default:
103 return KeyType::NONE;
104 }
105}
106
Davide Pesavento1bac1112019-06-13 21:48:46 -0400107size_t
108PrivateKey::getKeySize() const
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
laqinfan56a812d2019-06-03 15:33:58 -0500128ConstBufferPtr
129PrivateKey::getKeyDigest(DigestAlgorithm algo) const
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;
Davide Pesavento765abc92021-12-27 00:44:04 -0500150 bufferSource(make_span(buf, len)) >> digestFilter(algo) >> streamSink(os);
laqinfan56a812d2019-06-03 15:33:58 -0500151 return os.buf();
152}
153
Yingdi Yu202a2e92015-07-12 16:49:25 -0700154void
Davide Pesavento765abc92021-12-27 00:44:04 -0500155PrivateKey::loadRaw(KeyType type, span<const uint8_t> buf)
laqinfan48f97242019-06-03 15:33:58 -0500156{
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
Davide Pesavento765abc92021-12-27 00:44:04 -0500170 EVP_PKEY_new_raw_private_key(pkeyType, nullptr, buf.data(), buf.size());
laqinfan48f97242019-06-03 15:33:58 -0500171#else
Davide Pesavento765abc92021-12-27 00:44:04 -0500172 EVP_PKEY_new_mac_key(pkeyType, nullptr, buf.data(), static_cast<int>(buf.size()));
laqinfan48f97242019-06-03 15:33:58 -0500173#endif
174 if (m_impl->key == nullptr)
175 NDN_THROW(Error("Failed to load private key"));
176
Davide Pesavento1bac1112019-06-13 21:48:46 -0400177#if OPENSSL_VERSION_NUMBER < 0x1010100fL
178 m_impl->keySize = size * 8;
179#endif
laqinfan48f97242019-06-03 15:33:58 -0500180}
181
182void
Davide Pesavento765abc92021-12-27 00:44:04 -0500183PrivateKey::loadPkcs1(span<const uint8_t> buf)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700184{
Davide Pesaventof45fa212017-09-14 17:23:56 -0400185 ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
186 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700187
Davide Pesavento765abc92021-12-27 00:44:04 -0500188 auto ptr = buf.data();
189 if (d2i_AutoPrivateKey(&m_impl->key, &ptr, static_cast<long>(buf.size())) == nullptr)
Davide Pesavento923ba442019-02-12 22:00:38 -0500190 NDN_THROW(Error("Failed to load private key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700191}
192
193void
194PrivateKey::loadPkcs1(std::istream& is)
195{
196 OBufferStream os;
197 streamSource(is) >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500198 loadPkcs1(*os.buf());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700199}
200
201void
Davide Pesavento765abc92021-12-27 00:44:04 -0500202PrivateKey::loadPkcs1Base64(span<const uint8_t> buf)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700203{
204 OBufferStream os;
Davide Pesavento765abc92021-12-27 00:44:04 -0500205 bufferSource(buf) >> base64Decode() >> streamSink(os);
206 loadPkcs1(*os.buf());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700207}
208
209void
210PrivateKey::loadPkcs1Base64(std::istream& is)
211{
212 OBufferStream os;
213 streamSource(is) >> base64Decode() >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500214 loadPkcs1(*os.buf());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700215}
216
217void
Davide Pesavento765abc92021-12-27 00:44:04 -0500218PrivateKey::loadPkcs8(span<const uint8_t> buf, const char* pw, size_t pwLen)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700219{
220 BOOST_ASSERT(std::strlen(pw) == pwLen);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400221 ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200222 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700223
Davide Pesaventof45fa212017-09-14 17:23:56 -0400224 detail::Bio membio(BIO_s_mem());
Davide Pesavento765abc92021-12-27 00:44:04 -0500225 if (!membio.write(buf))
Davide Pesavento923ba442019-02-12 22:00:38 -0500226 NDN_THROW(Error("Failed to copy buffer"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700227
Davide Pesaventof45fa212017-09-14 17:23:56 -0400228 if (d2i_PKCS8PrivateKey_bio(membio, &m_impl->key, nullptr, const_cast<char*>(pw)) == nullptr)
Davide Pesavento923ba442019-02-12 22:00:38 -0500229 NDN_THROW(Error("Failed to load private key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700230}
231
232static inline int
Davide Pesaventof45fa212017-09-14 17:23:56 -0400233passwordCallbackWrapper(char* buf, int size, int rwflag, void* u)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700234{
Luca Keidel941fd8c2017-07-24 15:21:22 +0200235 BOOST_ASSERT(size >= 0);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700236 auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200237 return (*cb)(buf, static_cast<size_t>(size), rwflag);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700238}
239
240void
Davide Pesavento765abc92021-12-27 00:44:04 -0500241PrivateKey::loadPkcs8(span<const uint8_t> buf, PasswordCallback pwCallback)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700242{
Davide Pesaventof45fa212017-09-14 17:23:56 -0400243 ENSURE_PRIVATE_KEY_NOT_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200244 opensslInitAlgorithms();
245
Davide Pesaventof45fa212017-09-14 17:23:56 -0400246 detail::Bio membio(BIO_s_mem());
Davide Pesavento765abc92021-12-27 00:44:04 -0500247 if (!membio.write(buf))
Davide Pesavento923ba442019-02-12 22:00:38 -0500248 NDN_THROW(Error("Failed to copy buffer"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700249
250 if (pwCallback)
Davide Pesaventof45fa212017-09-14 17:23:56 -0400251 m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, &passwordCallbackWrapper, &pwCallback);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700252 else
Davide Pesaventof45fa212017-09-14 17:23:56 -0400253 m_impl->key = d2i_PKCS8PrivateKey_bio(membio, nullptr, nullptr, nullptr);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700254
Davide Pesaventof45fa212017-09-14 17:23:56 -0400255 if (m_impl->key == nullptr)
Davide Pesavento923ba442019-02-12 22:00:38 -0500256 NDN_THROW(Error("Failed to load private key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700257}
258
259void
260PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
261{
262 OBufferStream os;
263 streamSource(is) >> 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::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
269{
270 OBufferStream os;
271 streamSource(is) >> 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
Davide Pesavento765abc92021-12-27 00:44:04 -0500276PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, const char* pw, size_t pwLen)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700277{
278 OBufferStream os;
Davide Pesavento765abc92021-12-27 00:44:04 -0500279 bufferSource(buf) >> base64Decode() >> streamSink(os);
280 loadPkcs8(*os.buf(), pw, pwLen);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700281}
282
283void
Davide Pesavento765abc92021-12-27 00:44:04 -0500284PrivateKey::loadPkcs8Base64(span<const uint8_t> buf, PasswordCallback pwCallback)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700285{
286 OBufferStream os;
Davide Pesavento765abc92021-12-27 00:44:04 -0500287 bufferSource(buf) >> base64Decode() >> streamSink(os);
288 loadPkcs8(*os.buf(), std::move(pwCallback));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700289}
290
291void
292PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
293{
294 OBufferStream os;
295 streamSource(is) >> base64Decode() >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500296 loadPkcs8(*os.buf(), pw, pwLen);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700297}
298
299void
300PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
301{
302 OBufferStream os;
303 streamSource(is) >> base64Decode() >> streamSink(os);
Davide Pesavento765abc92021-12-27 00:44:04 -0500304 loadPkcs8(*os.buf(), std::move(pwCallback));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700305}
306
307void
308PrivateKey::savePkcs1(std::ostream& os) const
309{
Davide Pesavento765abc92021-12-27 00:44:04 -0500310 bufferSource(*toPkcs1()) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700311}
312
313void
314PrivateKey::savePkcs1Base64(std::ostream& os) const
315{
Davide Pesavento765abc92021-12-27 00:44:04 -0500316 bufferSource(*toPkcs1()) >> base64Encode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700317}
318
319void
320PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
321{
Davide Pesavento765abc92021-12-27 00:44:04 -0500322 bufferSource(*toPkcs8(pw, pwLen)) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700323}
324
325void
326PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
327{
Davide Pesavento765abc92021-12-27 00:44:04 -0500328 bufferSource(*toPkcs8(std::move(pwCallback))) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700329}
330
331void
332PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
333{
Davide Pesavento765abc92021-12-27 00:44:04 -0500334 bufferSource(*toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700335}
336
337void
338PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
339{
Davide Pesavento765abc92021-12-27 00:44:04 -0500340 bufferSource(*toPkcs8(std::move(pwCallback))) >> base64Encode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700341}
342
343ConstBufferPtr
344PrivateKey::derivePublicKey() const
345{
346 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
347
348 uint8_t* pkcs8 = nullptr;
349 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400350 if (len < 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500351 NDN_THROW(Error("Failed to derive public key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700352
353 auto result = make_shared<Buffer>(pkcs8, len);
354 OPENSSL_free(pkcs8);
355
356 return result;
357}
358
359ConstBufferPtr
Davide Pesavento765abc92021-12-27 00:44:04 -0500360PrivateKey::decrypt(span<const uint8_t> cipherText) const
Yingdi Yu202a2e92015-07-12 16:49:25 -0700361{
362 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
363
Davide Pesaventoc21979d2017-09-16 14:52:17 -0400364 int keyType = detail::getEvpPkeyType(m_impl->key);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400365 switch (keyType) {
366 case EVP_PKEY_NONE:
Davide Pesavento923ba442019-02-12 22:00:38 -0500367 NDN_THROW(Error("Failed to determine key type"));
Luca Keidel941fd8c2017-07-24 15:21:22 +0200368 case EVP_PKEY_RSA:
Davide Pesavento765abc92021-12-27 00:44:04 -0500369 return rsaDecrypt(cipherText);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200370 default:
Davide Pesavento923ba442019-02-12 22:00:38 -0500371 NDN_THROW(Error("Decryption is not supported for key type " + to_string(keyType)));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700372 }
373}
374
375void*
376PrivateKey::getEvpPkey() const
377{
378 return m_impl->key;
379}
380
381ConstBufferPtr
382PrivateKey::toPkcs1() const
383{
384 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200385 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700386
Davide Pesaventof45fa212017-09-14 17:23:56 -0400387 detail::Bio membio(BIO_s_mem());
388 if (!i2d_PrivateKey_bio(membio, m_impl->key))
Davide Pesavento923ba442019-02-12 22:00:38 -0500389 NDN_THROW(Error("Cannot convert key to PKCS #1 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700390
Davide Pesaventof45fa212017-09-14 17:23:56 -0400391 auto buffer = make_shared<Buffer>(BIO_pending(membio));
Davide Pesavento765abc92021-12-27 00:44:04 -0500392 if (!membio.read(*buffer))
Davide Pesavento140e9cb2019-08-19 00:02:05 -0400393 NDN_THROW(Error("Read error during PKCS #1 conversion"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700394
395 return buffer;
396}
397
398ConstBufferPtr
399PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
400{
Yingdi Yu202a2e92015-07-12 16:49:25 -0700401 BOOST_ASSERT(std::strlen(pw) == pwLen);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200402 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
403 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700404
Davide Pesaventof45fa212017-09-14 17:23:56 -0400405 detail::Bio membio(BIO_s_mem());
Davide Pesaventoee77ca02018-10-10 01:45:24 -0400406 if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
Davide Pesaventof45fa212017-09-14 17:23:56 -0400407 nullptr, const_cast<char*>(pw)))
Davide Pesavento923ba442019-02-12 22:00:38 -0500408 NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700409
Davide Pesaventof45fa212017-09-14 17:23:56 -0400410 auto buffer = make_shared<Buffer>(BIO_pending(membio));
Davide Pesavento765abc92021-12-27 00:44:04 -0500411 if (!membio.read(*buffer))
Davide Pesavento140e9cb2019-08-19 00:02:05 -0400412 NDN_THROW(Error("Read error during PKCS #8 conversion"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700413
414 return buffer;
415}
416
417ConstBufferPtr
418PrivateKey::toPkcs8(PasswordCallback pwCallback) const
419{
420 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200421 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700422
Davide Pesaventof45fa212017-09-14 17:23:56 -0400423 detail::Bio membio(BIO_s_mem());
Davide Pesaventoee77ca02018-10-10 01:45:24 -0400424 if (!i2d_PKCS8PrivateKey_bio(membio, m_impl->key, EVP_aes_256_cbc(), nullptr, 0,
Davide Pesaventof45fa212017-09-14 17:23:56 -0400425 &passwordCallbackWrapper, &pwCallback))
Davide Pesavento923ba442019-02-12 22:00:38 -0500426 NDN_THROW(Error("Cannot convert key to PKCS #8 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700427
Davide Pesaventof45fa212017-09-14 17:23:56 -0400428 auto buffer = make_shared<Buffer>(BIO_pending(membio));
Davide Pesavento765abc92021-12-27 00:44:04 -0500429 if (!membio.read(*buffer))
Davide Pesavento140e9cb2019-08-19 00:02:05 -0400430 NDN_THROW(Error("Read error during PKCS #8 conversion"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700431
432 return buffer;
433}
434
435ConstBufferPtr
Davide Pesavento765abc92021-12-27 00:44:04 -0500436PrivateKey::rsaDecrypt(span<const uint8_t> cipherText) const
Yingdi Yu202a2e92015-07-12 16:49:25 -0700437{
438 detail::EvpPkeyCtx ctx(m_impl->key);
439
Davide Pesaventof45fa212017-09-14 17:23:56 -0400440 if (EVP_PKEY_decrypt_init(ctx) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500441 NDN_THROW(Error("Failed to initialize decryption context"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700442
Davide Pesaventof45fa212017-09-14 17:23:56 -0400443 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500444 NDN_THROW(Error("Failed to set padding"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700445
446 size_t outlen = 0;
447 // Determine buffer length
Davide Pesavento765abc92021-12-27 00:44:04 -0500448 if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, cipherText.data(), cipherText.size()) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500449 NDN_THROW(Error("Failed to estimate output length"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700450
451 auto out = make_shared<Buffer>(outlen);
Davide Pesavento765abc92021-12-27 00:44:04 -0500452 if (EVP_PKEY_decrypt(ctx, out->data(), &outlen, cipherText.data(), cipherText.size()) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500453 NDN_THROW(Error("Failed to decrypt ciphertext"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700454
455 out->resize(outlen);
456 return out;
457}
458
Davide Pesaventof45fa212017-09-14 17:23:56 -0400459unique_ptr<PrivateKey>
460PrivateKey::generateRsaKey(uint32_t keySize)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700461{
462 detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
463
Davide Pesaventof45fa212017-09-14 17:23:56 -0400464 if (EVP_PKEY_keygen_init(kctx) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500465 NDN_THROW(PrivateKey::Error("Failed to initialize RSA keygen context"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700466
Davide Pesaventof45fa212017-09-14 17:23:56 -0400467 if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500468 NDN_THROW(PrivateKey::Error("Failed to set RSA key length"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700469
470 auto privateKey = make_unique<PrivateKey>();
Davide Pesaventof45fa212017-09-14 17:23:56 -0400471 if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
Davide Pesavento923ba442019-02-12 22:00:38 -0500472 NDN_THROW(PrivateKey::Error("Failed to generate RSA key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700473
474 return privateKey;
475}
476
Davide Pesaventof45fa212017-09-14 17:23:56 -0400477unique_ptr<PrivateKey>
478PrivateKey::generateEcKey(uint32_t keySize)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700479{
Junxiao Shifbb69902020-04-24 08:39:18 +0000480 EC_KEY* eckey = nullptr;
Yingdi Yu202a2e92015-07-12 16:49:25 -0700481 switch (keySize) {
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400482 case 224:
Junxiao Shifbb69902020-04-24 08:39:18 +0000483 eckey = EC_KEY_new_by_curve_name(NID_secp224r1);
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400484 break;
485 case 256:
Junxiao Shifbb69902020-04-24 08:39:18 +0000486 eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); // same as secp256r1
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400487 break;
488 case 384:
Junxiao Shifbb69902020-04-24 08:39:18 +0000489 eckey = EC_KEY_new_by_curve_name(NID_secp384r1);
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400490 break;
491 case 521:
Junxiao Shifbb69902020-04-24 08:39:18 +0000492 eckey = EC_KEY_new_by_curve_name(NID_secp521r1);
Davide Pesavento3c7969f2018-09-08 15:31:35 -0400493 break;
494 default:
Davide Pesavento923ba442019-02-12 22:00:38 -0500495 NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700496 }
Davide Pesavento97ee8112020-08-11 21:38:34 -0400497 if (eckey == nullptr) {
Junxiao Shifbb69902020-04-24 08:39:18 +0000498 NDN_THROW(Error("Failed to set EC curve"));
Davide Pesavento97ee8112020-08-11 21:38:34 -0400499 }
Yingdi Yu202a2e92015-07-12 16:49:25 -0700500
Davide Pesavento97ee8112020-08-11 21:38:34 -0400501 auto guard = make_scope_exit([eckey] { EC_KEY_free(eckey); });
Yingdi Yu202a2e92015-07-12 16:49:25 -0700502
Junxiao Shifbb69902020-04-24 08:39:18 +0000503#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 }
Yingdi Yu202a2e92015-07-12 16:49:25 -0700510
511 auto privateKey = make_unique<PrivateKey>();
Junxiao Shifbb69902020-04-24 08:39:18 +0000512 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"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700517
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500518 return privateKey;
519}
520
521unique_ptr<PrivateKey>
522PrivateKey::generateHmacKey(uint32_t keySize)
523{
524 std::vector<uint8_t> rawKey(keySize / 8);
Davide Pesavento765abc92021-12-27 00:44:04 -0500525 random::generateSecureBytes(rawKey);
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500526
527 auto privateKey = make_unique<PrivateKey>();
laqinfan48f97242019-06-03 15:33:58 -0500528 try {
Davide Pesavento765abc92021-12-27 00:44:04 -0500529 privateKey->loadRaw(KeyType::HMAC, rawKey);
laqinfan48f97242019-06-03 15:33:58 -0500530 }
531 catch (const PrivateKey::Error&) {
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500532 NDN_THROW(PrivateKey::Error("Failed to generate HMAC key"));
laqinfan48f97242019-06-03 15:33:58 -0500533 }
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500534
Yingdi Yu202a2e92015-07-12 16:49:25 -0700535 return privateKey;
536}
537
538unique_ptr<PrivateKey>
539generatePrivateKey(const KeyParams& keyParams)
540{
541 switch (keyParams.getKeyType()) {
542 case KeyType::RSA: {
Davide Pesavento765abc92021-12-27 00:44:04 -0500543 const auto& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400544 return PrivateKey::generateRsaKey(rsaParams.getKeySize());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700545 }
546 case KeyType::EC: {
Davide Pesavento765abc92021-12-27 00:44:04 -0500547 const auto& ecParams = static_cast<const EcKeyParams&>(keyParams);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400548 return PrivateKey::generateEcKey(ecParams.getKeySize());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700549 }
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500550 case KeyType::HMAC: {
Davide Pesavento765abc92021-12-27 00:44:04 -0500551 const auto& hmacParams = static_cast<const HmacKeyParams&>(keyParams);
Laqin Fan0fe72ea2019-05-22 16:42:59 -0500552 return PrivateKey::generateHmacKey(hmacParams.getKeySize());
553 }
Yingdi Yu202a2e92015-07-12 16:49:25 -0700554 default:
laqinfan48f97242019-06-03 15:33:58 -0500555 NDN_THROW(std::invalid_argument("Unsupported key type " +
Davide Pesavento923ba442019-02-12 22:00:38 -0500556 boost::lexical_cast<std::string>(keyParams.getKeyType())));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700557 }
558}
559
560} // namespace transform
561} // namespace security
562} // namespace ndn