blob: 3240ceee39972ccc7432166f37bef883b3ee45c4 [file] [log] [blame]
Yingdi Yu202a2e92015-07-12 16:49:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventof45fa212017-09-14 17:23:56 -04002/*
Muktadir R Chowdhury80fba6c2017-05-05 15:30:15 -05003 * 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 "public-key.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070023#include "base64-decode.hpp"
Davide Pesavento1c31a712017-09-15 00:52:03 -040024#include "base64-encode.hpp"
25#include "buffer-source.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070026#include "stream-sink.hpp"
Davide Pesavento1c31a712017-09-15 00:52:03 -040027#include "stream-source.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070028#include "../detail/openssl-helper.hpp"
Davide Pesavento1c31a712017-09-15 00:52:03 -040029#include "../../encoding/buffer-stream.hpp"
Yingdi Yu202a2e92015-07-12 16:49:25 -070030
31#define ENSURE_PUBLIC_KEY_LOADED(key) \
32 do { \
Davide Pesavento1c31a712017-09-15 00:52:03 -040033 if ((key) == nullptr) \
Yingdi Yu202a2e92015-07-12 16:49:25 -070034 BOOST_THROW_EXCEPTION(Error("Public key has not been loaded yet")); \
35 } while (false)
36
Davide Pesavento1c31a712017-09-15 00:52:03 -040037#define ENSURE_PUBLIC_KEY_NOT_LOADED(key) \
38 do { \
39 if ((key) != nullptr) \
40 BOOST_THROW_EXCEPTION(Error("Public key has already been loaded")); \
41 } while (false)
42
Yingdi Yu202a2e92015-07-12 16:49:25 -070043namespace ndn {
44namespace security {
45namespace transform {
46
47class PublicKey::Impl
48{
49public:
Davide Pesavento1c31a712017-09-15 00:52:03 -040050 Impl() noexcept
Yingdi Yu202a2e92015-07-12 16:49:25 -070051 : key(nullptr)
52 {
53 }
54
55 ~Impl()
56 {
57 EVP_PKEY_free(key);
58 }
59
60public:
61 EVP_PKEY* key;
62};
63
64PublicKey::PublicKey()
Davide Pesavento1c31a712017-09-15 00:52:03 -040065 : m_impl(make_unique<Impl>())
Yingdi Yu202a2e92015-07-12 16:49:25 -070066{
67}
68
69PublicKey::~PublicKey() = default;
70
71KeyType
72PublicKey::getKeyType() const
73{
74 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
75
Davide Pesavento1c31a712017-09-15 00:52:03 -040076 int keyType =
Alexander Afanasyev02948ec2016-09-12 18:04:50 -070077#if OPENSSL_VERSION_NUMBER < 0x1010000fL
Davide Pesavento1c31a712017-09-15 00:52:03 -040078 EVP_PKEY_type(m_impl->key->type);
Alexander Afanasyev02948ec2016-09-12 18:04:50 -070079#else
Davide Pesavento1c31a712017-09-15 00:52:03 -040080 EVP_PKEY_base_id(m_impl->key);
Alexander Afanasyev02948ec2016-09-12 18:04:50 -070081#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
Davide Pesavento1c31a712017-09-15 00:52:03 -040082
83 switch (keyType) {
Yingdi Yu202a2e92015-07-12 16:49:25 -070084 case EVP_PKEY_RSA:
85 return KeyType::RSA;
86 case EVP_PKEY_EC:
87 return KeyType::EC;
88 default:
Davide Pesavento1c31a712017-09-15 00:52:03 -040089 BOOST_THROW_EXCEPTION(Error("Unrecognized public key type"));
Yingdi Yu202a2e92015-07-12 16:49:25 -070090 }
91}
92
93void
94PublicKey::loadPkcs8(const uint8_t* buf, size_t size)
95{
Davide Pesavento1c31a712017-09-15 00:52:03 -040096 ENSURE_PUBLIC_KEY_NOT_LOADED(m_impl->key);
Yingdi Yu202a2e92015-07-12 16:49:25 -070097
Davide Pesavento1c31a712017-09-15 00:52:03 -040098 if (d2i_PUBKEY(&m_impl->key, &buf, static_cast<long>(size)) == nullptr)
99 BOOST_THROW_EXCEPTION(Error("Failed to load public key"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700100}
101
102void
103PublicKey::loadPkcs8(std::istream& is)
104{
105 OBufferStream os;
Davide Pesavento1c31a712017-09-15 00:52:03 -0400106 streamSource(is) >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700107 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
108}
109
110void
111PublicKey::loadPkcs8Base64(const uint8_t* buf, size_t size)
112{
113 OBufferStream os;
Davide Pesavento1c31a712017-09-15 00:52:03 -0400114 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700115 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
116}
117
118void
119PublicKey::loadPkcs8Base64(std::istream& is)
120{
121 OBufferStream os;
Davide Pesavento1c31a712017-09-15 00:52:03 -0400122 streamSource(is) >> base64Decode() >> streamSink(os);
Yingdi Yu202a2e92015-07-12 16:49:25 -0700123 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
124}
125
126void
127PublicKey::savePkcs8(std::ostream& os) const
128{
Yingdi Yu202a2e92015-07-12 16:49:25 -0700129 bufferSource(*this->toPkcs8()) >> streamSink(os);
130}
131
132void
133PublicKey::savePkcs8Base64(std::ostream& os) const
134{
Yingdi Yu202a2e92015-07-12 16:49:25 -0700135 bufferSource(*this->toPkcs8()) >> base64Encode() >> streamSink(os);
136}
137
138ConstBufferPtr
139PublicKey::encrypt(const uint8_t* plainText, size_t plainLen) const
140{
141 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
142
Davide Pesaventof45fa212017-09-14 17:23:56 -0400143 int keyType =
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700144#if OPENSSL_VERSION_NUMBER < 0x1010000fL
Davide Pesaventof45fa212017-09-14 17:23:56 -0400145 EVP_PKEY_type(m_impl->key->type);
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700146#else
Davide Pesaventof45fa212017-09-14 17:23:56 -0400147 EVP_PKEY_base_id(m_impl->key);
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700148#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
Davide Pesaventof45fa212017-09-14 17:23:56 -0400149
150 switch (keyType) {
151 case EVP_PKEY_NONE:
152 BOOST_THROW_EXCEPTION(Error("Failed to determine key type"));
153 case EVP_PKEY_RSA:
154 return rsaEncrypt(plainText, plainLen);
155 default:
156 BOOST_THROW_EXCEPTION(Error("Encryption is not supported for key type " + to_string(keyType)));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700157 }
158}
159
160void*
161PublicKey::getEvpPkey() const
162{
163 return m_impl->key;
164}
165
166ConstBufferPtr
167PublicKey::toPkcs8() const
168{
169 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
170
171 uint8_t* pkcs8 = nullptr;
172 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
Davide Pesavento1c31a712017-09-15 00:52:03 -0400173 if (len < 0)
174 BOOST_THROW_EXCEPTION(Error("Cannot convert key to PKCS #8 format"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700175
176 auto buffer = make_shared<Buffer>(pkcs8, len);
177 OPENSSL_free(pkcs8);
178
179 return buffer;
180}
181
182ConstBufferPtr
183PublicKey::rsaEncrypt(const uint8_t* plainText, size_t plainLen) const
184{
185 detail::EvpPkeyCtx ctx(m_impl->key);
186
Davide Pesaventof45fa212017-09-14 17:23:56 -0400187 if (EVP_PKEY_encrypt_init(ctx) <= 0)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700188 BOOST_THROW_EXCEPTION(Error("Failed to initialize encryption context"));
189
Davide Pesaventof45fa212017-09-14 17:23:56 -0400190 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700191 BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
192
193 size_t outlen = 0;
194 // Determine buffer length
Davide Pesaventof45fa212017-09-14 17:23:56 -0400195 if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, plainText, plainLen) <= 0)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700196 BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
197
198 auto out = make_shared<Buffer>(outlen);
Davide Pesaventof45fa212017-09-14 17:23:56 -0400199 if (EVP_PKEY_encrypt(ctx, out->buf(), &outlen, plainText, plainLen) <= 0)
Muktadir R Chowdhury80fba6c2017-05-05 15:30:15 -0500200 BOOST_THROW_EXCEPTION(Error("Failed to encrypt plaintext"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700201
202 out->resize(outlen);
203 return out;
204}
205
206} // namespace transform
207} // namespace security
208} // namespace ndn