blob: d69c2dd234e72820ecccb3b547feb5d6fac2556f [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 {
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -070045 // contexts
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -070046 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 Zhang11cf7eb2020-10-20 15:43:36 -070072 m_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 Zhang11cf7eb2020-10-20 15:43:36 -070075 if (nullptr == (m_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 Zhang11cf7eb2020-10-20 15:43:36 -070078 if (EVP_PKEY_paramgen_init(m_context->ctx_params) != 1) {
79 m_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 Zhang11cf7eb2020-10-20 15:43:36 -070082 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(m_context->ctx_params, EC_NID)) {
83 m_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 Zhang11cf7eb2020-10-20 15:43:36 -070086 if (!EVP_PKEY_paramgen(m_context->ctx_params, &m_context->params)) {
87 m_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 Zhang11cf7eb2020-10-20 15:43:36 -070090 if (nullptr == (m_context->ctx_keygen = EVP_PKEY_CTX_new(m_context->params, nullptr))) {
91 m_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 Zhang11cf7eb2020-10-20 15:43:36 -070094 if (1 != EVP_PKEY_keygen_init(m_context->ctx_keygen)) {
95 m_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 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -070098 if (1 != EVP_PKEY_keygen(m_context->ctx_keygen, &m_context->privkey)) {
99 m_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{
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700110 auto privECKey = EVP_PKEY_get1_EC_KEY(m_context->privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111 if (privECKey == nullptr) {
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700112 m_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 Zhang159ba632020-10-20 15:09:12 -0700118 m_publicKey, sizeof(m_publicKey), 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 Zhang11cf7eb2020-10-20 15:43:36 -0700121 m_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{
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700142 auto privECKey = EVP_PKEY_get1_EC_KEY(m_context->privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700143 if (privECKey == nullptr) {
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700144 m_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 Zhang11cf7eb2020-10-20 15:43:36 -0700153 m_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 Zhang159ba632020-10-20 15:09:12 -0700156 result = ECDH_compute_key(m_sharedSecret, sizeof(m_sharedSecret), peerPoint, privECKey, nullptr);
Zhiyi Zhang938d7032020-10-12 17:23:45 -0700157 if (result == -1) {
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800158 EC_POINT_free(peerPoint);
159 EC_KEY_free(privECKey);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700160 m_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 Zhang11cf7eb2020-10-20 15:43:36 -0700180hmacSha256(const uint8_t* data, size_t dataLen,
181 const uint8_t* key, size_t keyLen,
Zhiyi Zhangcfad98d2020-10-11 11:25:14 -0700182 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400183{
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700184 auto ret = HMAC(EVP_sha256(), key, keyLen,
185 static_cast<const unsigned char*>(data), dataLen,
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700186 static_cast<unsigned char*>(result), nullptr);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700187 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700188 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700189 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190}
191
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700192size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700193hkdf(const uint8_t* secret, size_t secretLen, const uint8_t* salt,
194 size_t saltLen, uint8_t* output, size_t outputLen,
195 const uint8_t* info, size_t infoLen)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700196{
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700197 EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
198 if (EVP_PKEY_derive_init(pctx) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700199 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700200 NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700201 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700202 if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700203 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700204 NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()."));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700205 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700206 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700207 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700208 NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()."));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700209 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700210 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen) <= 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 set secret when calling EVP_PKEY_CTX_set1_hkdf_key()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700213 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700214 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen) <= 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 info when calling EVP_PKEY_CTX_add1_hkdf_info()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700217 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700218 size_t outLen = outputLen;
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700219 if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700220 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700221 NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()."));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700222 }
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700223 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700224 return outLen;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700225}
226
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700227int
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700228aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700229 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
230{
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700231 EVP_CIPHER_CTX* ctx;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700232 int len;
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700233 int ciphertextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700234 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700235 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700236 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700237 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700238 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700239 NDN_THROW(std::runtime_error("Cannot initialise the encryption operation when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700240 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700241 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700242 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700243 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700244 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700245 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700246 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700247 NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700248 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700249 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700250 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700251 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700252 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700253 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700254 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700255 NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700256 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700257 ciphertextLen = len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700258 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700259 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700260 NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700261 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700262 ciphertextLen += len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700263 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700264 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700265 NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700266 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700267 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700268 return ciphertextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700269}
270
271int
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700272aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700273 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
274{
275 EVP_CIPHER_CTX* ctx;
276 int len;
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700277 int plaintextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700278 int ret;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700279 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700280 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700281 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700282 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700283 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700284 NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700285 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700286 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700287 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700288 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700289 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700290 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700291 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700292 NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700293 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700294 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700295 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700296 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700297 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700298 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700299 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700300 NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700301 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700302 plaintextLen = len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700303 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700304 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700305 NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700306 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700307 ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700308 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700309 if (ret > 0) {
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700310 plaintextLen += len;
311 return plaintextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700312 }
313 else {
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700314 return -1;
315 }
316}
317
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700318Block
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700319encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700320 const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700321{
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700322 Buffer iv(12);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700323 random::generateSecureBytes(iv.data(), iv.size());
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700324 if (tlvType == ndn::tlv::ApplicationParameters) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700325 // requester
326 iv[0] &= ~(1UL << 7);
327 }
328 else {
329 // CA
330 iv[0] |= 1UL << 7;
331 }
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700332 uint32_t temp = boost::endian::native_to_big(counter);
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700333 std::memcpy(&iv[8], reinterpret_cast<const uint8_t*>(&temp), 4);
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700334 uint32_t increment = std::ceil((payloadSize + 16 - 1)/16);
335 if (std::numeric_limits<uint32_t>::max() - counter < increment) {
336 // simply set counter to be 0. Will not hurt the property of being unique.
337 counter = 0;
338 }
339 else {
340 counter += increment;
341 }
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700342
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700343 Buffer encryptedPayload(payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700344 uint8_t tag[16];
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700345 size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700346 key, iv.data(), encryptedPayload.data(), tag);
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700347 auto content = makeEmptyBlock(tlvType);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700348 content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
349 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700350 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700351 content.encode();
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700352 return content;
353}
354
355Buffer
356decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
357{
358 block.parse();
Zhiyi Zhangcace6ab2020-10-16 21:51:44 -0700359 Buffer result(block.get(tlv::EncryptedPayload).value_size());
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700360 int resultLen = aesGcm128Decrypt(block.get(tlv::EncryptedPayload).value(),
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700361 block.get(tlv::EncryptedPayload).value_size(),
362 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
363 key, block.get(tlv::InitializationVector).value(), result.data());
364 if (resultLen == -1 || resultLen != (int)block.get(tlv::EncryptedPayload).value_size()) {
365 return Buffer();
366 }
367 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700368}
369
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700370} // namespace ndncert
371} // namespace ndn