blob: 1cd974a9d892912067422a7147606f52248d8efb [file] [log] [blame]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Zhiyi Zhanga67fa462020-04-19 13:48:03 -07002/**
3 * Copyright (c) 2017-2020, Regents of the University of California.
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07004 *
5 * This file is part of ndncert, a certificate management system based on NDN.
6 *
7 * ndncert is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * ndncert 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 General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License along with
16 * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ndncert authors and contributors.
19 */
20
21#include "crypto-helper.hpp"
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070022#include <openssl/err.h>
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -070023#include <openssl/hmac.h>
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070024#include <openssl/pem.h>
Zhiyi Zhang2b6d80c2020-10-12 12:49:14 -070025#include <openssl/ec.h>
26#include <openssl/evp.h>
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070027#include <openssl/kdf.h>
Davide Pesavento368341b2019-08-13 23:57:50 -040028#include <ndn-cxx/encoding/buffer-stream.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070029#include <ndn-cxx/security/transform/base64-decode.hpp>
30#include <ndn-cxx/security/transform/base64-encode.hpp>
31#include <ndn-cxx/security/transform/buffer-source.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070032#include <ndn-cxx/security/transform/stream-sink.hpp>
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -070033#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070034
35namespace ndn {
36namespace ndncert {
37
Zhiyi Zhang63123172020-10-12 14:24:44 -070038struct ECDHState::ECDH_CTX
39{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070040 EVP_PKEY_CTX* ctx_params = nullptr;
41 EVP_PKEY_CTX* ctx_keygen = nullptr;
42 EVP_PKEY* privkey = nullptr;
43 EVP_PKEY* peerkey = nullptr;
44 EVP_PKEY* params = nullptr;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070045};
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070046
47ECDHState::ECDHState()
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070048 : m_publicKeyLen(0)
49 , m_sharedSecretLen(0)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070050{
51 OpenSSL_add_all_algorithms();
52 context = std::make_unique<ECDH_CTX>();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070053 auto EC_NID = NID_X9_62_prime256v1;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070054
55 // Create the context for parameter generation
56 if (nullptr == (context->ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr))) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070057 NDN_THROW(std::runtime_error("Could not create context contexts."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070058 }
59
60 // Initialise the parameter generation
61 if (EVP_PKEY_paramgen_init(context->ctx_params) != 1) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070062 NDN_THROW(std::runtime_error("Could not initialize parameter generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070063 }
64
65 // We're going to use the ANSI X9.62 Prime 256v1 curve
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070066 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(context->ctx_params, EC_NID)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070067 NDN_THROW(std::runtime_error("Likely unknown elliptical curve ID specified."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070068 }
69
70 // Create the parameter object params
71 if (!EVP_PKEY_paramgen(context->ctx_params, &context->params)) {
72 // the generated key is written to context->params
Zhiyi Zhang5c059452020-10-16 17:43:31 -070073 NDN_THROW(std::runtime_error("Could not create parameter object parameters."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070074 }
75
76 // Create the context for the key generation
77 if (nullptr == (context->ctx_keygen = EVP_PKEY_CTX_new(context->params, nullptr))) {
78 //The EVP_PKEY_CTX_new() function allocates public key algorithm context using
79 //the algorithm specified in pkey and ENGINE e (in this case nullptr).
Zhiyi Zhang5c059452020-10-16 17:43:31 -070080 NDN_THROW(std::runtime_error("Could not create the context for the key generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070081 }
82
83 // initializes a public key algorithm context
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070084 if (1 != EVP_PKEY_keygen_init(context->ctx_keygen)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070085 NDN_THROW(std::runtime_error("Could not init context for key generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070086 }
87 if (1 != EVP_PKEY_keygen(context->ctx_keygen, &context->privkey)) {
88 //performs a key generation operation, the generated key is written to context->privkey.
Zhiyi Zhang5c059452020-10-16 17:43:31 -070089 NDN_THROW(std::runtime_error("Could not generate DHE keys in final step"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070090 }
91}
92
93ECDHState::~ECDHState()
94{
95 // Contexts
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070096 if (context->ctx_params != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070097 EVP_PKEY_CTX_free(context->ctx_params);
98 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070099 if (context->ctx_keygen != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700100 EVP_PKEY_CTX_free(context->ctx_keygen);
101 }
102
103 // Keys
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700104 if (context->privkey != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700105 EVP_PKEY_free(context->privkey);
106 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700107 if (context->peerkey != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700108 EVP_PKEY_free(context->peerkey);
109 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700110 if (context->params != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 EVP_PKEY_free(context->params);
112 }
113}
114
115uint8_t*
116ECDHState::getRawSelfPubKey()
117{
118 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
119
120 if (privECKey == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700121 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700122 }
123
124 auto ecPoint = EC_KEY_get0_public_key(privECKey);
125 const EC_GROUP* group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700126 m_publicKeyLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_COMPRESSED,
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700127 m_publicKey, 256, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700128 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700129 if (m_publicKeyLen == 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700130 NDN_THROW(std::runtime_error("Could not convert EC_POINTS to octet string when calling EC_POINT_point2oct."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700131 }
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700132 return m_publicKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700133}
134
135std::string
136ECDHState::getBase64PubKey()
137{
Davide Pesavento368341b2019-08-13 23:57:50 -0400138 namespace t = ndn::security::transform;
139
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700140 if (m_publicKeyLen == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700141 this->getRawSelfPubKey();
142 }
Davide Pesavento368341b2019-08-13 23:57:50 -0400143 std::ostringstream os;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700144 t::bufferSource(m_publicKey, m_publicKeyLen) >> t::base64Encode(false) >> t::streamSink(os);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700145 return os.str();
146}
147
148uint8_t*
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700149ECDHState::deriveSecret(const uint8_t* peerkey, size_t peerKeySize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700150{
151 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
152
153 if (privECKey == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700154 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700155 }
156
157 auto group = EC_KEY_get0_group(privECKey);
158 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800159 int result = EC_POINT_oct2point(group, peerPoint, peerkey, peerKeySize, nullptr);
160 if (result == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700161 EC_POINT_free(peerPoint);
162 EC_KEY_free(privECKey);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700163 NDN_THROW(std::runtime_error("Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()"));
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800164 }
165
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700166 result = ECDH_compute_key(m_sharedSecret, 256, peerPoint, privECKey, nullptr);
167 if (result == -1) {
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800168 EC_POINT_free(peerPoint);
169 EC_KEY_free(privECKey);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700170 NDN_THROW(std::runtime_error("Cannot generate ECDH secret when calling ECDH_compute_key()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700171 }
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700172 m_sharedSecretLen = static_cast<size_t>(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700173 EC_POINT_free(peerPoint);
174 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700175 return m_sharedSecret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700176}
177
178uint8_t*
179ECDHState::deriveSecret(const std::string& peerKeyStr)
180{
181 namespace t = ndn::security::transform;
Davide Pesavento368341b2019-08-13 23:57:50 -0400182
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700183 OBufferStream os;
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800184 t::bufferSource(peerKeyStr) >> t::base64Decode(false) >> t::streamSink(os);
Davide Pesavento368341b2019-08-13 23:57:50 -0400185 auto result = os.buf();
186
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700187 return this->deriveSecret(result->data(), result->size());
188}
189
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700190void
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700191hmac_sha256(const uint8_t* data, size_t data_length,
192 const uint8_t* key, size_t key_length,
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700193 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400194{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700195 auto ret = HMAC(EVP_sha256(), key, key_length, (unsigned char*)data, data_length,
196 (unsigned char*)result, nullptr);
197 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700198 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700199 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700200}
201
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700202int
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700203hkdf(const uint8_t* secret, size_t secret_len, const uint8_t* salt,
204 size_t salt_len, uint8_t* output, size_t output_len,
205 const uint8_t* info, size_t info_len)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700206{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700207 EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
208 if (EVP_PKEY_derive_init(pctx) <= 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700209 NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700210 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700211 if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700212 NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700213 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700214 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700215 NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700216 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700217 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700218 NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700219 }
220 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700221 NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700222 }
223 size_t outLen = output_len;
224 if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700225 NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700226 }
227 return (int)outLen;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700228}
229
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700230int
231aes_gcm_128_encrypt(const uint8_t* plaintext, size_t plaintext_len, const uint8_t* associated, size_t associated_len,
232 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
233{
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700234 EVP_CIPHER_CTX* ctx;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700235 int len;
236 int ciphertext_len;
237
238 // Create and initialise the context
239 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700240 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700241 }
242
243 // Initialise the encryption operation.
244 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700245 NDN_THROW(std::runtime_error("Cannot initialise the encryption operation when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700246 }
247
248 // Set IV length if default 12 bytes (96 bits) is not appropriate
249 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700250 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700251 }
252
253 // Initialise key and IV
254 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700255 NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700256 }
257
258 // Provide any AAD data. This can be called zero or more times as required
259 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700260 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700261 }
262
263 // Provide the message to be encrypted, and obtain the encrypted output.
264 // EVP_EncryptUpdate can be called multiple times if necessary
265 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700266 NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700267 }
268 ciphertext_len = len;
269
270 // Finalise the encryption. Normally ciphertext bytes may be written at
271 // this stage, but this does not occur in GCM mode
272 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700273 NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700274 }
275 ciphertext_len += len;
276
277 // Get the tag
278 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700279 NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700280 }
281
282 // Clean up
283 EVP_CIPHER_CTX_free(ctx);
284 return ciphertext_len;
285}
286
287int
288aes_gcm_128_decrypt(const uint8_t* ciphertext, size_t ciphertext_len, const uint8_t* associated, size_t associated_len,
289 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
290{
291 EVP_CIPHER_CTX* ctx;
292 int len;
293 int plaintext_len;
294 int ret;
295
296 // Create and initialise the context
297 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700298 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700299 }
300
301 // Initialise the decryption operation.
302 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700303 NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700304 }
305
306 // Set IV length. Not necessary if this is 12 bytes (96 bits)
307 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700308 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700309 }
310
311 // Initialise key and IV
312 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700313 NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700314 }
315
316 // Provide any AAD data. This can be called zero or more times as required
317 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700318 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700319 }
320
321 // Provide the message to be decrypted, and obtain the plaintext output.
322 // EVP_DecryptUpdate can be called multiple times if necessary
323 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700324 NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700325 }
326 plaintext_len = len;
327
328 // Set expected tag value. Works in OpenSSL 1.0.1d and later
329 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700330 NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700331 }
332
333 // Finalise the decryption. A positive return value indicates success,
334 // anything else is a failure - the plaintext is not trustworthy.
335 ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
336
337 // Clean up
338 EVP_CIPHER_CTX_free(ctx);
339
340 if (ret > 0) {
341 // Success
342 plaintext_len += len;
343 return plaintext_len;
344 }
345 else {
346 // Verify failed
347 return -1;
348 }
349}
350
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700351Block
352encodeBlockWithAesGcm128(uint32_t tlv_type, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
353 const uint8_t* associatedData, size_t associatedDataSize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700354{
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700355 Buffer iv;
356 iv.resize(12);
357 random::generateSecureBytes(iv.data(), iv.size());
358
359 uint8_t* encryptedPayload = new uint8_t[payloadSize];
360 uint8_t tag[16];
361 size_t encryptedPayloadLen = aes_gcm_128_encrypt(payload, payloadSize, associatedData, associatedDataSize,
362 key, iv.data(), encryptedPayload, tag);
363 auto content = makeEmptyBlock(tlv_type);
364 content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
365 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
366 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload, encryptedPayloadLen));
367 content.encode();
368 delete[] encryptedPayload;
369 return content;
370}
371
372Buffer
373decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
374{
375 block.parse();
376 Buffer result;
377 result.resize(block.get(tlv::EncryptedPayload).value_size());
378 int resultLen = aes_gcm_128_decrypt(block.get(tlv::EncryptedPayload).value(),
379 block.get(tlv::EncryptedPayload).value_size(),
380 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
381 key, block.get(tlv::InitializationVector).value(), result.data());
382 if (resultLen == -1 || resultLen != (int)block.get(tlv::EncryptedPayload).value_size()) {
383 return Buffer();
384 }
385 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700386}
387
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700388} // namespace ndncert
389} // namespace ndn