blob: ae8679642926e9c607151b0911d04f23f3b2f0f7 [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 Zhang222810b2020-10-16 21:50:35 -070022#include <cmath>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070023#include <openssl/err.h>
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -070024#include <openssl/hmac.h>
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070025#include <openssl/pem.h>
Zhiyi Zhang2b6d80c2020-10-12 12:49:14 -070026#include <openssl/ec.h>
27#include <openssl/evp.h>
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070028#include <openssl/kdf.h>
Davide Pesavento368341b2019-08-13 23:57:50 -040029#include <ndn-cxx/encoding/buffer-stream.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070030#include <ndn-cxx/security/transform/base64-decode.hpp>
31#include <ndn-cxx/security/transform/base64-encode.hpp>
32#include <ndn-cxx/security/transform/buffer-source.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070033#include <ndn-cxx/security/transform/stream-sink.hpp>
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -070034#include <ndn-cxx/util/random.hpp>
Zhiyi Zhang222810b2020-10-16 21:50:35 -070035#include <boost/endian/conversion.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070036
37namespace ndn {
38namespace ndncert {
39
Zhiyi Zhang63123172020-10-12 14:24:44 -070040struct ECDHState::ECDH_CTX
41{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070042 EVP_PKEY_CTX* ctx_params = nullptr;
43 EVP_PKEY_CTX* ctx_keygen = nullptr;
44 EVP_PKEY* privkey = nullptr;
45 EVP_PKEY* peerkey = nullptr;
46 EVP_PKEY* params = nullptr;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070047};
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070048
49ECDHState::ECDHState()
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070050 : m_publicKeyLen(0)
51 , m_sharedSecretLen(0)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070052{
53 OpenSSL_add_all_algorithms();
54 context = std::make_unique<ECDH_CTX>();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070055 auto EC_NID = NID_X9_62_prime256v1;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070056
57 // Create the context for parameter generation
58 if (nullptr == (context->ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr))) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070059 NDN_THROW(std::runtime_error("Could not create context contexts."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070060 }
61
62 // Initialise the parameter generation
63 if (EVP_PKEY_paramgen_init(context->ctx_params) != 1) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070064 NDN_THROW(std::runtime_error("Could not initialize parameter generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065 }
66
67 // We're going to use the ANSI X9.62 Prime 256v1 curve
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070068 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(context->ctx_params, EC_NID)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070069 NDN_THROW(std::runtime_error("Likely unknown elliptical curve ID specified."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070070 }
71
72 // Create the parameter object params
73 if (!EVP_PKEY_paramgen(context->ctx_params, &context->params)) {
74 // the generated key is written to context->params
Zhiyi Zhang5c059452020-10-16 17:43:31 -070075 NDN_THROW(std::runtime_error("Could not create parameter object parameters."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070076 }
77
78 // Create the context for the key generation
79 if (nullptr == (context->ctx_keygen = EVP_PKEY_CTX_new(context->params, nullptr))) {
80 //The EVP_PKEY_CTX_new() function allocates public key algorithm context using
81 //the algorithm specified in pkey and ENGINE e (in this case nullptr).
Zhiyi Zhang5c059452020-10-16 17:43:31 -070082 NDN_THROW(std::runtime_error("Could not create the context for the key generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070083 }
84
85 // initializes a public key algorithm context
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070086 if (1 != EVP_PKEY_keygen_init(context->ctx_keygen)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -070087 NDN_THROW(std::runtime_error("Could not init context for key generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070088 }
89 if (1 != EVP_PKEY_keygen(context->ctx_keygen, &context->privkey)) {
90 //performs a key generation operation, the generated key is written to context->privkey.
Zhiyi Zhang5c059452020-10-16 17:43:31 -070091 NDN_THROW(std::runtime_error("Could not generate DHE keys in final step"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070092 }
93}
94
95ECDHState::~ECDHState()
96{
97 // Contexts
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070098 if (context->ctx_params != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070099 EVP_PKEY_CTX_free(context->ctx_params);
100 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700101 if (context->ctx_keygen != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700102 EVP_PKEY_CTX_free(context->ctx_keygen);
103 }
104
105 // Keys
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700106 if (context->privkey != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700107 EVP_PKEY_free(context->privkey);
108 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700109 if (context->peerkey != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700110 EVP_PKEY_free(context->peerkey);
111 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700112 if (context->params != nullptr) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700113 EVP_PKEY_free(context->params);
114 }
115}
116
117uint8_t*
118ECDHState::getRawSelfPubKey()
119{
120 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
121
122 if (privECKey == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700123 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 }
125
126 auto ecPoint = EC_KEY_get0_public_key(privECKey);
127 const EC_GROUP* group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700128 m_publicKeyLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_COMPRESSED,
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700129 m_publicKey, 256, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700130 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700131 if (m_publicKeyLen == 0) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700132 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 -0700133 }
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700134 return m_publicKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700135}
136
137std::string
138ECDHState::getBase64PubKey()
139{
Davide Pesavento368341b2019-08-13 23:57:50 -0400140 namespace t = ndn::security::transform;
141
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700142 if (m_publicKeyLen == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700143 this->getRawSelfPubKey();
144 }
Davide Pesavento368341b2019-08-13 23:57:50 -0400145 std::ostringstream os;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700146 t::bufferSource(m_publicKey, m_publicKeyLen) >> t::base64Encode(false) >> t::streamSink(os);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700147 return os.str();
148}
149
150uint8_t*
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700151ECDHState::deriveSecret(const uint8_t* peerkey, size_t peerKeySize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700152{
153 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
154
155 if (privECKey == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700156 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700157 }
158
159 auto group = EC_KEY_get0_group(privECKey);
160 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800161 int result = EC_POINT_oct2point(group, peerPoint, peerkey, peerKeySize, nullptr);
162 if (result == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700163 EC_POINT_free(peerPoint);
164 EC_KEY_free(privECKey);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700165 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 -0800166 }
167
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700168 result = ECDH_compute_key(m_sharedSecret, 256, peerPoint, privECKey, nullptr);
169 if (result == -1) {
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800170 EC_POINT_free(peerPoint);
171 EC_KEY_free(privECKey);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700172 NDN_THROW(std::runtime_error("Cannot generate ECDH secret when calling ECDH_compute_key()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700173 }
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700174 m_sharedSecretLen = static_cast<size_t>(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700175 EC_POINT_free(peerPoint);
176 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700177 return m_sharedSecret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700178}
179
180uint8_t*
181ECDHState::deriveSecret(const std::string& peerKeyStr)
182{
183 namespace t = ndn::security::transform;
Davide Pesavento368341b2019-08-13 23:57:50 -0400184
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700185 OBufferStream os;
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800186 t::bufferSource(peerKeyStr) >> t::base64Decode(false) >> t::streamSink(os);
Davide Pesavento368341b2019-08-13 23:57:50 -0400187 auto result = os.buf();
188
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700189 return this->deriveSecret(result->data(), result->size());
190}
191
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700192void
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700193hmac_sha256(const uint8_t* data, size_t data_length,
194 const uint8_t* key, size_t key_length,
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700195 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400196{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700197 auto ret = HMAC(EVP_sha256(), key, key_length, (unsigned char*)data, data_length,
198 (unsigned char*)result, nullptr);
199 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700200 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700201 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700202}
203
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700204int
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700205hkdf(const uint8_t* secret, size_t secret_len, const uint8_t* salt,
206 size_t salt_len, uint8_t* output, size_t output_len,
207 const uint8_t* info, size_t info_len)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700208{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700209 EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
210 if (EVP_PKEY_derive_init(pctx) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700211 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700212 NDN_THROW(std::runtime_error("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) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700215 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700216 NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700217 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700218 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700219 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700220 NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700221 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700222 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700223 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700224 NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700225 }
226 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700227 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700228 NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700229 }
230 size_t outLen = output_len;
231 if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700232 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700233 NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700234 }
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700235 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700236 return (int)outLen;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700237}
238
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700239int
240aes_gcm_128_encrypt(const uint8_t* plaintext, size_t plaintext_len, const uint8_t* associated, size_t associated_len,
241 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
242{
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700243 EVP_CIPHER_CTX* ctx;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700244 int len;
245 int ciphertext_len;
246
247 // Create and initialise the context
248 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700249 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700250 }
251
252 // Initialise the encryption operation.
253 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700254 NDN_THROW(std::runtime_error("Cannot initialise the encryption operation when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700255 }
256
257 // Set IV length if default 12 bytes (96 bits) is not appropriate
258 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700259 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700260 }
261
262 // Initialise key and IV
263 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700264 NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700265 }
266
267 // Provide any AAD data. This can be called zero or more times as required
268 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700269 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700270 }
271
272 // Provide the message to be encrypted, and obtain the encrypted output.
273 // EVP_EncryptUpdate can be called multiple times if necessary
274 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700275 NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700276 }
277 ciphertext_len = len;
278
279 // Finalise the encryption. Normally ciphertext bytes may be written at
280 // this stage, but this does not occur in GCM mode
281 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700282 NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700283 }
284 ciphertext_len += len;
285
286 // Get the tag
287 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700288 NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700289 }
290
291 // Clean up
292 EVP_CIPHER_CTX_free(ctx);
293 return ciphertext_len;
294}
295
296int
297aes_gcm_128_decrypt(const uint8_t* ciphertext, size_t ciphertext_len, const uint8_t* associated, size_t associated_len,
298 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
299{
300 EVP_CIPHER_CTX* ctx;
301 int len;
302 int plaintext_len;
303 int ret;
304
305 // Create and initialise the context
306 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700307 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700308 }
309
310 // Initialise the decryption operation.
311 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700312 NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700313 }
314
315 // Set IV length. Not necessary if this is 12 bytes (96 bits)
316 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700317 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700318 }
319
320 // Initialise key and IV
321 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700322 NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700323 }
324
325 // Provide any AAD data. This can be called zero or more times as required
326 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700327 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700328 }
329
330 // Provide the message to be decrypted, and obtain the plaintext output.
331 // EVP_DecryptUpdate can be called multiple times if necessary
332 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700333 NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700334 }
335 plaintext_len = len;
336
337 // Set expected tag value. Works in OpenSSL 1.0.1d and later
338 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag)) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700339 NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700340 }
341
342 // Finalise the decryption. A positive return value indicates success,
343 // anything else is a failure - the plaintext is not trustworthy.
344 ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
345
346 // Clean up
347 EVP_CIPHER_CTX_free(ctx);
348
349 if (ret > 0) {
350 // Success
351 plaintext_len += len;
352 return plaintext_len;
353 }
354 else {
355 // Verify failed
356 return -1;
357 }
358}
359
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700360Block
361encodeBlockWithAesGcm128(uint32_t tlv_type, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700362 const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700363{
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700364 Buffer iv(12);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700365 random::generateSecureBytes(iv.data(), iv.size());
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700366 if (tlv_type == ndn::tlv::ApplicationParameters) {
367 // requester
368 iv[0] &= ~(1UL << 7);
369 }
370 else {
371 // CA
372 iv[0] |= 1UL << 7;
373 }
374 uint32_t temp = counter;
375 boost::endian::native_to_big_inplace(temp);
376 std::memcpy(&iv[8], reinterpret_cast<const uint8_t*>(&temp), 4);
377 counter += std::ceil(payloadSize / 8);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700378
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700379 Buffer encryptedPayload(payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700380 uint8_t tag[16];
381 size_t encryptedPayloadLen = aes_gcm_128_encrypt(payload, payloadSize, associatedData, associatedDataSize,
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700382 key, iv.data(), encryptedPayload.data(), tag);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700383 auto content = makeEmptyBlock(tlv_type);
384 content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
385 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700386 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700387 content.encode();
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700388 return content;
389}
390
391Buffer
392decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
393{
394 block.parse();
Zhiyi Zhangcace6ab2020-10-16 21:51:44 -0700395 Buffer result(block.get(tlv::EncryptedPayload).value_size());
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700396 int resultLen = aes_gcm_128_decrypt(block.get(tlv::EncryptedPayload).value(),
397 block.get(tlv::EncryptedPayload).value_size(),
398 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
399 key, block.get(tlv::InitializationVector).value(), result.data());
400 if (resultLen == -1 || resultLen != (int)block.get(tlv::EncryptedPayload).value_size()) {
401 return Buffer();
402 }
403 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700404}
405
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700406} // namespace ndncert
407} // namespace ndn