blob: 9cad918be26365ea818915c540b0836bf948f500 [file] [log] [blame]
Yingdi Yu202a2e92015-07-12 16:49:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
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
44class PrivateKey::Impl
45{
46public:
47 Impl()
48 : key(nullptr)
49 {
50 }
51
52 ~Impl()
53 {
54 EVP_PKEY_free(key);
55 }
56
57public:
58 EVP_PKEY* key;
59};
60
61PrivateKey::PrivateKey()
62 : m_impl(new Impl)
63{
64}
65
66PrivateKey::~PrivateKey() = default;
67
68void
69PrivateKey::loadPkcs1(const uint8_t* buf, size_t size)
70{
71 detail::Bio mem(BIO_s_mem());
72 BIO_write(mem.get(), buf, size);
73
74 d2i_PrivateKey_bio(mem.get(), &m_impl->key);
75
76 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
77}
78
79void
80PrivateKey::loadPkcs1(std::istream& is)
81{
82 OBufferStream os;
83 streamSource(is) >> streamSink(os);
84 this->loadPkcs1(os.buf()->buf(), os.buf()->size());
85}
86
87void
88PrivateKey::loadPkcs1Base64(const uint8_t* buf, size_t size)
89{
90 OBufferStream os;
91 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
92 this->loadPkcs1(os.buf()->buf(), os.buf()->size());
93}
94
95void
96PrivateKey::loadPkcs1Base64(std::istream& is)
97{
98 OBufferStream os;
99 streamSource(is) >> base64Decode() >> streamSink(os);
100 this->loadPkcs1(os.buf()->buf(), os.buf()->size());
101}
102
103void
104PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
105{
106 BOOST_ASSERT(std::strlen(pw) == pwLen);
107
108 detail::Bio mem(BIO_s_mem());
109 BIO_write(mem.get(), buf, size);
110
111 m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, nullptr, const_cast<char*>(pw));
112
113 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
114}
115
116static inline int
117passwordCallback(char* buf, int size, int rwflag, void* u)
118{
119 auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
120 return (*cb)(buf, size, rwflag);
121}
122
123void
124PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
125{
126 OpenSSL_add_all_algorithms();
127 detail::Bio mem(BIO_s_mem());
128 BIO_write(mem.get(), buf, size);
129
130 if (pwCallback)
131 m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, passwordCallback, &pwCallback);
132 else
133 m_impl->key = d2i_PKCS8PrivateKey_bio(mem.get(), &m_impl->key, nullptr, nullptr);
134
135 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
136}
137
138void
139PrivateKey::loadPkcs8(std::istream& is, const char* pw, size_t pwLen)
140{
141 OBufferStream os;
142 streamSource(is) >> streamSink(os);
143 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pw, pwLen);
144}
145
146void
147PrivateKey::loadPkcs8(std::istream& is, PasswordCallback pwCallback)
148{
149 OBufferStream os;
150 streamSource(is) >> streamSink(os);
151 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pwCallback);
152}
153
154void
155PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
156{
157 OBufferStream os;
158 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
159 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pw, pwLen);
160}
161
162void
163PrivateKey::loadPkcs8Base64(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
164{
165 OBufferStream os;
166 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
167 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pwCallback);
168}
169
170void
171PrivateKey::loadPkcs8Base64(std::istream& is, const char* pw, size_t pwLen)
172{
173 OBufferStream os;
174 streamSource(is) >> base64Decode() >> streamSink(os);
175 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pw, pwLen);
176}
177
178void
179PrivateKey::loadPkcs8Base64(std::istream& is, PasswordCallback pwCallback)
180{
181 OBufferStream os;
182 streamSource(is) >> base64Decode() >> streamSink(os);
183 this->loadPkcs8(os.buf()->buf(), os.buf()->size(), pwCallback);
184}
185
186void
187PrivateKey::savePkcs1(std::ostream& os) const
188{
189 bufferSource(*this->toPkcs1()) >> streamSink(os);
190}
191
192void
193PrivateKey::savePkcs1Base64(std::ostream& os) const
194{
195 bufferSource(*this->toPkcs1()) >> base64Encode() >> streamSink(os);
196}
197
198void
199PrivateKey::savePkcs8(std::ostream& os, const char* pw, size_t pwLen) const
200{
201 bufferSource(*this->toPkcs8(pw, pwLen)) >> streamSink(os);
202}
203
204void
205PrivateKey::savePkcs8(std::ostream& os, PasswordCallback pwCallback) const
206{
207 bufferSource(*this->toPkcs8(pwCallback)) >> streamSink(os);
208}
209
210void
211PrivateKey::savePkcs8Base64(std::ostream& os, const char* pw, size_t pwLen) const
212{
213 bufferSource(*this->toPkcs8(pw, pwLen)) >> base64Encode() >> streamSink(os);
214}
215
216void
217PrivateKey::savePkcs8Base64(std::ostream& os, PasswordCallback pwCallback) const
218{
219 bufferSource(*this->toPkcs8(pwCallback)) >> base64Encode() >> streamSink(os);
220}
221
222ConstBufferPtr
223PrivateKey::derivePublicKey() const
224{
225 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
226
227 uint8_t* pkcs8 = nullptr;
228 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
229
230 if (len <= 0)
231 BOOST_THROW_EXCEPTION(Error("Failed to derive public key"));
232
233 auto result = make_shared<Buffer>(pkcs8, len);
234 OPENSSL_free(pkcs8);
235
236 return result;
237}
238
239ConstBufferPtr
240PrivateKey::decrypt(const uint8_t* cipherText, size_t cipherLen) const
241{
242 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
243
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700244#if OPENSSL_VERSION_NUMBER < 0x1010000fL
Yingdi Yu202a2e92015-07-12 16:49:25 -0700245 switch (EVP_PKEY_type(m_impl->key->type)) {
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700246#else
247 switch (EVP_PKEY_base_id(m_impl->key)) {
248#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
Yingdi Yu202a2e92015-07-12 16:49:25 -0700249 case EVP_PKEY_RSA:
250 return rsaDecrypt(cipherText, cipherLen);
251 default:
252 BOOST_THROW_EXCEPTION(Error("Decryption is not supported for this key type"));
253 }
254}
255
256void*
257PrivateKey::getEvpPkey() const
258{
259 return m_impl->key;
260}
261
262ConstBufferPtr
263PrivateKey::toPkcs1() const
264{
265 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
266
267 OpenSSL_add_all_algorithms();
268 detail::Bio mem(BIO_s_mem());
269 int ret = i2d_PrivateKey_bio(mem.get(), m_impl->key);
270 if (ret != 1)
271 BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS1 format"));
272
273 int len8 = BIO_pending(mem.get());
274 auto buffer = make_shared<Buffer>(len8);
275 BIO_read(mem.get(), buffer->buf(), len8);
276
277 return buffer;
278}
279
280ConstBufferPtr
281PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
282{
283 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
284
285 BOOST_ASSERT(std::strlen(pw) == pwLen);
286
287 OpenSSL_add_all_algorithms();
288 detail::Bio mem(BIO_s_mem());
289 int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
290 const_cast<char*>(pw), pwLen, nullptr, nullptr);
291 if (ret != 1)
292 BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS8 format"));
293
294 int len8 = BIO_pending(mem.get());
295 auto buffer = make_shared<Buffer>(len8);
296 BIO_read(mem.get(), buffer->buf(), len8);
297
298 return buffer;
299}
300
301ConstBufferPtr
302PrivateKey::toPkcs8(PasswordCallback pwCallback) const
303{
304 ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
305
306 OpenSSL_add_all_algorithms();
307 detail::Bio mem(BIO_s_mem());
308 int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
309 nullptr, 0,
310 passwordCallback, &pwCallback);
311 if (ret != 1)
312 BOOST_THROW_EXCEPTION(Error("Cannot convert key into PKCS8 format"));
313
314 int len8 = BIO_pending(mem.get());
315 auto buffer = make_shared<Buffer>(len8);
316 BIO_read(mem.get(), buffer->buf(), len8);
317
318 return buffer;
319}
320
321ConstBufferPtr
322PrivateKey::rsaDecrypt(const uint8_t* cipherText, size_t cipherLen) const
323{
324 detail::EvpPkeyCtx ctx(m_impl->key);
325
326 if (EVP_PKEY_decrypt_init(ctx.get()) <= 0)
327 BOOST_THROW_EXCEPTION(Error("Failed to initialize decryption context"));
328
329 if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0)
330 BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
331
332 size_t outlen = 0;
333 // Determine buffer length
334 if (EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen, cipherText, cipherLen) <= 0)
335 BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
336
337 auto out = make_shared<Buffer>(outlen);
338
339 if (EVP_PKEY_decrypt(ctx.get(), out->buf(), &outlen, cipherText, cipherLen) <= 0)
340 BOOST_THROW_EXCEPTION(Error("Failed to decrypt cipher text"));
341
342 out->resize(outlen);
343 return out;
344}
345
346static unique_ptr<PrivateKey>
347generateRsaKey(uint32_t keySize)
348{
349 detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
350
351 int ret = EVP_PKEY_keygen_init(kctx.get());
352 if (ret != 1)
353 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
354
355 ret = EVP_PKEY_CTX_set_rsa_keygen_bits(kctx.get(), keySize);
356 if (ret != 1)
357 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
358
359 detail::EvpPkey key;
360 ret = EVP_PKEY_keygen(kctx.get(), &key);
361 if (ret != 1)
362 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate RSA key"));
363
364 detail::Bio mem(BIO_s_mem());
365 i2d_PrivateKey_bio(mem.get(), key.get());
366 int len = BIO_pending(mem.get());
367 Buffer buffer(len);
368 BIO_read(mem.get(), buffer.buf(), len);
369
370 auto privateKey = make_unique<PrivateKey>();
371 privateKey->loadPkcs1(buffer.buf(), buffer.size());
372
373 return privateKey;
374}
375
376static unique_ptr<PrivateKey>
377generateEcKey(uint32_t keySize)
378{
379 detail::EvpPkeyCtx ctx(EVP_PKEY_EC);
380
381 int ret = EVP_PKEY_paramgen_init(ctx.get());
382 if (ret != 1)
383 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
384
385 switch (keySize) {
386 case 256:
387 ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_X9_62_prime256v1);
388 break;
389 case 384:
390 ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1);
391 break;
392 default:
393 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
394 }
395 if (ret != 1)
396 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
397
398 detail::EvpPkey params;
399 ret = EVP_PKEY_paramgen(ctx.get(), &params);
400 if (ret != 1)
401 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
402
403 detail::EvpPkeyCtx kctx(params.get());
404 ret = EVP_PKEY_keygen_init(kctx.get());
405 if (ret != 1)
406 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
407
408 detail::EvpPkey key;
409 ret = EVP_PKEY_keygen(kctx.get(), &key);
410 if (ret != 1)
411 BOOST_THROW_EXCEPTION(PrivateKey::Error("Fail to generate EC key"));
412
413 detail::Bio mem(BIO_s_mem());
414 i2d_PrivateKey_bio(mem.get(), key.get());
415 int len = BIO_pending(mem.get());
416 Buffer buffer(len);
417 BIO_read(mem.get(), buffer.buf(), len);
418
419 auto privateKey = make_unique<PrivateKey>();
420 privateKey->loadPkcs1(buffer.buf(), buffer.size());
421
422 return privateKey;
423}
424
425unique_ptr<PrivateKey>
426generatePrivateKey(const KeyParams& keyParams)
427{
428 switch (keyParams.getKeyType()) {
429 case KeyType::RSA: {
430 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(keyParams);
431 return generateRsaKey(rsaParams.getKeySize());
432 }
433 case KeyType::EC: {
Spyridon Mastorakis1ece2e32015-08-27 18:52:21 -0700434 const EcKeyParams& ecParams = static_cast<const EcKeyParams&>(keyParams);
435 return generateEcKey(ecParams.getKeySize());
Yingdi Yu202a2e92015-07-12 16:49:25 -0700436 }
437 default:
438 BOOST_THROW_EXCEPTION(std::invalid_argument("Unsupported asymmetric key type"));
439 }
440}
441
442} // namespace transform
443} // namespace security
444} // namespace ndn