blob: 2437d163ef0a80e27df8062933605fe2b6e4b033 [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 Zhangdc25ddf2020-10-20 14:28:55 -070021#include "crypto-helpers.hpp"
22
23#include <boost/endian/conversion.hpp>
Zhiyi Zhang222810b2020-10-16 21:50:35 -070024#include <cmath>
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>
33#include <openssl/evp.h>
34#include <openssl/hmac.h>
35#include <openssl/kdf.h>
36#include <openssl/pem.h>
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 Zhangdc25ddf2020-10-20 14:28:55 -070043 ~ECDH_CTX()
44 {
45 // Contexts
46 if (ctx_params != nullptr) {
47 EVP_PKEY_CTX_free(ctx_params);
48 }
49 if (ctx_keygen != nullptr) {
50 EVP_PKEY_CTX_free(ctx_keygen);
51 }
52 // Keys
53 if (privkey != nullptr) {
54 EVP_PKEY_free(privkey);
55 }
56 if (peerkey != nullptr) {
57 EVP_PKEY_free(peerkey);
58 }
59 if (params != nullptr) {
60 EVP_PKEY_free(params);
61 }
62 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070063 EVP_PKEY_CTX* ctx_params = nullptr;
64 EVP_PKEY_CTX* ctx_keygen = nullptr;
65 EVP_PKEY* privkey = nullptr;
66 EVP_PKEY* peerkey = nullptr;
67 EVP_PKEY* params = nullptr;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -070068};
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070069
70ECDHState::ECDHState()
71{
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070072 context = std::make_unique<ECDH_CTX>();
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070073 auto EC_NID = NID_X9_62_prime256v1;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070074
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070075 if (nullptr == (context->ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr))) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070076 NDN_THROW(std::runtime_error("Could not create context."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070077 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070078 if (EVP_PKEY_paramgen_init(context->ctx_params) != 1) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070079 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -070080 NDN_THROW(std::runtime_error("Could not initialize parameter generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070081 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -070082 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(context->ctx_params, EC_NID)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070083 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -070084 NDN_THROW(std::runtime_error("Likely unknown elliptical curve ID specified."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070085 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070086 if (!EVP_PKEY_paramgen(context->ctx_params, &context->params)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070087 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -070088 NDN_THROW(std::runtime_error("Could not create parameter object parameters."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070089 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070090 if (nullptr == (context->ctx_keygen = EVP_PKEY_CTX_new(context->params, nullptr))) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070091 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -070092 NDN_THROW(std::runtime_error("Could not create the context for the key generation"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070093 }
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -070094 if (1 != EVP_PKEY_keygen_init(context->ctx_keygen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070095 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -070096 NDN_THROW(std::runtime_error("Could not init context for key generation."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070097 }
98 if (1 != EVP_PKEY_keygen(context->ctx_keygen, &context->privkey)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070099 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700100 NDN_THROW(std::runtime_error("Could not generate DHE keys in final step"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700101 }
102}
103
104ECDHState::~ECDHState()
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700105{}
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700106
107uint8_t*
108ECDHState::getRawSelfPubKey()
109{
110 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 if (privECKey == nullptr) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700112 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700113 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700114 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115 auto ecPoint = EC_KEY_get0_public_key(privECKey);
116 const EC_GROUP* group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700117 m_publicKeyLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_COMPRESSED,
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700118 m_publicKey, 256, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700119 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700120 if (m_publicKeyLen == 0) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700121 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700122 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 -0700123 }
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700124 return m_publicKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700125}
126
127std::string
128ECDHState::getBase64PubKey()
129{
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700130 if (m_publicKeyLen == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700131 this->getRawSelfPubKey();
132 }
Davide Pesavento368341b2019-08-13 23:57:50 -0400133 std::ostringstream os;
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700134 namespace t = ndn::security::transform;
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700135 t::bufferSource(m_publicKey, m_publicKeyLen) >> t::base64Encode(false) >> t::streamSink(os);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700136 return os.str();
137}
138
139uint8_t*
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700140ECDHState::deriveSecret(const uint8_t* peerkey, size_t peerKeySize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700141{
142 auto privECKey = EVP_PKEY_get1_EC_KEY(context->privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700143 if (privECKey == nullptr) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700144 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700145 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700146 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700147 auto group = EC_KEY_get0_group(privECKey);
148 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800149 int result = EC_POINT_oct2point(group, peerPoint, peerkey, peerKeySize, nullptr);
150 if (result == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700151 EC_POINT_free(peerPoint);
152 EC_KEY_free(privECKey);
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700153 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700154 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 -0800155 }
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700156 result = ECDH_compute_key(m_sharedSecret, 256, peerPoint, privECKey, nullptr);
157 if (result == -1) {
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800158 EC_POINT_free(peerPoint);
159 EC_KEY_free(privECKey);
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700160 context.reset();
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700161 NDN_THROW(std::runtime_error("Cannot generate ECDH secret when calling ECDH_compute_key()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700162 }
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700163 m_sharedSecretLen = static_cast<size_t>(result);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700164 EC_POINT_free(peerPoint);
165 EC_KEY_free(privECKey);
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700166 return m_sharedSecret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700167}
168
169uint8_t*
170ECDHState::deriveSecret(const std::string& peerKeyStr)
171{
172 namespace t = ndn::security::transform;
173 OBufferStream os;
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800174 t::bufferSource(peerKeyStr) >> t::base64Decode(false) >> t::streamSink(os);
Davide Pesavento368341b2019-08-13 23:57:50 -0400175 auto result = os.buf();
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700176 return this->deriveSecret(result->data(), result->size());
177}
178
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700179void
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700180hmac_sha256(const uint8_t* data, size_t data_length,
181 const uint8_t* key, size_t key_length,
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700182 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400183{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700184 auto ret = HMAC(EVP_sha256(), key, key_length, (unsigned char*)data, data_length,
185 (unsigned char*)result, nullptr);
186 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700187 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700188 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700189}
190
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700191int
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700192hkdf(const uint8_t* secret, size_t secret_len, const uint8_t* salt,
193 size_t salt_len, uint8_t* output, size_t output_len,
194 const uint8_t* info, size_t info_len)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700195{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700196 EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
197 if (EVP_PKEY_derive_init(pctx) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700198 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700199 NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700200 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700201 if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700202 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700203 NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700204 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700205 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700206 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700207 NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700208 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700209 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700210 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700211 NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700212 }
213 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700214 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700215 NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700216 }
217 size_t outLen = output_len;
218 if (EVP_PKEY_derive(pctx, output, &outLen) <= 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 derive result when calling EVP_PKEY_derive()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700221 }
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700222 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700223 return (int)outLen;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700224}
225
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700226int
227aes_gcm_128_encrypt(const uint8_t* plaintext, size_t plaintext_len, const uint8_t* associated, size_t associated_len,
228 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
229{
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700230 EVP_CIPHER_CTX* ctx;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700231 int len;
232 int ciphertext_len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700233 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700234 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700235 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700236 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700237 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700238 NDN_THROW(std::runtime_error("Cannot initialise the encryption operation when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700239 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700240 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700241 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700242 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700243 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700244 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700245 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700246 NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700247 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700248 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700249 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700250 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700251 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700252 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700253 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700254 NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700255 }
256 ciphertext_len = len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700257 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700258 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700259 NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700260 }
261 ciphertext_len += len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700262 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700263 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700264 NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700265 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700266 EVP_CIPHER_CTX_free(ctx);
267 return ciphertext_len;
268}
269
270int
271aes_gcm_128_decrypt(const uint8_t* ciphertext, size_t ciphertext_len, const uint8_t* associated, size_t associated_len,
272 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
273{
274 EVP_CIPHER_CTX* ctx;
275 int len;
276 int plaintext_len;
277 int ret;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700278 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700279 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700280 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700281 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700282 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700283 NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700284 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700285 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700286 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700287 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700288 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700289 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700290 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700291 NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700292 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700293 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700294 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700295 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700296 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700297 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700298 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700299 NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700300 }
301 plaintext_len = len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700302 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700303 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700304 NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700305 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700306 // Finalise the decryption. A positive return value indicates success,
307 // anything else is a failure - the plaintext is not trustworthy.
308 ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700309 // Clean up
310 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700311 if (ret > 0) {
312 // Success
313 plaintext_len += len;
314 return plaintext_len;
315 }
316 else {
317 // Verify failed
318 return -1;
319 }
320}
321
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700322Block
323encodeBlockWithAesGcm128(uint32_t tlv_type, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700324 const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700325{
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700326 Buffer iv(12);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700327 random::generateSecureBytes(iv.data(), iv.size());
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700328 if (tlv_type == ndn::tlv::ApplicationParameters) {
329 // requester
330 iv[0] &= ~(1UL << 7);
331 }
332 else {
333 // CA
334 iv[0] |= 1UL << 7;
335 }
336 uint32_t temp = counter;
337 boost::endian::native_to_big_inplace(temp);
338 std::memcpy(&iv[8], reinterpret_cast<const uint8_t*>(&temp), 4);
339 counter += std::ceil(payloadSize / 8);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700340
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700341 Buffer encryptedPayload(payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700342 uint8_t tag[16];
343 size_t encryptedPayloadLen = aes_gcm_128_encrypt(payload, payloadSize, associatedData, associatedDataSize,
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700344 key, iv.data(), encryptedPayload.data(), tag);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700345 auto content = makeEmptyBlock(tlv_type);
346 content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
347 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700348 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700349 content.encode();
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700350 return content;
351}
352
353Buffer
354decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
355{
356 block.parse();
Zhiyi Zhangcace6ab2020-10-16 21:51:44 -0700357 Buffer result(block.get(tlv::EncryptedPayload).value_size());
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700358 int resultLen = aes_gcm_128_decrypt(block.get(tlv::EncryptedPayload).value(),
359 block.get(tlv::EncryptedPayload).value_size(),
360 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
361 key, block.get(tlv::InitializationVector).value(), result.data());
362 if (resultLen == -1 || resultLen != (int)block.get(tlv::EncryptedPayload).value_size()) {
363 return Buffer();
364 }
365 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700366}
367
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700368} // namespace ndncert
369} // namespace ndn