blob: c17fdc045a1c9cf50d6412eb2c7eed5b433ddfd2 [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"
23#include "buffer-source.hpp"
24#include "stream-source.hpp"
25#include "base64-encode.hpp"
26#include "base64-decode.hpp"
27#include "stream-sink.hpp"
28#include "../../encoding/buffer-stream.hpp"
29#include "../detail/openssl-helper.hpp"
30
31#define ENSURE_PUBLIC_KEY_LOADED(key) \
32 do { \
33 if (key == nullptr) \
34 BOOST_THROW_EXCEPTION(Error("Public key has not been loaded yet")); \
35 } while (false)
36
37namespace ndn {
38namespace security {
39namespace transform {
40
41class PublicKey::Impl
42{
43public:
44 Impl()
45 : key(nullptr)
46 {
47 }
48
49 ~Impl()
50 {
51 EVP_PKEY_free(key);
52 }
53
54public:
55 EVP_PKEY* key;
56};
57
58PublicKey::PublicKey()
59 : m_impl(new Impl)
60{
61}
62
63PublicKey::~PublicKey() = default;
64
65KeyType
66PublicKey::getKeyType() const
67{
68 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
69
Alexander Afanasyev02948ec2016-09-12 18:04:50 -070070#if OPENSSL_VERSION_NUMBER < 0x1010000fL
Yingdi Yu202a2e92015-07-12 16:49:25 -070071 switch (EVP_PKEY_type(m_impl->key->type)) {
Alexander Afanasyev02948ec2016-09-12 18:04:50 -070072#else
73 switch (EVP_PKEY_base_id(m_impl->key)) {
74#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
Yingdi Yu202a2e92015-07-12 16:49:25 -070075 case EVP_PKEY_RSA:
76 return KeyType::RSA;
77 case EVP_PKEY_EC:
78 return KeyType::EC;
79 default:
80 BOOST_THROW_EXCEPTION(Error("Public key type is not recognized"));
81 }
82}
83
84void
85PublicKey::loadPkcs8(const uint8_t* buf, size_t size)
86{
87 m_impl->key = d2i_PUBKEY(nullptr, &buf, size);
88
89 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
90}
91
92void
93PublicKey::loadPkcs8(std::istream& is)
94{
95 OBufferStream os;
96 {
97 using namespace transform;
98 streamSource(is) >> streamSink(os);
99 }
100 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
101}
102
103void
104PublicKey::loadPkcs8Base64(const uint8_t* buf, size_t size)
105{
106 OBufferStream os;
107 {
108 using namespace transform;
109 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
110 }
111 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
112}
113
114void
115PublicKey::loadPkcs8Base64(std::istream& is)
116{
117 OBufferStream os;
118 {
119 using namespace transform;
120 streamSource(is) >> base64Decode() >> streamSink(os);
121 }
122 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
123}
124
125void
126PublicKey::savePkcs8(std::ostream& os) const
127{
128 using namespace transform;
129 bufferSource(*this->toPkcs8()) >> streamSink(os);
130}
131
132void
133PublicKey::savePkcs8Base64(std::ostream& os) const
134{
135 using namespace transform;
136 bufferSource(*this->toPkcs8()) >> base64Encode() >> streamSink(os);
137}
138
139ConstBufferPtr
140PublicKey::encrypt(const uint8_t* plainText, size_t plainLen) const
141{
142 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
143
Davide Pesaventof45fa212017-09-14 17:23:56 -0400144 int keyType =
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700145#if OPENSSL_VERSION_NUMBER < 0x1010000fL
Davide Pesaventof45fa212017-09-14 17:23:56 -0400146 EVP_PKEY_type(m_impl->key->type);
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700147#else
Davide Pesaventof45fa212017-09-14 17:23:56 -0400148 EVP_PKEY_base_id(m_impl->key);
Alexander Afanasyev02948ec2016-09-12 18:04:50 -0700149#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
Davide Pesaventof45fa212017-09-14 17:23:56 -0400150
151 switch (keyType) {
152 case EVP_PKEY_NONE:
153 BOOST_THROW_EXCEPTION(Error("Failed to determine key type"));
154 case EVP_PKEY_RSA:
155 return rsaEncrypt(plainText, plainLen);
156 default:
157 BOOST_THROW_EXCEPTION(Error("Encryption is not supported for key type " + to_string(keyType)));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700158 }
159}
160
161void*
162PublicKey::getEvpPkey() const
163{
164 return m_impl->key;
165}
166
167ConstBufferPtr
168PublicKey::toPkcs8() const
169{
170 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
171
172 uint8_t* pkcs8 = nullptr;
173 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
174
175 if (pkcs8 == nullptr)
176 BOOST_THROW_EXCEPTION(Error("Failed to convert to pkcs8 format"));
177
178 auto buffer = make_shared<Buffer>(pkcs8, len);
179 OPENSSL_free(pkcs8);
180
181 return buffer;
182}
183
184ConstBufferPtr
185PublicKey::rsaEncrypt(const uint8_t* plainText, size_t plainLen) const
186{
187 detail::EvpPkeyCtx ctx(m_impl->key);
188
Davide Pesaventof45fa212017-09-14 17:23:56 -0400189 if (EVP_PKEY_encrypt_init(ctx) <= 0)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700190 BOOST_THROW_EXCEPTION(Error("Failed to initialize encryption context"));
191
Davide Pesaventof45fa212017-09-14 17:23:56 -0400192 if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700193 BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
194
195 size_t outlen = 0;
196 // Determine buffer length
Davide Pesaventof45fa212017-09-14 17:23:56 -0400197 if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, plainText, plainLen) <= 0)
Yingdi Yu202a2e92015-07-12 16:49:25 -0700198 BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
199
200 auto out = make_shared<Buffer>(outlen);
201
Davide Pesaventof45fa212017-09-14 17:23:56 -0400202 if (EVP_PKEY_encrypt(ctx, out->buf(), &outlen, plainText, plainLen) <= 0)
Muktadir R Chowdhury80fba6c2017-05-05 15:30:15 -0500203 BOOST_THROW_EXCEPTION(Error("Failed to encrypt plaintext"));
Yingdi Yu202a2e92015-07-12 16:49:25 -0700204
205 out->resize(outlen);
206 return out;
207}
208
209} // namespace transform
210} // namespace security
211} // namespace ndn