blob: c67d3d993465841c9736eea3d6cdc384b977cfe5 [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/*
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -07003 * Copyright (c) 2013-2017 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
22#include "private-key.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070023#include "base64-decode.hpp"
Davide Pesaventoe1789892017-02-26 15:50:52 -050024#include "base64-encode.hpp"
25#include "buffer-source.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070026#include "stream-sink.hpp"
Davide Pesaventoe1789892017-02-26 15:50:52 -050027#include "stream-source.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070028#include "../detail/openssl-helper.hpp"
29#include "../key-params.hpp"
Davide Pesaventoe1789892017-02-26 15:50:52 -050030#include "../../encoding/buffer-stream.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070031
Davide Pesaventoe1789892017-02-26 15:50:52 -050032#include <cstring>
Yingdi Yu202a2e92015-07-12 16:49:25 -070033
34#define ENSURE_PRIVATE_KEY_LOADED(key) \
35 do { \
36 if (key == nullptr) \
37 BOOST_THROW_EXCEPTION(Error("Private key has not been loaded yet")); \
38 } while (false)
39
40namespace ndn {
41namespace security {
42namespace transform {
43
Luca Keidel941fd8c2017-07-24 15:21:22 +020044static void
45opensslInitAlgorithms()
46{
47#if OPENSSL_VERSION_NUMBER < 0x1010000fL
48 static bool isInitialized = false;
49 if (!isInitialized) {
50 OpenSSL_add_all_algorithms();
51 isInitialized = true;
52 }
53#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
54}
55
Yingdi Yu202a2e92015-07-12 16:49:25 -070056class PrivateKey::Impl
57{
58public:
59 Impl()
60 : key(nullptr)
61 {
62 }
63
64 ~Impl()
65 {
66 EVP_PKEY_free(key);
67 }
68
69public:
70 EVP_PKEY* key;
71};
72
73PrivateKey::PrivateKey()
74 : m_impl(new Impl)
75{
76}
77
78PrivateKey::~PrivateKey() = default;
79
80void
81PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
82{
83 detail::Bio mem(BIO_s_mem());
84 BIO_write(mem.get(), buf, size);
85
86 d2i_PrivateKey_bio(mem.get(), &m_impl->key);
87
88 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
89}
90
91void
92PrivateKey::loadPkcs1(std::istream& is)
93{
94 OBufferStream os;
95 streamSource(is) >> streamSink(os);
96 this->loadPkcs1(os.buf()->buf(), os.buf()->size());
97}
98
99void
100PrivateKey::loadPkcs1Base64(const uint8_t* buf, size_t size)
101{
102 OBufferStream os;
103 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
104 this->loadPkcs1(os.buf()->buf(), os.buf()->size());
105}
106
107void
108PrivateKey::loadPkcs1Base64(std::istream& is)
109{
110 OBufferStream os;
111 streamSource(is) >> base64Decode() >> streamSink(os);
112 this->loadPkcs1(os.buf()->buf(), os.buf()->size());
113}
114
115void
116PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
117{
118 BOOST_ASSERT(std::strlen(pw) == pwLen);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200119 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700120
121 detail::Bio mem(BIO_s_mem());
122 BIO_write(mem.get(), buf, size);
123
124 m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, nullptr, const_cast<char*>(pw));
125
126 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
127}
128
129static inline int
130passwordCallback(char* buf, int size, int rwflag, void* u)
131{
Luca Keidel941fd8c2017-07-24 15:21:22 +0200132 BOOST_ASSERT(size >= 0);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700133 auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200134 return (*cb)(buf, static_cast<size_t>(size), rwflag);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700135}
136
137void
138PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
139{
Luca Keidel941fd8c2017-07-24 15:21:22 +0200140 opensslInitAlgorithms();
141
Yingdi Yu202a2e92015-07-12 16:49:25 -0700142 detail::Bio mem(BIO_s_mem());
143 BIO_write(mem.get(), buf, size);
144
145 if (pwCallback)
146 m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, passwordCallback, &pwCallback);
147 else
148 m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, nullptr, nullptr);
149
150 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
151}
152
153void
154PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
155{
156 OBufferStream os;
157 streamSource(is) >> streamSink(os);
158 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pw, pwLen);
159}
160
161void
162PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
163{
164 OBufferStream os;
165 streamSource(is) >> streamSink(os);
166 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pwCallback);
167}
168
169void
170PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
171{
172 OBufferStream os;
173 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
174 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pw, pwLen);
175}
176
177void
178PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
179{
180 OBufferStream os;
181 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
182 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pwCallback);
183}
184
185void
186PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
187{
188 OBufferStream os;
189 streamSource(is) >> base64Decode() >> streamSink(os);
190 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pw, pwLen);
191}
192
193void
194PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
195{
196 OBufferStream os;
197 streamSource(is) >> base64Decode() >> streamSink(os);
198 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pwCallback);
199}
200
201void
202PrivateKey::savePkcs1(std::ostream& os) const
203{
204 bufferSource(*this->toPkcs1()) >> streamSink(os);
205}
206
207void
208PrivateKey::savePkcs1Base64(std::ostream& os) const
209{
210 bufferSource(*this->toPkcs1()) >> base64Encode() >> streamSink(os);
211}
212
213void
214PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
215{
216 bufferSource(*this->toPkcs8(pw, pwLen)) >> streamSink(os);
217}
218
219void
220PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
221{
222 bufferSource(*this->toPkcs8(pwCallback)) >> streamSink(os);
223}
224
225void
226PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
227{
228 bufferSource(*this->toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
229}
230
231void
232PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
233{
234 bufferSource(*this->toPkcs8(pwCallback)) >> base64Encode() >> streamSink(os);
235}
236
237ConstBufferPtr
238PrivateKey::derivePublicKey() const
239{
240 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
241
242 uint8_t* pkcs8 = nullptr;
243 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
244
245 if (len <= 0)
246 BOOST_THROW_EXCEPTION(Error("Failed to derive public key"));
247
248 auto result = make_shared<Buffer>(pkcs8, len);
249 OPENSSL_free(pkcs8);
250
251 return result;
252}
253
254ConstBufferPtr
255PrivateKey::decrypt(const uint8_t* cipherText, size_t cipherLen) const
256{
257 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
258
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700259#if OPENSSL_VERSION_NUMBER < 0x1010000fL
Yingdi Yu202a2e92015-07-12 16:49:25 -0700260 switch (EVP_PKEY_type(m_impl->key->type)) {
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700261#else
262 switch (EVP_PKEY_base_id(m_impl->key)) {
263#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
Luca Keidel941fd8c2017-07-24 15:21:22 +0200264 case EVP_PKEY_RSA:
265 return rsaDecrypt(cipherText, cipherLen);
266 default:
267 BOOST_THROW_EXCEPTION(Error("Decryption is not supported for this key type"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700268 }
269}
270
271void*
272PrivateKey::getEvpPkey() const
273{
274 return m_impl->key;
275}
276
277ConstBufferPtr
278PrivateKey::toPkcs1() const
279{
280 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200281 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700282
Yingdi Yu202a2e92015-07-12 16:49:25 -0700283 detail::Bio mem(BIO_s_mem());
284 int ret = i2d_PrivateKey_bio(mem.get(), m_impl->key);
285 if (ret != 1)
286 BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS1 format"));
287
288 int len8 = BIO_pending(mem.get());
289 auto buffer = make_shared<Buffer>(len8);
290 BIO_read(mem.get(), buffer->buf(), len8);
291
292 return buffer;
293}
294
295ConstBufferPtr
296PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
297{
Yingdi Yu202a2e92015-07-12 16:49:25 -0700298 BOOST_ASSERT(std::strlen(pw) == pwLen);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200299 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
300 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700301
Yingdi Yu202a2e92015-07-12 16:49:25 -0700302 detail::Bio mem(BIO_s_mem());
303 int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
304 const_cast<char*>(pw), pwLen, nullptr, nullptr);
305 if (ret != 1)
306 BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS8 format"));
307
308 int len8 = BIO_pending(mem.get());
309 auto buffer = make_shared<Buffer>(len8);
310 BIO_read(mem.get(), buffer->buf(), len8);
311
312 return buffer;
313}
314
315ConstBufferPtr
316PrivateKey::toPkcs8(PasswordCallback pwCallback) const
317{
318 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
Luca Keidel941fd8c2017-07-24 15:21:22 +0200319 opensslInitAlgorithms();
Yingdi Yu202a2e92015-07-12 16:49:25 -0700320
Yingdi Yu202a2e92015-07-12 16:49:25 -0700321 detail::Bio mem(BIO_s_mem());
322 int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
323 nullptr, 0,
324 passwordCallback, &pwCallback);
325 if (ret != 1)
326 BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS8 format"));
327
328 int len8 = BIO_pending(mem.get());
329 auto buffer = make_shared<Buffer>(len8);
330 BIO_read(mem.get(), buffer->buf(), len8);
331
332 return buffer;
333}
334
335ConstBufferPtr
336PrivateKey::rsaDecrypt(const uint8_t* cipherText, size_t cipherLen) const
337{
338 detail::EvpPkeyCtx ctx(m_impl->key);
339
340 if (EVP_PKEY_decrypt_init(ctx.get()) <= 0)
341 BOOST_THROW_EXCEPTION(Error("Failed to initialize decryption context"));
342
343 if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0)
344 BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
345
346 size_t outlen = 0;
347 // Determine buffer length
348 if (EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen, cipherText, cipherLen) <= 0)
349 BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
350
351 auto out = make_shared<Buffer>(outlen);
352
353 if (EVP_PKEY_decrypt(ctx.get(), out->buf(), &outlen, cipherText, cipherLen) <= 0)
354 BOOST_THROW_EXCEPTION(Error("Failed to decrypt cipher text"));
355
356 out->resize(outlen);
357 return out;
358}
359
360static unique_ptr<PrivateKey>
361generateRsaKey(uint32_t keySize)
362{
363 detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
364
365 int ret = EVP_PKEY_keygen_init(kctx.get());
366 if (ret != 1)
367 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
368
369 ret = EVP_PKEY_CTX_set_rsa_keygen_bits(kctx.get(), keySize);
370 if (ret != 1)
371 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
372
373 detail::EvpPkey key;
374 ret = EVP_PKEY_keygen(kctx.get(), &key);
375 if (ret != 1)
376 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
377
378 detail::Bio mem(BIO_s_mem());
379 i2d_PrivateKey_bio(mem.get(), key.get());
380 int len = BIO_pending(mem.get());
381 Buffer buffer(len);
382 BIO_read(mem.get(), buffer.buf(), len);
383
384 auto privateKey = make_unique<PrivateKey>();
385 privateKey->loadPkcs1(buffer.buf(), buffer.size());
386
387 return privateKey;
388}
389
390static unique_ptr<PrivateKey>
391generateEcKey(uint32_t keySize)
392{
393 detail::EvpPkeyCtx ctx(EVP_PKEY_EC);
394
395 int ret = EVP_PKEY_paramgen_init(ctx.get());
396 if (ret != 1)
397 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
398
399 switch (keySize) {
400 case 256:
401 ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_X9_62_prime256v1);
402 break;
403 case 384:
404 ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1);
405 break;
406 default:
407 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
408 }
409 if (ret != 1)
410 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
411
412 detail::EvpPkey params;
413 ret = EVP_PKEY_paramgen(ctx.get(), &params);
414 if (ret != 1)
415 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
416
417 detail::EvpPkeyCtx kctx(params.get());
418 ret = EVP_PKEY_keygen_init(kctx.get());
419 if (ret != 1)
420 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
421
422 detail::EvpPkey key;
423 ret = EVP_PKEY_keygen(kctx.get(), &key);
424 if (ret != 1)
425 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
426
427 detail::Bio mem(BIO_s_mem());
428 i2d_PrivateKey_bio(mem.get(), key.get());
429 int len = BIO_pending(mem.get());
430 Buffer buffer(len);
431 BIO_read(mem.get(), buffer.buf(), len);
432
433 auto privateKey = make_unique<PrivateKey>();
434 privateKey->loadPkcs1(buffer.buf(), buffer.size());
435
436 return privateKey;
437}
438
439unique_ptr<PrivateKey>
440generatePrivateKey(const KeyParams& keyParams)
441{
442 switch (keyParams.getKeyType()) {
443 case KeyType::RSA: {
444 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
445 return generateRsaKey(rsaParams.getKeySize());
446 }
447 case KeyType::EC: {
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700448 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(keyParams);
449 return generateEcKey(ecParams.getKeySize());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700450 }
451 default:
452 BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported asymmetric key type"));
453 }
454}
455
456} // namespace transform
457} // namespace security
458} // namespace ndn