blob: a0116e95c65b1c2b4110b0a1c9e669300c042bad [file] [log] [blame]
Yingdi Yu202a2e92015-07-12 16:49:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2016 Regents of the University of California.
4 *
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
70 switch (EVP_PKEY_type(m_impl->key->type)) {
71 case EVP_PKEY_RSA:
72 return KeyType::RSA;
73 case EVP_PKEY_EC:
74 return KeyType::EC;
75 default:
76 BOOST_THROW_EXCEPTION(Error("Public key type is not recognized"));
77 }
78}
79
80void
81PublicKey::loadPkcs8(const uint8_t* buf, size_t size)
82{
83 m_impl->key = d2i_PUBKEY(nullptr, &buf, size);
84
85 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
86}
87
88void
89PublicKey::loadPkcs8(std::istream& is)
90{
91 OBufferStream os;
92 {
93 using namespace transform;
94 streamSource(is) >> streamSink(os);
95 }
96 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
97}
98
99void
100PublicKey::loadPkcs8Base64(const uint8_t* buf, size_t size)
101{
102 OBufferStream os;
103 {
104 using namespace transform;
105 bufferSource(buf, size) >> base64Decode() >> streamSink(os);
106 }
107 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
108}
109
110void
111PublicKey::loadPkcs8Base64(std::istream& is)
112{
113 OBufferStream os;
114 {
115 using namespace transform;
116 streamSource(is) >> base64Decode() >> streamSink(os);
117 }
118 this->loadPkcs8(os.buf()->buf(), os.buf()->size());
119}
120
121void
122PublicKey::savePkcs8(std::ostream& os) const
123{
124 using namespace transform;
125 bufferSource(*this->toPkcs8()) >> streamSink(os);
126}
127
128void
129PublicKey::savePkcs8Base64(std::ostream& os) const
130{
131 using namespace transform;
132 bufferSource(*this->toPkcs8()) >> base64Encode() >> streamSink(os);
133}
134
135ConstBufferPtr
136PublicKey::encrypt(const uint8_t* plainText, size_t plainLen) const
137{
138 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
139
140 switch (EVP_PKEY_type(m_impl->key->type)) {
141 case EVP_PKEY_RSA:
142 return rsaEncrypt(plainText, plainLen);
143 default:
144 BOOST_THROW_EXCEPTION(Error("Encryption is not supported for this key type"));
145 }
146}
147
148void*
149PublicKey::getEvpPkey() const
150{
151 return m_impl->key;
152}
153
154ConstBufferPtr
155PublicKey::toPkcs8() const
156{
157 ENSURE_PUBLIC_KEY_LOADED(m_impl->key);
158
159 uint8_t* pkcs8 = nullptr;
160 int len = i2d_PUBKEY(m_impl->key, &pkcs8);
161
162 if (pkcs8 == nullptr)
163 BOOST_THROW_EXCEPTION(Error("Failed to convert to pkcs8 format"));
164
165 auto buffer = make_shared<Buffer>(pkcs8, len);
166 OPENSSL_free(pkcs8);
167
168 return buffer;
169}
170
171ConstBufferPtr
172PublicKey::rsaEncrypt(const uint8_t* plainText, size_t plainLen) const
173{
174 detail::EvpPkeyCtx ctx(m_impl->key);
175
176 if (EVP_PKEY_encrypt_init(ctx.get()) <= 0)
177 BOOST_THROW_EXCEPTION(Error("Failed to initialize encryption context"));
178
179 if (EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0)
180 BOOST_THROW_EXCEPTION(Error("Failed to set padding"));
181
182 size_t outlen = 0;
183 // Determine buffer length
184 if (EVP_PKEY_encrypt(ctx.get(), nullptr, &outlen, plainText, plainLen) <= 0)
185 BOOST_THROW_EXCEPTION(Error("Failed to estimate output length"));
186
187 auto out = make_shared<Buffer>(outlen);
188
189 if (EVP_PKEY_encrypt(ctx.get(), out->buf(), &outlen, plainText, plainLen) <= 0)
190 BOOST_THROW_EXCEPTION(Error("Failed to decrypt cipher text"));
191
192 out->resize(outlen);
193 return out;
194}
195
196} // namespace transform
197} // namespace security
198} // namespace ndn