blob: e719d89ccb3ba12131ceb601fdd7505f0fc40a30 [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
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>
Zhiyi Zhangaeab4972020-10-22 22:20:40 -070024#include <cstring>
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>
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070031#include <openssl/ec.h>
32#include <openssl/err.h>
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070033#include <openssl/hmac.h>
34#include <openssl/kdf.h>
35#include <openssl/pem.h>
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070036
37namespace ndn {
38namespace ndncert {
39
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070040ECDHState::ECDHState()
41{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070042 auto EC_NID = NID_X9_62_prime256v1;
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070043 // params context
44 EVP_PKEY_CTX* ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
45 if (ctx_params == nullptr) {
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070046 NDN_THROW(std::runtime_error("Could not create context"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070047 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070048 if (EVP_PKEY_paramgen_init(ctx_params) != 1) {
49 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070050 NDN_THROW(std::runtime_error("Could not initialize parameter generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070051 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070052 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_params, EC_NID)) {
53 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070054 NDN_THROW(std::runtime_error("Likely unknown elliptical curve ID specified"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070055 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070056 // generate params
57 EVP_PKEY* params = nullptr;
58 if (!EVP_PKEY_paramgen(ctx_params, &params)) {
59 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070060 NDN_THROW(std::runtime_error("Could not create parameter object parameters"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070061 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070062 // key generation context
Zhiyi Zhangf908f662020-10-21 12:55:10 -070063 EVP_PKEY_CTX* ctx_keygen = EVP_PKEY_CTX_new(params, nullptr);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070064 if (ctx_keygen == nullptr) {
65 EVP_PKEY_free(params);
66 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang5c059452020-10-16 17:43:31 -070067 NDN_THROW(std::runtime_error("Could not create the context for the key generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070068 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070069 if (1 != EVP_PKEY_keygen_init(ctx_keygen)) {
70 EVP_PKEY_CTX_free(ctx_keygen);
71 EVP_PKEY_free(params);
72 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070073 NDN_THROW(std::runtime_error("Could not init context for key generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070074 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070075 if (1 != EVP_PKEY_keygen(ctx_keygen, &m_privkey)) {
76 EVP_PKEY_CTX_free(ctx_keygen);
77 EVP_PKEY_free(params);
78 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhang5c059452020-10-16 17:43:31 -070079 NDN_THROW(std::runtime_error("Could not generate DHE keys in final step"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070080 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070081 EVP_PKEY_CTX_free(ctx_keygen);
82 EVP_PKEY_free(params);
83 EVP_PKEY_CTX_free(ctx_params);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070084}
85
86ECDHState::~ECDHState()
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070087{
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070088 if (m_privkey != nullptr) {
89 EVP_PKEY_free(m_privkey);
90 }
91}
92
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -080093const std::vector <uint8_t>&
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070094ECDHState::getSelfPubKey()
95{
96 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070097 auto ecPoint = EC_KEY_get0_public_key(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -070098 auto group = EC_KEY_get0_group(privECKey);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070099 auto requiredBufLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700100 m_pubKey.resize(requiredBufLen);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800101 auto resultCode = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED,
102 m_pubKey.data(), requiredBufLen, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700103 EC_KEY_free(privECKey);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800104 if (resultCode == 0) {
105 NDN_THROW(std::runtime_error("Error in getting EC Public Key in the format of octet string"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700106 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700107 return m_pubKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700108}
109
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800110const std::vector<uint8_t>&
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800111ECDHState::deriveSecret(const std::vector <uint8_t>& peerKey)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700112{
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700113 // prepare self private key
114 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115 auto group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700116 EC_KEY_free(privECKey);
117 // prepare the peer public key
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700118 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800119 EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700120 EC_KEY* ecPeerkey = EC_KEY_new();
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800121 EC_KEY_set_group(ecPeerkey, group);
122 EC_KEY_set_public_key(ecPeerkey, peerPoint);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700123 EVP_PKEY* evpPeerkey = EVP_PKEY_new();
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800124 EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700125 EC_KEY_free(ecPeerkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700126 EC_POINT_free(peerPoint);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700127 // ECDH context
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700128 EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700129 // Initialize
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800130 EVP_PKEY_derive_init(ctx);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700131 // Provide the peer public key
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800132 EVP_PKEY_derive_set_peer(ctx, evpPeerkey);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700133 // Determine buffer length for shared secret
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700134 size_t secretLen = 0;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800135 EVP_PKEY_derive(ctx, nullptr, &secretLen);
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700136 m_secret.resize(secretLen);
137 // Derive the shared secret
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800138 auto resultCode = EVP_PKEY_derive(ctx, m_secret.data(), &secretLen);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700139 EVP_PKEY_CTX_free(ctx);
140 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800141 if (resultCode == 0) {
142 NDN_THROW(std::runtime_error("Error when calling ECDH"));
143 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700144 return m_secret;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800145
146// // prepare self private key
147// auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
148// if (privECKey == nullptr) {
149// NDN_THROW(std::runtime_error("Cannot not get key when calling EVP_PKEY_get1_EC_KEY()"));
150// }
151// auto group = EC_KEY_get0_group(privECKey);
152// EC_KEY_free(privECKey);
153// // prepare the peer public key
154// auto peerPoint = EC_POINT_new(group);
155// if (peerPoint == nullptr) {
156// NDN_THROW(std::runtime_error("Cannot create the EC_POINT for peer key when calling EC_POINT_new()"));
157// }
158// if (EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr) == 0) {
159// EC_POINT_free(peerPoint);
160// NDN_THROW(std::runtime_error("Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()"));
161// }
162// EC_KEY* ecPeerkey = EC_KEY_new();
163// if (ecPeerkey == nullptr) {
164// EC_POINT_free(peerPoint);
165// NDN_THROW(std::runtime_error("Cannot create EC_KEY for peer key when calling EC_KEY_new()"));
166// }
167// if (EC_KEY_set_group(ecPeerkey, group) != 1) {
168// EC_POINT_free(peerPoint);
169// NDN_THROW(std::runtime_error("Cannot set group for peer key's EC_KEY when calling EC_KEY_set_group()"));
170// }
171// if (EC_KEY_set_public_key(ecPeerkey, peerPoint) == 0) {
172// EC_KEY_free(ecPeerkey);
173// EC_POINT_free(peerPoint);
174// NDN_THROW(
175// std::runtime_error("Cannot initialize peer EC_KEY with the EC_POINT when calling EC_KEY_set_public_key()"));
176// }
177// EVP_PKEY* evpPeerkey = EVP_PKEY_new();
178// if (EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey) == 0) {
179// EC_KEY_free(ecPeerkey);
180// EC_POINT_free(peerPoint);
181// NDN_THROW(std::runtime_error("Cannot create EVP_PKEY for peer key when calling EVP_PKEY_new()"));
182// }
183// EC_KEY_free(ecPeerkey);
184// EC_POINT_free(peerPoint);
185// // ECDH context
186// EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
187// if (ctx == nullptr) {
188// EVP_PKEY_free(evpPeerkey);
189// NDN_THROW(std::runtime_error("Cannot create context for ECDH when calling EVP_PKEY_CTX_new()"));
190// }
191// // Initialize
192// if (1 != EVP_PKEY_derive_init(ctx)) {
193// EVP_PKEY_CTX_free(ctx);
194// EVP_PKEY_free(evpPeerkey);
195// NDN_THROW(std::runtime_error("Cannot initialize context for ECDH when calling EVP_PKEY_derive_init()"));
196// }
197// // Provide the peer public key
198// if (1 != EVP_PKEY_derive_set_peer(ctx, evpPeerkey)) {
199// EVP_PKEY_CTX_free(ctx);
200// EVP_PKEY_free(evpPeerkey);
201// NDN_THROW(std::runtime_error("Cannot set peer key for ECDH when calling EVP_PKEY_derive_set_peer()"));
202// }
203// // Determine buffer length for shared secret
204// size_t secretLen = 0;
205// if (1 != EVP_PKEY_derive(ctx, nullptr, &secretLen)) {
206// EVP_PKEY_CTX_free(ctx);
207// EVP_PKEY_free(evpPeerkey);
208// NDN_THROW(std::runtime_error("Cannot determine the needed buffer length when calling EVP_PKEY_derive()"));
209// }
210// m_secret.resize(secretLen);
211// // Derive the shared secret
212// if (1 != (EVP_PKEY_derive(ctx, m_secret.data(), &secretLen))) {
213// EVP_PKEY_CTX_free(ctx);
214// EVP_PKEY_free(evpPeerkey);
215// NDN_THROW(std::runtime_error("Cannot derive ECDH secret when calling EVP_PKEY_derive()"));
216// }
217// EVP_PKEY_CTX_free(ctx);
218// EVP_PKEY_free(evpPeerkey);
219// return m_secret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700220}
221
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700222void
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700223hmacSha256(const uint8_t* data, size_t dataLen,
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700224 const uint8_t* key, size_t keyLen,
225 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400226{
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800227 auto ret = HMAC(EVP_sha256(), key, keyLen, data, dataLen, result, nullptr);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700228 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700229 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700230 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700231}
232
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700233size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700234hkdf(const uint8_t* secret, size_t secretLen, const uint8_t* salt,
235 size_t saltLen, uint8_t* output, size_t outputLen,
236 const uint8_t* info, size_t infoLen)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700237{
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700238 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800239 EVP_PKEY_derive_init(pctx);
240 EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256());
241 EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen);
242 EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen);
243 EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700244 size_t outLen = outputLen;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800245 auto resultCode = EVP_PKEY_derive(pctx, output, &outLen);
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700246 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800247 if (resultCode == 0) {
248 NDN_THROW(std::runtime_error("Error when calling HKDF"));
249 }
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700250 return outLen;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800251
252// if (EVP_PKEY_derive_init(pctx) <= 0) {
253// EVP_PKEY_CTX_free(pctx);
254// NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()"));
255// }
256// if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
257// EVP_PKEY_CTX_free(pctx);
258// NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()"));
259// }
260// if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen) <= 0) {
261// EVP_PKEY_CTX_free(pctx);
262// NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()"));
263// }
264// if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen) <= 0) {
265// EVP_PKEY_CTX_free(pctx);
266// NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()"));
267// }
268// if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen) <= 0) {
269// EVP_PKEY_CTX_free(pctx);
270// NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()"));
271// }
272// size_t outLen = outputLen;
273// if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
274// EVP_PKEY_CTX_free(pctx);
275// NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()"));
276// }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700277}
278
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700279size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700280aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700281 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700282{
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800283 int len = 0;
284 size_t ciphertextLen = 0;
285 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
286 EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
287 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr);
288 EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv);
289 EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen);
290 EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700291 ciphertextLen = len;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800292 EVP_EncryptFinal_ex(ctx, ciphertext + len, &len);
293 auto resultCode = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700294 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800295 if (resultCode == 0) {
296 NDN_THROW(std::runtime_error("Error in encryption plaintext with AES GCM"));
297 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700298 return ciphertextLen;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800299
300// if (!(ctx = EVP_CIPHER_CTX_new())) {
301// NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
302// }
303// if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
304// EVP_CIPHER_CTX_free(ctx);
305// NDN_THROW(std::runtime_error("Cannot initialize the encryption operation when calling EVP_EncryptInit_ex()"));
306// }
307// if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
308// EVP_CIPHER_CTX_free(ctx);
309// NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
310// }
311// if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
312// EVP_CIPHER_CTX_free(ctx);
313// NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
314// }
315// if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
316// EVP_CIPHER_CTX_free(ctx);
317// NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
318// }
319// if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen)) {
320// EVP_CIPHER_CTX_free(ctx);
321// NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
322// }
323// ciphertextLen = len;
324// if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
325// EVP_CIPHER_CTX_free(ctx);
326// NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
327// }
328// ciphertextLen += len;
329// if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
330// EVP_CIPHER_CTX_free(ctx);
331// NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
332// }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700333}
334
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700335size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700336aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700337 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700338{
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800339 int len = 0;
340 size_t plaintextLen = 0;
341 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
342 EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr);
343 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr);
344 EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv);
345 EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen);
346 EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700347 plaintextLen = len;
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800348 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)));
349 auto resultCode = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
350 plaintextLen += len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700351 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800352 if (resultCode == 0) {
353 NDN_THROW(std::runtime_error("Error in decrypting ciphertext with AES GCM"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700354 }
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800355 return plaintextLen;
356
357// if (!(ctx = EVP_CIPHER_CTX_new())) {
358// NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
359// }
360// if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
361// EVP_CIPHER_CTX_free(ctx);
362// NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
363// }
364// if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
365// EVP_CIPHER_CTX_free(ctx);
366// NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
367// }
368// if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
369// EVP_CIPHER_CTX_free(ctx);
370// NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
371// }
372// if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
373// EVP_CIPHER_CTX_free(ctx);
374// NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
375// }
376// if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen)) {
377// EVP_CIPHER_CTX_free(ctx);
378// NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
379// }
380// plaintextLen = len;
381// if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)))) {
382// EVP_CIPHER_CTX_free(ctx);
383// NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl()"));
384// }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700385}
386
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800387// Can be removed after boost version 1.72, replaced by boost::endian::load_big_u32
388static uint32_t
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800389loadBigU32(const std::vector <uint8_t>& iv, size_t pos)
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800390{
391 uint32_t result = iv[pos] << 24 | iv[pos + 1] << 16 | iv[pos + 2] << 8 | iv[pos + 3];
392 return result;
393}
394
395// Can be removed after boost version 1.72, replaced by boost::endian::store_big_u32
396static void
397storeBigU32(uint8_t* iv, uint32_t counter)
398{
399 uint32_t temp = boost::endian::native_to_big(counter);
400 std::memcpy(iv, reinterpret_cast<const uint8_t*>(&temp), 4);
401 return;
402}
403
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800404static void
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800405updateIv(std::vector <uint8_t>& iv, size_t payloadSize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700406{
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800407 // uint32_t counter = boost::endian::load_big_u32(&iv[8]);
408 uint32_t counter = loadBigU32(iv, 8);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700409 uint32_t increment = (payloadSize + 15) / 16;
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800410 if (std::numeric_limits<uint32_t>::max() - counter <= increment) {
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700411 NDN_THROW(std::runtime_error("Error incrementing the AES block counter: "
Zhiyi Zhange11a2d62020-10-22 09:59:12 -0700412 "too many blocks have been encrypted for the same request instance"));
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700413 }
414 else {
415 counter += increment;
416 }
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800417 // boost::endian::store_big_u32(&iv[8], counter);
418 storeBigU32(&iv[8], counter);
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800419}
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700420
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800421Block
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800422encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key,
423 const uint8_t* payload, size_t payloadSize,
424 const uint8_t* associatedData, size_t associatedDataSize,
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800425 std::vector <uint8_t>& encryptionIv)
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800426{
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700427 // The spec of AES encrypted payload TLV used in NDNCERT:
428 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700429 Buffer encryptedPayload(payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700430 uint8_t tag[16];
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800431 if (encryptionIv.empty()) {
432 encryptionIv.resize(12, 0);
433 random::generateSecureBytes(encryptionIv.data(), 8);
434 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700435 size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800436 key, encryptionIv.data(), encryptedPayload.data(), tag);
tylerliu1f480be2020-11-10 13:02:53 -0800437 Block content(tlvType);
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800438 content.push_back(makeBinaryBlock(tlv::InitializationVector, encryptionIv.data(), encryptionIv.size()));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700439 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700440 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700441 content.encode();
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800442 // update IV's counter
443 updateIv(encryptionIv, payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700444 return content;
445}
446
447Buffer
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800448decodeBlockWithAesGcm128(const Block& block, const uint8_t* key,
449 const uint8_t* associatedData, size_t associatedDataSize,
Zhiyi Zhang1e5d6772021-02-18 17:35:46 -0800450 std::vector <uint8_t>& decryptionIv, const std::vector <uint8_t>& encryptionIv)
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700451{
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700452 // The spec of AES encrypted payload TLV used in NDNCERT:
453 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700454 block.parse();
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700455 const auto& encryptedPayloadBlock = block.get(tlv::EncryptedPayload);
456 Buffer result(encryptedPayloadBlock.value_size());
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800457 if (block.get(tlv::InitializationVector).value_size() != 12 ||
458 block.get(tlv::AuthenticationTag).value_size() != 16) {
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800459 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800460 "The observed IV or Authentication Tag is of an unexpected size."));
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800461 }
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800462 std::vector <uint8_t> observedDecryptionIv(block.get(tlv::InitializationVector).value(),
463 block.get(tlv::InitializationVector).value() + 12);
464 if (!encryptionIv.empty()) {
465 if (std::equal(observedDecryptionIv.begin(), observedDecryptionIv.begin() + 8, encryptionIv.begin())) {
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800466 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800467 "The observed IV's the random component should be different from ours."));
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800468 }
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800469 }
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800470 if (!decryptionIv.empty()) {
471 if (loadBigU32(observedDecryptionIv, 8) < loadBigU32(decryptionIv, 8) ||
472 !std::equal(observedDecryptionIv.begin(), observedDecryptionIv.begin() + 8, decryptionIv.begin())) {
473 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
474 "The observed IV's counter should be monotonically increasing "
475 "and the random component must be the same from the requester."));
476 }
477 }
478 decryptionIv = observedDecryptionIv;
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700479 auto resultLen = aesGcm128Decrypt(encryptedPayloadBlock.value(), encryptedPayloadBlock.value_size(),
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700480 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800481 key, decryptionIv.data(), result.data());
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700482 if (resultLen != encryptedPayloadBlock.value_size()) {
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700483 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800484 "Decrypted payload is of an unexpected size."));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700485 }
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800486 updateIv(decryptionIv, resultLen);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700487 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700488}
489
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700490} // namespace ndncert
491} // namespace ndn