blob: d0c2e55d41e543df1a05805f5695df520405f7ad [file] [log] [blame]
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoa34d6de2021-11-18 22:16:04 -05002/*
3 * Copyright (c) 2017-2021, 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
Zhiyi Zhangb041d442020-10-22 21:57:11 -070021#include "detail/crypto-helpers.hpp"
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070022
23#include <boost/endian/conversion.hpp>
Davide Pesaventoa34d6de2021-11-18 22:16:04 -050024
Davide Pesavento368341b2019-08-13 23:57:50 -040025#include <ndn-cxx/encoding/buffer-stream.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070026#include <ndn-cxx/security/transform/base64-decode.hpp>
27#include <ndn-cxx/security/transform/base64-encode.hpp>
28#include <ndn-cxx/security/transform/buffer-source.hpp>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070029#include <ndn-cxx/security/transform/stream-sink.hpp>
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -070030#include <ndn-cxx/util/random.hpp>
Davide Pesaventoa34d6de2021-11-18 22:16:04 -050031
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070032#include <openssl/ec.h>
33#include <openssl/err.h>
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070034#include <openssl/hmac.h>
35#include <openssl/kdf.h>
36#include <openssl/pem.h>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070037
Davide Pesaventoa34d6de2021-11-18 22:16:04 -050038#include <cstring>
39
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070040namespace ndncert {
41
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070042ECDHState::ECDHState()
43{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070044 auto EC_NID = NID_X9_62_prime256v1;
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070045 // params context
46 EVP_PKEY_CTX* ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
Zhiyi Zhang6701f3c2021-02-18 17:38:42 -080047 EVP_PKEY_paramgen_init(ctx_params);
48 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_params, EC_NID);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070049 // generate params
50 EVP_PKEY* params = nullptr;
Zhiyi Zhang6701f3c2021-02-18 17:38:42 -080051 EVP_PKEY_paramgen(ctx_params, &params);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070052 // key generation context
Zhiyi Zhangf908f662020-10-21 12:55:10 -070053 EVP_PKEY_CTX* ctx_keygen = EVP_PKEY_CTX_new(params, nullptr);
Zhiyi Zhang6701f3c2021-02-18 17:38:42 -080054 EVP_PKEY_keygen_init(ctx_keygen);
55 auto resultCode = EVP_PKEY_keygen(ctx_keygen, &m_privkey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070056 EVP_PKEY_CTX_free(ctx_keygen);
57 EVP_PKEY_free(params);
58 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang6701f3c2021-02-18 17:38:42 -080059 if (resultCode <= 0) {
60 NDN_THROW(std::runtime_error("Error in initiating ECDH"));
61 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070062}
63
64ECDHState::~ECDHState()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070065{
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070066 if (m_privkey != nullptr) {
67 EVP_PKEY_free(m_privkey);
68 }
69}
70
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -080071const std::vector <uint8_t>&
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070072ECDHState::getSelfPubKey()
73{
74 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 auto ecPoint = EC_KEY_get0_public_key(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070076 auto group = EC_KEY_get0_group(privECKey);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070077 auto requiredBufLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070078 m_pubKey.resize(requiredBufLen);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -080079 auto resultCode = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED,
80 m_pubKey.data(), requiredBufLen, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070081 EC_KEY_free(privECKey);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -080082 if (resultCode == 0) {
83 NDN_THROW(std::runtime_error("Error in getting EC Public Key in the format of octet string"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070084 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070085 return m_pubKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070086}
87
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -080088const std::vector<uint8_t>&
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -080089ECDHState::deriveSecret(const std::vector <uint8_t>& peerKey)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070090{
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070091 // prepare self private key
92 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070093 auto group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070094 EC_KEY_free(privECKey);
95 // prepare the peer public key
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070096 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -080097 EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -070098 EC_KEY* ecPeerkey = EC_KEY_new();
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -080099 EC_KEY_set_group(ecPeerkey, group);
100 EC_KEY_set_public_key(ecPeerkey, peerPoint);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700101 EVP_PKEY* evpPeerkey = EVP_PKEY_new();
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800102 EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700103 EC_KEY_free(ecPeerkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700104 EC_POINT_free(peerPoint);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700105 // ECDH context
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700106 EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700107 // Initialize
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800108 EVP_PKEY_derive_init(ctx);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700109 // Provide the peer public key
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800110 EVP_PKEY_derive_set_peer(ctx, evpPeerkey);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700111 // Determine buffer length for shared secret
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700112 size_t secretLen = 0;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800113 EVP_PKEY_derive(ctx, nullptr, &secretLen);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700114 m_secret.resize(secretLen);
115 // Derive the shared secret
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800116 auto resultCode = EVP_PKEY_derive(ctx, m_secret.data(), &secretLen);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700117 EVP_PKEY_CTX_free(ctx);
118 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800119 if (resultCode == 0) {
120 NDN_THROW(std::runtime_error("Error when calling ECDH"));
121 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700122 return m_secret;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800123
124// // prepare self private key
125// auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
126// if (privECKey == nullptr) {
127// NDN_THROW(std::runtime_error("Cannot not get key when calling EVP_PKEY_get1_EC_KEY()"));
128// }
129// auto group = EC_KEY_get0_group(privECKey);
130// EC_KEY_free(privECKey);
131// // prepare the peer public key
132// auto peerPoint = EC_POINT_new(group);
133// if (peerPoint == nullptr) {
134// NDN_THROW(std::runtime_error("Cannot create the EC_POINT for peer key when calling EC_POINT_new()"));
135// }
136// if (EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr) == 0) {
137// EC_POINT_free(peerPoint);
138// NDN_THROW(std::runtime_error("Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()"));
139// }
140// EC_KEY* ecPeerkey = EC_KEY_new();
141// if (ecPeerkey == nullptr) {
142// EC_POINT_free(peerPoint);
143// NDN_THROW(std::runtime_error("Cannot create EC_KEY for peer key when calling EC_KEY_new()"));
144// }
145// if (EC_KEY_set_group(ecPeerkey, group) != 1) {
146// EC_POINT_free(peerPoint);
147// NDN_THROW(std::runtime_error("Cannot set group for peer key's EC_KEY when calling EC_KEY_set_group()"));
148// }
149// if (EC_KEY_set_public_key(ecPeerkey, peerPoint) == 0) {
150// EC_KEY_free(ecPeerkey);
151// EC_POINT_free(peerPoint);
152// NDN_THROW(
153// std::runtime_error("Cannot initialize peer EC_KEY with the EC_POINT when calling EC_KEY_set_public_key()"));
154// }
155// EVP_PKEY* evpPeerkey = EVP_PKEY_new();
156// if (EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey) == 0) {
157// EC_KEY_free(ecPeerkey);
158// EC_POINT_free(peerPoint);
159// NDN_THROW(std::runtime_error("Cannot create EVP_PKEY for peer key when calling EVP_PKEY_new()"));
160// }
161// EC_KEY_free(ecPeerkey);
162// EC_POINT_free(peerPoint);
163// // ECDH context
164// EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
165// if (ctx == nullptr) {
166// EVP_PKEY_free(evpPeerkey);
167// NDN_THROW(std::runtime_error("Cannot create context for ECDH when calling EVP_PKEY_CTX_new()"));
168// }
169// // Initialize
170// if (1 != EVP_PKEY_derive_init(ctx)) {
171// EVP_PKEY_CTX_free(ctx);
172// EVP_PKEY_free(evpPeerkey);
173// NDN_THROW(std::runtime_error("Cannot initialize context for ECDH when calling EVP_PKEY_derive_init()"));
174// }
175// // Provide the peer public key
176// if (1 != EVP_PKEY_derive_set_peer(ctx, evpPeerkey)) {
177// EVP_PKEY_CTX_free(ctx);
178// EVP_PKEY_free(evpPeerkey);
179// NDN_THROW(std::runtime_error("Cannot set peer key for ECDH when calling EVP_PKEY_derive_set_peer()"));
180// }
181// // Determine buffer length for shared secret
182// size_t secretLen = 0;
183// if (1 != EVP_PKEY_derive(ctx, nullptr, &secretLen)) {
184// EVP_PKEY_CTX_free(ctx);
185// EVP_PKEY_free(evpPeerkey);
186// NDN_THROW(std::runtime_error("Cannot determine the needed buffer length when calling EVP_PKEY_derive()"));
187// }
188// m_secret.resize(secretLen);
189// // Derive the shared secret
190// if (1 != (EVP_PKEY_derive(ctx, m_secret.data(), &secretLen))) {
191// EVP_PKEY_CTX_free(ctx);
192// EVP_PKEY_free(evpPeerkey);
193// NDN_THROW(std::runtime_error("Cannot derive ECDH secret when calling EVP_PKEY_derive()"));
194// }
195// EVP_PKEY_CTX_free(ctx);
196// EVP_PKEY_free(evpPeerkey);
197// return m_secret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700198}
199
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700200void
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700201hmacSha256(const uint8_t* data, size_t dataLen,
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700202 const uint8_t* key, size_t keyLen,
203 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400204{
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800205 auto ret = HMAC(EVP_sha256(), key, keyLen, data, dataLen, result, nullptr);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700206 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700207 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700208 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700209}
210
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700211size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700212hkdf(const uint8_t* secret, size_t secretLen, const uint8_t* salt,
213 size_t saltLen, uint8_t* output, size_t outputLen,
214 const uint8_t* info, size_t infoLen)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700215{
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700216 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800217 EVP_PKEY_derive_init(pctx);
218 EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
219 EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen);
220 EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen);
221 EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700222 size_t outLen = outputLen;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800223 auto resultCode = EVP_PKEY_derive(pctx, output, &outLen);
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700224 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800225 if (resultCode == 0) {
226 NDN_THROW(std::runtime_error("Error when calling HKDF"));
227 }
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700228 return outLen;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800229
230// if (EVP_PKEY_derive_init(pctx) <= 0) {
231// EVP_PKEY_CTX_free(pctx);
232// NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()"));
233// }
234// if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
235// EVP_PKEY_CTX_free(pctx);
236// NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()"));
237// }
238// if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen) <= 0) {
239// EVP_PKEY_CTX_free(pctx);
240// NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()"));
241// }
242// if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen) <= 0) {
243// EVP_PKEY_CTX_free(pctx);
244// NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()"));
245// }
246// if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen) <= 0) {
247// EVP_PKEY_CTX_free(pctx);
248// NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()"));
249// }
250// size_t outLen = outputLen;
251// if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
252// EVP_PKEY_CTX_free(pctx);
253// NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()"));
254// }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700255}
256
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700257size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700258aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700259 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700260{
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800261 int len = 0;
262 size_t ciphertextLen = 0;
263 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
264 EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
265 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr);
266 EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv);
267 EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen);
268 EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700269 ciphertextLen = len;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800270 EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
271 auto resultCode = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700272 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800273 if (resultCode == 0) {
274 NDN_THROW(std::runtime_error("Error in encryption plaintext with AES GCM"));
275 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700276 return ciphertextLen;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800277
278// if (!(ctx = EVP_CIPHER_CTX_new())) {
279// NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
280// }
281// if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
282// EVP_CIPHER_CTX_free(ctx);
283// NDN_THROW(std::runtime_error("Cannot initialize the encryption operation when calling EVP_EncryptInit_ex()"));
284// }
285// if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
286// EVP_CIPHER_CTX_free(ctx);
287// NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
288// }
289// if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
290// EVP_CIPHER_CTX_free(ctx);
291// NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
292// }
293// if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
294// EVP_CIPHER_CTX_free(ctx);
295// NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
296// }
297// if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen)) {
298// EVP_CIPHER_CTX_free(ctx);
299// NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
300// }
301// ciphertextLen = len;
302// if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
303// EVP_CIPHER_CTX_free(ctx);
304// NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
305// }
306// ciphertextLen += len;
307// if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
308// EVP_CIPHER_CTX_free(ctx);
309// NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
310// }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700311}
312
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700313size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700314aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700315 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700316{
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800317 int len = 0;
318 size_t plaintextLen = 0;
319 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
320 EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
321 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr);
322 EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv);
323 EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen);
324 EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700325 plaintextLen = len;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800326 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)));
327 auto resultCode = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
328 plaintextLen += len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700329 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800330 if (resultCode == 0) {
331 NDN_THROW(std::runtime_error("Error in decrypting ciphertext with AES GCM"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700332 }
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800333 return plaintextLen;
334
335// if (!(ctx = EVP_CIPHER_CTX_new())) {
336// NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
337// }
338// if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
339// EVP_CIPHER_CTX_free(ctx);
340// NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
341// }
342// if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
343// EVP_CIPHER_CTX_free(ctx);
344// NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
345// }
346// if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
347// EVP_CIPHER_CTX_free(ctx);
348// NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
349// }
350// if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
351// EVP_CIPHER_CTX_free(ctx);
352// NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
353// }
354// if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen)) {
355// EVP_CIPHER_CTX_free(ctx);
356// NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
357// }
358// plaintextLen = len;
359// if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)))) {
360// EVP_CIPHER_CTX_free(ctx);
361// NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl()"));
362// }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700363}
364
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500365#ifndef NDNCERT_HAVE_TESTS
366static
367#endif
368uint32_t
369loadBigU32(const uint8_t* src) noexcept
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800370{
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500371#if BOOST_VERSION >= 107100
372 return boost::endian::endian_load<uint32_t, 4, boost::endian::order::big>(src);
373#else
374 uint32_t dest;
375 std::memcpy(reinterpret_cast<uint8_t*>(&dest), src, sizeof(dest));
376 return boost::endian::big_to_native(dest);
377#endif
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800378}
379
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500380#ifndef NDNCERT_HAVE_TESTS
381static
382#endif
383void
384storeBigU32(uint8_t* dest, uint32_t src) noexcept
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800385{
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500386#if BOOST_VERSION >= 107100
387 boost::endian::endian_store<uint32_t, 4, boost::endian::order::big>(dest, src);
388#else
389 boost::endian::native_to_big_inplace(src);
390 std::memcpy(dest, reinterpret_cast<const uint8_t*>(&src), sizeof(src));
391#endif
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800392}
393
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800394static void
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500395updateIv(std::vector<uint8_t>& iv, size_t payloadSize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700396{
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500397 BOOST_ASSERT(iv.size() >= 12);
398 uint32_t counter = loadBigU32(&iv[8]);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700399 uint32_t increment = (payloadSize + 15) / 16;
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800400 if (std::numeric_limits<uint32_t>::max() - counter <= increment) {
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700401 NDN_THROW(std::runtime_error("Error incrementing the AES block counter: "
Zhiyi Zhange11a2d62020-10-22 09:59:12 -0700402 "too many blocks have been encrypted for the same request instance"));
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700403 }
404 else {
405 counter += increment;
406 }
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800407 storeBigU32(&iv[8], counter);
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800408}
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700409
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800410Block
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800411encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key,
412 const uint8_t* payload, size_t payloadSize,
413 const uint8_t* associatedData, size_t associatedDataSize,
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500414 std::vector<uint8_t>& encryptionIv)
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800415{
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700416 // The spec of AES encrypted payload TLV used in NDNCERT:
417 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
Davide Pesavento0dc02012021-11-23 22:55:03 -0500418 ndn::Buffer encryptedPayload(payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700419 uint8_t tag[16];
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800420 if (encryptionIv.empty()) {
421 encryptionIv.resize(12, 0);
Davide Pesavento0dc02012021-11-23 22:55:03 -0500422 ndn::random::generateSecureBytes(encryptionIv.data(), 8);
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800423 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700424 size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800425 key, encryptionIv.data(), encryptedPayload.data(), tag);
tylerliu1f480be2020-11-10 13:02:53 -0800426 Block content(tlvType);
Davide Pesavento0dc02012021-11-23 22:55:03 -0500427 content.push_back(ndn::makeBinaryBlock(tlv::InitializationVector, encryptionIv.data(), encryptionIv.size()));
428 content.push_back(ndn::makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
429 content.push_back(ndn::makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700430 content.encode();
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800431 // update IV's counter
432 updateIv(encryptionIv, payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700433 return content;
434}
435
Davide Pesavento0dc02012021-11-23 22:55:03 -0500436ndn::Buffer
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800437decodeBlockWithAesGcm128(const Block& block, const uint8_t* key,
438 const uint8_t* associatedData, size_t associatedDataSize,
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500439 std::vector<uint8_t>& decryptionIv, const std::vector<uint8_t>& encryptionIv)
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700440{
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700441 // The spec of AES encrypted payload TLV used in NDNCERT:
442 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700443 block.parse();
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700444 const auto& encryptedPayloadBlock = block.get(tlv::EncryptedPayload);
Davide Pesavento0dc02012021-11-23 22:55:03 -0500445 ndn::Buffer result(encryptedPayloadBlock.value_size());
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800446 if (block.get(tlv::InitializationVector).value_size() != 12 ||
447 block.get(tlv::AuthenticationTag).value_size() != 16) {
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800448 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800449 "The observed IV or Authentication Tag is of an unexpected size."));
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800450 }
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500451 std::vector<uint8_t> observedDecryptionIv(block.get(tlv::InitializationVector).value(),
452 block.get(tlv::InitializationVector).value() + 12);
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800453 if (!encryptionIv.empty()) {
454 if (std::equal(observedDecryptionIv.begin(), observedDecryptionIv.begin() + 8, encryptionIv.begin())) {
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800455 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800456 "The observed IV's the random component should be different from ours."));
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800457 }
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800458 }
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800459 if (!decryptionIv.empty()) {
Davide Pesaventoa34d6de2021-11-18 22:16:04 -0500460 if (loadBigU32(&observedDecryptionIv[8]) < loadBigU32(&decryptionIv[8]) ||
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800461 !std::equal(observedDecryptionIv.begin(), observedDecryptionIv.begin() + 8, decryptionIv.begin())) {
462 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
463 "The observed IV's counter should be monotonically increasing "
464 "and the random component must be the same from the requester."));
465 }
466 }
467 decryptionIv = observedDecryptionIv;
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700468 auto resultLen = aesGcm128Decrypt(encryptedPayloadBlock.value(), encryptedPayloadBlock.value_size(),
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700469 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800470 key, decryptionIv.data(), result.data());
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700471 if (resultLen != encryptedPayloadBlock.value_size()) {
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700472 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800473 "Decrypted payload is of an unexpected size."));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700474 }
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800475 updateIv(decryptionIv, resultLen);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700476 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700477}
478
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700479} // namespace ndncert