blob: f2024c138e3861587ea9f1a5630c8a372a6b5ff5 [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 if (privECKey == nullptr) {
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -070098 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070099 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700100 auto ecPoint = EC_KEY_get0_public_key(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700101 auto group = EC_KEY_get0_group(privECKey);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700102 auto requiredBufLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700103 m_pubKey.resize(requiredBufLen);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700104 auto rev = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED,
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700105 m_pubKey.data(), requiredBufLen, nullptr);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700106 EC_KEY_free(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700107 if (rev == 0) {
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700108 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 -0700109 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700110 return m_pubKey;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700111}
112
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800113const std::vector <uint8_t>&
114ECDHState::deriveSecret(const std::vector <uint8_t>& peerKey)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700115{
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700116 // prepare self private key
117 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700118 if (privECKey == nullptr) {
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700119 NDN_THROW(std::runtime_error("Cannot not get key when calling EVP_PKEY_get1_EC_KEY()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700120 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700121 auto group = EC_KEY_get0_group(privECKey);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700122 EC_KEY_free(privECKey);
123 // prepare the peer public key
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 auto peerPoint = EC_POINT_new(group);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700125 if (peerPoint == nullptr) {
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700126 NDN_THROW(std::runtime_error("Cannot create the EC_POINT for peer key when calling EC_POINT_new()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700127 }
Zhiyi Zhang0eb74242020-10-20 18:33:22 -0700128 if (EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr) == 0) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700129 EC_POINT_free(peerPoint);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700130 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 -0800131 }
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700132 EC_KEY* ecPeerkey = EC_KEY_new();
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700133 if (ecPeerkey == nullptr) {
Zhiyi Zhang8da54d62019-11-21 00:03:05 -0800134 EC_POINT_free(peerPoint);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700135 NDN_THROW(std::runtime_error("Cannot create EC_KEY for peer key when calling EC_KEY_new()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700136 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700137 if (EC_KEY_set_group(ecPeerkey, group) != 1) {
138 EC_POINT_free(peerPoint);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700139 NDN_THROW(std::runtime_error("Cannot set group for peer key's EC_KEY when calling EC_KEY_set_group()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700140 }
141 if (EC_KEY_set_public_key(ecPeerkey, peerPoint) == 0) {
142 EC_KEY_free(ecPeerkey);
143 EC_POINT_free(peerPoint);
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800144 NDN_THROW(
145 std::runtime_error("Cannot initialize peer EC_KEY with the EC_POINT when calling EC_KEY_set_public_key()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700146 }
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700147 EVP_PKEY* evpPeerkey = EVP_PKEY_new();
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700148 if (EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey) == 0) {
149 EC_KEY_free(ecPeerkey);
150 EC_POINT_free(peerPoint);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700151 NDN_THROW(std::runtime_error("Cannot create EVP_PKEY for peer key when calling EVP_PKEY_new()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700152 }
153 EC_KEY_free(ecPeerkey);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700154 EC_POINT_free(peerPoint);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700155 // ECDH context
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700156 EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700157 if (ctx == nullptr) {
158 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700159 NDN_THROW(std::runtime_error("Cannot create context for ECDH when calling EVP_PKEY_CTX_new()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700160 }
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700161 // Initialize
162 if (1 != EVP_PKEY_derive_init(ctx)) {
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700163 EVP_PKEY_CTX_free(ctx);
164 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700165 NDN_THROW(std::runtime_error("Cannot initialize context for ECDH when calling EVP_PKEY_derive_init()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700166 }
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700167 // Provide the peer public key
168 if (1 != EVP_PKEY_derive_set_peer(ctx, evpPeerkey)) {
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700169 EVP_PKEY_CTX_free(ctx);
170 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700171 NDN_THROW(std::runtime_error("Cannot set peer key for ECDH when calling EVP_PKEY_derive_set_peer()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700172 }
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700173 // Determine buffer length for shared secret
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700174 size_t secretLen = 0;
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700175 if (1 != EVP_PKEY_derive(ctx, nullptr, &secretLen)) {
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700176 EVP_PKEY_CTX_free(ctx);
177 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700178 NDN_THROW(std::runtime_error("Cannot determine the needed buffer length when calling EVP_PKEY_derive()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700179 }
Zhiyi Zhangdef00e42020-10-20 22:41:56 -0700180 m_secret.resize(secretLen);
181 // Derive the shared secret
182 if (1 != (EVP_PKEY_derive(ctx, m_secret.data(), &secretLen))) {
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700183 EVP_PKEY_CTX_free(ctx);
184 EVP_PKEY_free(evpPeerkey);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700185 NDN_THROW(std::runtime_error("Cannot derive ECDH secret when calling EVP_PKEY_derive()"));
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700186 }
Zhiyi Zhangbed854c2020-10-20 18:25:35 -0700187 EVP_PKEY_CTX_free(ctx);
188 EVP_PKEY_free(evpPeerkey);
189 return m_secret;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190}
191
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700192void
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700193hmacSha256(const uint8_t* data, size_t dataLen,
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700194 const uint8_t* key, size_t keyLen,
195 uint8_t* result)
Davide Pesavento368341b2019-08-13 23:57:50 -0400196{
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700197 auto ret = HMAC(EVP_sha256(), key, keyLen,
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700198 data, dataLen, result, nullptr);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700199 if (ret == nullptr) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700200 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700201 }
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700202}
203
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700204size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700205hkdf(const uint8_t* secret, size_t secretLen, const uint8_t* salt,
206 size_t saltLen, uint8_t* output, size_t outputLen,
207 const uint8_t* info, size_t infoLen)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700208{
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700209 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700210 if (EVP_PKEY_derive_init(pctx) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700211 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700212 NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()"));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700213 }
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700214 if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700215 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700216 NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()"));
Zhiyi Zhanga2ce5992019-08-14 17:35:00 -0700217 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700218 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700219 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700220 NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()"));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700221 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700222 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700223 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700224 NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700225 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700226 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700227 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700228 NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700229 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700230 size_t outLen = outputLen;
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700231 if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700232 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700233 NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()"));
Zhiyi Zhangf12ee302020-10-14 17:46:44 -0700234 }
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700235 EVP_PKEY_CTX_free(pctx);
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700236 return outLen;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700237}
238
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700239size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700240aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700241 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700242{
Zhiyi Zhangb8cb0472020-05-05 20:55:05 -0700243 EVP_CIPHER_CTX* ctx;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700244 int len;
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700245 size_t ciphertextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700246 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700247 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700248 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700249 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700250 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700251 NDN_THROW(std::runtime_error("Cannot initialize the encryption operation when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700252 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700253 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
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 set IV length when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700256 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700257 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
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 initialize key and IV when calling EVP_EncryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700260 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700261 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700262 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700263 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700264 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700265 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700266 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700267 NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700268 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700269 ciphertextLen = len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700270 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700271 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700272 NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700273 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700274 ciphertextLen += len;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700275 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700276 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700277 NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700278 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700279 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700280 return ciphertextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700281}
282
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700283size_t
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700284aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700285 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700286{
287 EVP_CIPHER_CTX* ctx;
288 int len;
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700289 size_t plaintextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700290 if (!(ctx = EVP_CIPHER_CTX_new())) {
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700291 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700292 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700293 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
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 initialise the decryption operation when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700296 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700297 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
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 set IV length when calling EVP_CIPHER_CTX_ctrl"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700300 }
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700301 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700302 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700303 NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700304 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700305 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700306 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700307 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700308 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700309 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen)) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700310 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang5c059452020-10-16 17:43:31 -0700311 NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700312 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700313 plaintextLen = len;
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700314 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)))) {
Zhiyi Zhangdc25ddf2020-10-20 14:28:55 -0700315 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700316 NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700317 }
Zhiyi Zhange5a07c92020-10-21 10:51:37 -0700318 auto ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700319 EVP_CIPHER_CTX_free(ctx);
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700320 if (ret > 0) {
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700321 plaintextLen += len;
322 return plaintextLen;
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700323 }
324 else {
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700325 NDN_THROW(std::runtime_error("Cannot finalize the decryption when calling EVP_DecryptFinal_ex()"));
Zhiyi Zhanga67fa462020-04-19 13:48:03 -0700326 }
327}
328
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800329// Can be removed after boost version 1.72, replaced by boost::endian::load_big_u32
330static uint32_t
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800331loadBigU32(const std::vector <uint8_t>& iv, size_t pos)
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800332{
333 uint32_t result = iv[pos] << 24 | iv[pos + 1] << 16 | iv[pos + 2] << 8 | iv[pos + 3];
334 return result;
335}
336
337// Can be removed after boost version 1.72, replaced by boost::endian::store_big_u32
338static void
339storeBigU32(uint8_t* iv, uint32_t counter)
340{
341 uint32_t temp = boost::endian::native_to_big(counter);
342 std::memcpy(iv, reinterpret_cast<const uint8_t*>(&temp), 4);
343 return;
344}
345
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800346static void
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800347updateIv(std::vector <uint8_t>& iv, size_t payloadSize)
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700348{
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800349 // uint32_t counter = boost::endian::load_big_u32(&iv[8]);
350 uint32_t counter = loadBigU32(iv, 8);
Zhiyi Zhang199508a2020-10-21 10:45:50 -0700351 uint32_t increment = (payloadSize + 15) / 16;
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800352 if (std::numeric_limits<uint32_t>::max() - counter <= increment) {
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700353 NDN_THROW(std::runtime_error("Error incrementing the AES block counter: "
Zhiyi Zhange11a2d62020-10-22 09:59:12 -0700354 "too many blocks have been encrypted for the same request instance"));
Zhiyi Zhang159ba632020-10-20 15:09:12 -0700355 }
356 else {
357 counter += increment;
358 }
Zhiyi Zhang886a2262020-12-24 15:19:33 -0800359 // boost::endian::store_big_u32(&iv[8], counter);
360 storeBigU32(&iv[8], counter);
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800361}
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700362
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800363Block
Zhiyi Zhangc1e98152020-12-21 16:15:39 -0800364encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key,
365 const uint8_t* payload, size_t payloadSize,
366 const uint8_t* associatedData, size_t associatedDataSize,
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800367 std::vector <uint8_t>& encryptionIv)
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800368{
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700369 // The spec of AES encrypted payload TLV used in NDNCERT:
370 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700371 Buffer encryptedPayload(payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700372 uint8_t tag[16];
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800373 if (encryptionIv.empty()) {
374 encryptionIv.resize(12, 0);
375 random::generateSecureBytes(encryptionIv.data(), 8);
376 }
Zhiyi Zhang11cf7eb2020-10-20 15:43:36 -0700377 size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800378 key, encryptionIv.data(), encryptedPayload.data(), tag);
tylerliu1f480be2020-11-10 13:02:53 -0800379 Block content(tlvType);
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800380 content.push_back(makeBinaryBlock(tlv::InitializationVector, encryptionIv.data(), encryptionIv.size()));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700381 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
Zhiyi Zhang222810b2020-10-16 21:50:35 -0700382 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700383 content.encode();
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800384 // update IV's counter
385 updateIv(encryptionIv, payloadSize);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700386 return content;
387}
388
389Buffer
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800390decodeBlockWithAesGcm128(const Block& block, const uint8_t* key,
391 const uint8_t* associatedData, size_t associatedDataSize,
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800392 std::vector<uint8_t>& decryptionIv, const std::vector <uint8_t>& encryptionIv)
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700393{
Zhiyi Zhang5e74ecd2020-10-21 12:52:25 -0700394 // The spec of AES encrypted payload TLV used in NDNCERT:
395 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700396 block.parse();
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700397 const auto& encryptedPayloadBlock = block.get(tlv::EncryptedPayload);
398 Buffer result(encryptedPayloadBlock.value_size());
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800399 if (block.get(tlv::InitializationVector).value_size() != 12 ||
400 block.get(tlv::AuthenticationTag).value_size() != 16) {
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800401 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800402 "The observed IV or Authentication Tag is of an unexpected size."));
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800403 }
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800404 std::vector <uint8_t> observedDecryptionIv(block.get(tlv::InitializationVector).value(),
405 block.get(tlv::InitializationVector).value() + 12);
406 if (!encryptionIv.empty()) {
407 if (std::equal(observedDecryptionIv.begin(), observedDecryptionIv.begin() + 8, encryptionIv.begin())) {
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800408 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800409 "The observed IV's the random component should be different from ours."));
Zhiyi Zhangde58e602021-02-17 23:09:02 -0800410 }
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800411 }
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800412 if (!decryptionIv.empty()) {
413 if (loadBigU32(observedDecryptionIv, 8) < loadBigU32(decryptionIv, 8) ||
414 !std::equal(observedDecryptionIv.begin(), observedDecryptionIv.begin() + 8, decryptionIv.begin())) {
415 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
416 "The observed IV's counter should be monotonically increasing "
417 "and the random component must be the same from the requester."));
418 }
419 }
420 decryptionIv = observedDecryptionIv;
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700421 auto resultLen = aesGcm128Decrypt(encryptedPayloadBlock.value(), encryptedPayloadBlock.value_size(),
Zhiyi Zhangf908f662020-10-21 12:55:10 -0700422 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800423 key, decryptionIv.data(), result.data());
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700424 if (resultLen != encryptedPayloadBlock.value_size()) {
Zhiyi Zhangaeab4972020-10-22 22:20:40 -0700425 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
Zhiyi Zhang3b9a5032021-02-18 11:15:09 -0800426 "Decrypted payload is of an unexpected size."));
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700427 }
Zhiyi Zhang4f1c0102020-12-21 15:08:09 -0800428 updateIv(decryptionIv, resultLen);
Zhiyi Zhangc5d93a92020-10-14 17:07:35 -0700429 return result;
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700430}
431
Zhiyi Zhange4891b72020-10-10 15:11:57 -0700432} // namespace ndncert
433} // namespace ndn