blob: 0c1dfbd5848769ee9acdf50259ce893b2ef20176 [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>
Davide Pesavento368341b2019-08-13 23:57:50 -040032#include <ndn-cxx/security/transform/private-key.hpp>
33#include <ndn-cxx/security/transform/signer-filter.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070034#include <ndn-cxx/security/transform/step-source.hpp>
35#include <ndn-cxx/security/transform/stream-sink.hpp>
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -070036#include <ndn-cxx/util/random.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070037
38namespace ndn {
39namespace ndncert {
40
Zhiyi Zhang63123172020-10-12 14:24:44 -070041struct ECDHState::ECDH_CTX
42{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070043 EVP_PKEY_CTX* ctx_params = nullptr;
44 EVP_PKEY_CTX* ctx_keygen = nullptr;
45 EVP_PKEY* privkey = nullptr;
46 EVP_PKEY* peerkey = nullptr;
47 EVP_PKEY* params = nullptr;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070048};
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070049
50ECDHState::ECDHState()
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070051 : m_publicKeyLen(0)
52 , m_sharedSecretLen(0)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070053{
54 OpenSSL_add_all_algorithms();
55 context = std::make_unique<ECDH_CTX>();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070056 auto EC_NID = NID_X9_62_prime256v1;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070057
58 // Create the context for parameter generation
59 if (nullptr == (context->ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr))) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070060 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not create context contexts."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070061 }
62
63 // Initialise the parameter generation
64 if (EVP_PKEY_paramgen_init(context->ctx_params) != 1) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070065 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not initialize parameter generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070066 }
67
68 // We're going to use the ANSI X9.62 Prime 256v1 curve
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070069 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(context->ctx_params, EC_NID)) {
70 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Likely unknown elliptical curve ID specified."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070071 }
72
73 // Create the parameter object params
74 if (!EVP_PKEY_paramgen(context->ctx_params, &context->params)) {
75 // the generated key is written to context->params
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070076 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not create parameter object parameters."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070077 }
78
79 // Create the context for the key generation
80 if (nullptr == (context->ctx_keygen = EVP_PKEY_CTX_new(context->params, nullptr))) {
81 //The EVP_PKEY_CTX_new() function allocates public key algorithm context using
82 //the algorithm specified in pkey and ENGINE e (in this case nullptr).
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070083 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not create the context for the key generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070084 }
85
86 // initializes a public key algorithm context
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070087 if (1 != EVP_PKEY_keygen_init(context->ctx_keygen)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070088 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not init context for key generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070089 }
90 if (1 != EVP_PKEY_keygen(context->ctx_keygen, &context->privkey)) {
91 //performs a key generation operation, the generated key is written to context->privkey.
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070092 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not generate DHE keys in final step"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070093 }
94}
95
96ECDHState::~ECDHState()
97{
98 // Contexts
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070099 if (context->ctx_params != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700100 EVP_PKEY_CTX_free(context->ctx_params);
101 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700102 if (context->ctx_keygen != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700103 EVP_PKEY_CTX_free(context->ctx_keygen);
104 }
105
106 // Keys
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700107 if (context->privkey != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700108 EVP_PKEY_free(context->privkey);
109 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700110 if (context->peerkey != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 EVP_PKEY_free(context->peerkey);
112 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700113 if (context->params != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700114 EVP_PKEY_free(context->params);
115 }
116}
117
118uint8_t*
119ECDHState::getRawSelfPubKey()
120{
121 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
122
123 if (privECKey == nullptr) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700124 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not get key when calling EVP_PKEY_get1_EC_KEY()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700125 }
126
127 auto ecPoint = EC_KEY_get0_public_key(privECKey);
128 const EC_GROUP* group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700129 m_publicKeyLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_COMPRESSED,
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700130 m_publicKey, 256, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700131 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700132 if (m_publicKeyLen == 0) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700133 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not convert EC_POINTS to octet string when calling EC_POINT_point2oct."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700134 }
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700135 return m_publicKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700136}
137
138std::string
139ECDHState::getBase64PubKey()
140{
Davide Pesavento368341b2019-08-13 23:57:50 -0400141 namespace t = ndn::security::transform;
142
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700143 if (m_publicKeyLen == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700144 this->getRawSelfPubKey();
145 }
Davide Pesavento368341b2019-08-13 23:57:50 -0400146 std::ostringstream os;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700147 t::bufferSource(m_publicKey, m_publicKeyLen) >> t::base64Encode(false) >> t::streamSink(os);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700148 return os.str();
149}
150
151uint8_t*
152ECDHState::deriveSecret(const uint8_t* peerkey, int peerKeySize)
153{
154 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
155
156 if (privECKey == nullptr) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700157 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700158 }
159
160 auto group = EC_KEY_get0_group(privECKey);
161 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800162 int result = EC_POINT_oct2point(group, peerPoint, peerkey, peerKeySize, nullptr);
163 if (result == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700164 EC_POINT_free(peerPoint);
165 EC_KEY_free(privECKey);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700166 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()"));
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800167 }
168
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700169 result = ECDH_compute_key(m_sharedSecret, 256, peerPoint, privECKey, nullptr);
170 if (result == -1) {
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800171 EC_POINT_free(peerPoint);
172 EC_KEY_free(privECKey);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700173 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot generate ECDH secret when calling ECDH_compute_key()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700174 }
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700175 m_sharedSecretLen = static_cast<size_t>(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700176 EC_POINT_free(peerPoint);
177 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700178 return m_sharedSecret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700179}
180
181uint8_t*
182ECDHState::deriveSecret(const std::string& peerKeyStr)
183{
184 namespace t = ndn::security::transform;
Davide Pesavento368341b2019-08-13 23:57:50 -0400185
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700186 OBufferStream os;
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800187 t::bufferSource(peerKeyStr) >> t::base64Decode(false) >> t::streamSink(os);
Davide Pesavento368341b2019-08-13 23:57:50 -0400188 auto result = os.buf();
189
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190 return this->deriveSecret(result->data(), result->size());
191}
192
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700193void
Zhiyi Zhang97bedb82020-10-10 11:11:35 -0700194hmac_sha256(const uint8_t* data, const unsigned data_length,
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700195 const uint8_t* key, const unsigned key_length,
196 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400197{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700198 auto ret = HMAC(EVP_sha256(), key, key_length, (unsigned char*)data, data_length,
199 (unsigned char*)result, nullptr);
200 if (ret == nullptr) {
201 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Error computing HMAC when calling HMAC()"));
202 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700203}
204
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700205int
Zhiyi Zhang97bedb82020-10-10 11:11:35 -0700206hkdf(const uint8_t* secret, int secret_len, const uint8_t* salt,
207 int salt_len, uint8_t* output, int output_len,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700208 const uint8_t* info, int info_len)
209{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700210 EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
211 if (EVP_PKEY_derive_init(pctx) <= 0) {
212 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700213 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700214 if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
215 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700216 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700217 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) {
218 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700219 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700220 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) {
221 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()."));
222 }
223 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) {
224 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()."));
225 }
226 size_t outLen = output_len;
227 if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
228 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: HKDF: Cannot derive result when calling EVP_PKEY_derive()."));
229 }
230 return (int)outLen;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700231}
232
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700233int
234aes_gcm_128_encrypt(const uint8_t* plaintext, size_t plaintext_len, const uint8_t* associated, size_t associated_len,
235 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
236{
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700237 EVP_CIPHER_CTX* ctx;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700238 int len;
239 int ciphertext_len;
240
241 // Create and initialise the context
242 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700243 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700244 }
245
246 // Initialise the encryption operation.
247 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700248 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot initialise the encryption operation when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700249 }
250
251 // Set IV length if default 12 bytes (96 bits) is not appropriate
252 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700253 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700254 }
255
256 // Initialise key and IV
257 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700258 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700259 }
260
261 // Provide any AAD data. This can be called zero or more times as required
262 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700263 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700264 }
265
266 // Provide the message to be encrypted, and obtain the encrypted output.
267 // EVP_EncryptUpdate can be called multiple times if necessary
268 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700269 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot encrypt when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700270 }
271 ciphertext_len = len;
272
273 // Finalise the encryption. Normally ciphertext bytes may be written at
274 // this stage, but this does not occur in GCM mode
275 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700276 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700277 }
278 ciphertext_len += len;
279
280 // Get the tag
281 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700282 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700283 }
284
285 // Clean up
286 EVP_CIPHER_CTX_free(ctx);
287 return ciphertext_len;
288}
289
290int
291aes_gcm_128_decrypt(const uint8_t* ciphertext, size_t ciphertext_len, const uint8_t* associated, size_t associated_len,
292 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
293{
294 EVP_CIPHER_CTX* ctx;
295 int len;
296 int plaintext_len;
297 int ret;
298
299 // Create and initialise the context
300 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700301 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700302 }
303
304 // Initialise the decryption operation.
305 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700306 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700307 }
308
309 // Set IV length. Not necessary if this is 12 bytes (96 bits)
310 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700311 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700312 }
313
314 // Initialise key and IV
315 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700316 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700317 }
318
319 // Provide any AAD data. This can be called zero or more times as required
320 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700321 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700322 }
323
324 // Provide the message to be decrypted, and obtain the plaintext output.
325 // EVP_DecryptUpdate can be called multiple times if necessary
326 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700327 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot decrypt when calling EVP_DecryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700328 }
329 plaintext_len = len;
330
331 // Set expected tag value. Works in OpenSSL 1.0.1d and later
332 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag)) {
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700333 NDN_THROW(std::runtime_error("Error in CRYPTO SUPPORT: Cannot set tag value when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700334 }
335
336 // Finalise the decryption. A positive return value indicates success,
337 // anything else is a failure - the plaintext is not trustworthy.
338 ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
339
340 // Clean up
341 EVP_CIPHER_CTX_free(ctx);
342
343 if (ret > 0) {
344 // Success
345 plaintext_len += len;
346 return plaintext_len;
347 }
348 else {
349 // Verify failed
350 return -1;
351 }
352}
353
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700354Block
355encodeBlockWithAesGcm128(uint32_t tlv_type, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
356 const uint8_t* associatedData, size_t associatedDataSize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700357{
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700358 Buffer iv;
359 iv.resize(12);
360 random::generateSecureBytes(iv.data(), iv.size());
361
362 uint8_t* encryptedPayload = new uint8_t[payloadSize];
363 uint8_t tag[16];
364 size_t encryptedPayloadLen = aes_gcm_128_encrypt(payload, payloadSize, associatedData, associatedDataSize,
365 key, iv.data(), encryptedPayload, tag);
366 auto content = makeEmptyBlock(tlv_type);
367 content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
368 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
369 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload, encryptedPayloadLen));
370 content.encode();
371 delete[] encryptedPayload;
372 return content;
373}
374
375Buffer
376decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
377{
378 block.parse();
379 Buffer result;
380 result.resize(block.get(tlv::EncryptedPayload).value_size());
381 int resultLen = aes_gcm_128_decrypt(block.get(tlv::EncryptedPayload).value(),
382 block.get(tlv::EncryptedPayload).value_size(),
383 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
384 key, block.get(tlv::InitializationVector).value(), result.data());
385 if (resultLen == -1 || resultLen != (int)block.get(tlv::EncryptedPayload).value_size()) {
386 return Buffer();
387 }
388 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700389}
390
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700391} // namespace ndncert
392} // namespace ndn