blob: 3142268735a129da4300901f133a44c4fff13a3f [file] [log] [blame]
Zhiyi Zhang63589b82020-10-10 10:27:09 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2017-2020, Regents of the University of California.
4 *
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
21#include "detail/crypto-helpers.hpp"
22
23#include <boost/endian/conversion.hpp>
24#include <cstring>
25#include <ndn-cxx/encoding/buffer-stream.hpp>
26#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>
29#include <ndn-cxx/security/transform/stream-sink.hpp>
30#include <ndn-cxx/util/random.hpp>
31#include <openssl/ec.h>
32#include <openssl/err.h>
33#include <openssl/hmac.h>
34#include <openssl/kdf.h>
35#include <openssl/pem.h>
36
37namespace ndn {
38namespace ndncert {
39
40ECDHState::ECDHState()
41{
42 auto EC_NID = NID_X9_62_prime256v1;
43 // params context
44 EVP_PKEY_CTX* ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr);
45 if (ctx_params == nullptr) {
46 NDN_THROW(std::runtime_error("Could not create context"));
47 }
48 if (EVP_PKEY_paramgen_init(ctx_params) != 1) {
49 EVP_PKEY_CTX_free(ctx_params);
50 NDN_THROW(std::runtime_error("Could not initialize parameter generation"));
51 }
52 if (1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_params, EC_NID)) {
53 EVP_PKEY_CTX_free(ctx_params);
54 NDN_THROW(std::runtime_error("Likely unknown elliptical curve ID specified"));
55 }
56 // generate params
57 EVP_PKEY* params = nullptr;
58 if (!EVP_PKEY_paramgen(ctx_params, &params)) {
59 EVP_PKEY_CTX_free(ctx_params);
60 NDN_THROW(std::runtime_error("Could not create parameter object parameters"));
61 }
62 // key generation context
63 EVP_PKEY_CTX* ctx_keygen = EVP_PKEY_CTX_new(params, nullptr);
64 if (ctx_keygen == nullptr) {
65 EVP_PKEY_free(params);
66 EVP_PKEY_CTX_free(ctx_params);
67 NDN_THROW(std::runtime_error("Could not create the context for the key generation"));
68 }
69 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);
73 NDN_THROW(std::runtime_error("Could not init context for key generation"));
74 }
75 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);
79 NDN_THROW(std::runtime_error("Could not generate DHE keys in final step"));
80 }
81 EVP_PKEY_CTX_free(ctx_keygen);
82 EVP_PKEY_free(params);
83 EVP_PKEY_CTX_free(ctx_params);
84}
85
86ECDHState::~ECDHState()
87{
88 if (m_privkey != nullptr) {
89 EVP_PKEY_free(m_privkey);
90 }
91}
92
93const std::vector<uint8_t>&
94ECDHState::getSelfPubKey()
95{
96 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
97 if (privECKey == nullptr) {
98 NDN_THROW(std::runtime_error("Could not get key when calling EVP_PKEY_get1_EC_KEY()"));
99 }
100 auto ecPoint = EC_KEY_get0_public_key(privECKey);
101 auto group = EC_KEY_get0_group(privECKey);
102 auto requiredBufLen = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr);
103 m_pubKey.resize(requiredBufLen);
104 auto rev = EC_POINT_point2oct(group, ecPoint, POINT_CONVERSION_UNCOMPRESSED,
105 m_pubKey.data(), requiredBufLen, nullptr);
106 EC_KEY_free(privECKey);
107 if (rev == 0) {
108 NDN_THROW(std::runtime_error("Could not convert EC_POINTS to octet string when calling EC_POINT_point2oct()"));
109 }
110 return m_pubKey;
111}
112
113const std::vector<uint8_t>&
114ECDHState::deriveSecret(const std::vector<uint8_t>& peerKey)
115{
116 // prepare self private key
117 auto privECKey = EVP_PKEY_get1_EC_KEY(m_privkey);
118 if (privECKey == nullptr) {
119 NDN_THROW(std::runtime_error("Cannot not get key when calling EVP_PKEY_get1_EC_KEY()"));
120 }
121 auto group = EC_KEY_get0_group(privECKey);
122 EC_KEY_free(privECKey);
123 // prepare the peer public key
124 auto peerPoint = EC_POINT_new(group);
125 if (peerPoint == nullptr) {
126 NDN_THROW(std::runtime_error("Cannot create the EC_POINT for peer key when calling EC_POINT_new()"));
127 }
128 if (EC_POINT_oct2point(group, peerPoint, peerKey.data(), peerKey.size(), nullptr) == 0) {
129 EC_POINT_free(peerPoint);
130 NDN_THROW(std::runtime_error("Cannot convert peer's key into a EC point when calling EC_POINT_oct2point()"));
131 }
132 EC_KEY* ecPeerkey = EC_KEY_new();
133 if (ecPeerkey == nullptr) {
134 EC_POINT_free(peerPoint);
135 NDN_THROW(std::runtime_error("Cannot create EC_KEY for peer key when calling EC_KEY_new()"));
136 }
137 if (EC_KEY_set_group(ecPeerkey, group) != 1) {
138 EC_POINT_free(peerPoint);
139 NDN_THROW(std::runtime_error("Cannot set group for peer key's EC_KEY when calling EC_KEY_set_group()"));
140 }
141 if (EC_KEY_set_public_key(ecPeerkey, peerPoint) == 0) {
142 EC_KEY_free(ecPeerkey);
143 EC_POINT_free(peerPoint);
144 NDN_THROW(std::runtime_error("Cannot initialize peer EC_KEY with the EC_POINT when calling EC_KEY_set_public_key()"));
145 }
146 EVP_PKEY* evpPeerkey = EVP_PKEY_new();
147 if (EVP_PKEY_set1_EC_KEY(evpPeerkey, ecPeerkey) == 0) {
148 EC_KEY_free(ecPeerkey);
149 EC_POINT_free(peerPoint);
150 NDN_THROW(std::runtime_error("Cannot create EVP_PKEY for peer key when calling EVP_PKEY_new()"));
151 }
152 EC_KEY_free(ecPeerkey);
153 EC_POINT_free(peerPoint);
154 // ECDH context
155 EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(m_privkey, nullptr);
156 if (ctx == nullptr) {
157 EVP_PKEY_free(evpPeerkey);
158 NDN_THROW(std::runtime_error("Cannot create context for ECDH when calling EVP_PKEY_CTX_new()"));
159 }
160 // Initialize
161 if (1 != EVP_PKEY_derive_init(ctx)) {
162 EVP_PKEY_CTX_free(ctx);
163 EVP_PKEY_free(evpPeerkey);
164 NDN_THROW(std::runtime_error("Cannot initialize context for ECDH when calling EVP_PKEY_derive_init()"));
165 }
166 // Provide the peer public key
167 if (1 != EVP_PKEY_derive_set_peer(ctx, evpPeerkey)) {
168 EVP_PKEY_CTX_free(ctx);
169 EVP_PKEY_free(evpPeerkey);
170 NDN_THROW(std::runtime_error("Cannot set peer key for ECDH when calling EVP_PKEY_derive_set_peer()"));
171 }
172 // Determine buffer length for shared secret
173 size_t secretLen = 0;
174 if (1 != EVP_PKEY_derive(ctx, nullptr, &secretLen)) {
175 EVP_PKEY_CTX_free(ctx);
176 EVP_PKEY_free(evpPeerkey);
177 NDN_THROW(std::runtime_error("Cannot determine the needed buffer length when calling EVP_PKEY_derive()"));
178 }
179 m_secret.resize(secretLen);
180 // Derive the shared secret
181 if (1 != (EVP_PKEY_derive(ctx, m_secret.data(), &secretLen))) {
182 EVP_PKEY_CTX_free(ctx);
183 EVP_PKEY_free(evpPeerkey);
184 NDN_THROW(std::runtime_error("Cannot derive ECDH secret when calling EVP_PKEY_derive()"));
185 }
186 EVP_PKEY_CTX_free(ctx);
187 EVP_PKEY_free(evpPeerkey);
188 return m_secret;
189}
190
191void
192hmacSha256(const uint8_t* data, size_t dataLen,
193 const uint8_t* key, size_t keyLen,
194 uint8_t* result)
195{
196 auto ret = HMAC(EVP_sha256(), key, keyLen,
197 data, dataLen, result, nullptr);
198 if (ret == nullptr) {
199 NDN_THROW(std::runtime_error("Error computing HMAC when calling HMAC()"));
200 }
201}
202
203size_t
204hkdf(const uint8_t* secret, size_t secretLen, const uint8_t* salt,
205 size_t saltLen, uint8_t* output, size_t outputLen,
206 const uint8_t* info, size_t infoLen)
207{
208 EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
209 if (EVP_PKEY_derive_init(pctx) <= 0) {
210 EVP_PKEY_CTX_free(pctx);
211 NDN_THROW(std::runtime_error("HKDF: Cannot init ctx when calling EVP_PKEY_derive_init()"));
212 }
213 if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
214 EVP_PKEY_CTX_free(pctx);
215 NDN_THROW(std::runtime_error("HKDF: Cannot set md when calling EVP_PKEY_CTX_set_hkdf_md()"));
216 }
217 if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, saltLen) <= 0) {
218 EVP_PKEY_CTX_free(pctx);
219 NDN_THROW(std::runtime_error("HKDF: Cannot set salt when calling EVP_PKEY_CTX_set1_hkdf_salt()"));
220 }
221 if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secretLen) <= 0) {
222 EVP_PKEY_CTX_free(pctx);
223 NDN_THROW(std::runtime_error("HKDF: Cannot set secret when calling EVP_PKEY_CTX_set1_hkdf_key()"));
224 }
225 if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, infoLen) <= 0) {
226 EVP_PKEY_CTX_free(pctx);
227 NDN_THROW(std::runtime_error("HKDF: Cannot set info when calling EVP_PKEY_CTX_add1_hkdf_info()"));
228 }
229 size_t outLen = outputLen;
230 if (EVP_PKEY_derive(pctx, output, &outLen) <= 0) {
231 EVP_PKEY_CTX_free(pctx);
232 NDN_THROW(std::runtime_error("HKDF: Cannot derive result when calling EVP_PKEY_derive()"));
233 }
234 EVP_PKEY_CTX_free(pctx);
235 return outLen;
236}
237
238size_t
239aesGcm128Encrypt(const uint8_t* plaintext, size_t plaintextLen, const uint8_t* associated, size_t associatedLen,
240 const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
241{
242 EVP_CIPHER_CTX* ctx;
243 int len;
244 size_t ciphertextLen;
245 if (!(ctx = EVP_CIPHER_CTX_new())) {
246 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
247 }
248 if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
249 EVP_CIPHER_CTX_free(ctx);
250 NDN_THROW(std::runtime_error("Cannot initialize the encryption operation when calling EVP_EncryptInit_ex()"));
251 }
252 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
253 EVP_CIPHER_CTX_free(ctx);
254 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()"));
255 }
256 if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
257 EVP_CIPHER_CTX_free(ctx);
258 NDN_THROW(std::runtime_error("Cannot initialize key and IV when calling EVP_EncryptInit_ex()"));
259 }
260 if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
261 EVP_CIPHER_CTX_free(ctx);
262 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
263 }
264 if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintextLen)) {
265 EVP_CIPHER_CTX_free(ctx);
266 NDN_THROW(std::runtime_error("Cannot encrypt when calling EVP_EncryptUpdate()"));
267 }
268 ciphertextLen = len;
269 if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
270 EVP_CIPHER_CTX_free(ctx);
271 NDN_THROW(std::runtime_error("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()"));
272 }
273 ciphertextLen += len;
274 if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
275 EVP_CIPHER_CTX_free(ctx);
276 NDN_THROW(std::runtime_error("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()"));
277 }
278 EVP_CIPHER_CTX_free(ctx);
279 return ciphertextLen;
280}
281
282size_t
283aesGcm128Decrypt(const uint8_t* ciphertext, size_t ciphertextLen, const uint8_t* associated, size_t associatedLen,
284 const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
285{
286 EVP_CIPHER_CTX* ctx;
287 int len;
288 size_t plaintextLen;
289 if (!(ctx = EVP_CIPHER_CTX_new())) {
290 NDN_THROW(std::runtime_error("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()"));
291 }
292 if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
293 EVP_CIPHER_CTX_free(ctx);
294 NDN_THROW(std::runtime_error("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()"));
295 }
296 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
297 EVP_CIPHER_CTX_free(ctx);
298 NDN_THROW(std::runtime_error("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl"));
299 }
300 if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
301 EVP_CIPHER_CTX_free(ctx);
302 NDN_THROW(std::runtime_error("Cannot initialise key and IV when calling EVP_DecryptInit_ex()"));
303 }
304 if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associatedLen)) {
305 EVP_CIPHER_CTX_free(ctx);
306 NDN_THROW(std::runtime_error("Cannot set associated authentication data when calling EVP_EncryptUpdate()"));
307 }
308 if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertextLen)) {
309 EVP_CIPHER_CTX_free(ctx);
310 NDN_THROW(std::runtime_error("Cannot decrypt when calling EVP_DecryptUpdate()"));
311 }
312 plaintextLen = len;
313 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, const_cast<void*>(reinterpret_cast<const void*>(tag)))) {
314 EVP_CIPHER_CTX_free(ctx);
315 NDN_THROW(std::runtime_error("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl()"));
316 }
317 auto ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
318 EVP_CIPHER_CTX_free(ctx);
319 if (ret > 0) {
320 plaintextLen += len;
321 return plaintextLen;
322 }
323 else {
324 NDN_THROW(std::runtime_error("Cannot finalize the decryption when calling EVP_DecryptFinal_ex()"));
325 }
326}
327
328Block
329encodeBlockWithAesGcm128(uint32_t tlvType, const uint8_t* key, const uint8_t* payload, size_t payloadSize,
330 const uint8_t* associatedData, size_t associatedDataSize, uint32_t& counter)
331{
332 Buffer iv(12);
333 random::generateSecureBytes(iv.data(), 8);
334 if (tlvType == ndn::tlv::ApplicationParameters) {
335 // requester
336 iv[0] &= ~(1UL << 7);
337 }
338 else {
339 // CA
340 iv[0] |= 1UL << 7;
341 }
342 uint32_t temp = boost::endian::native_to_big(counter);
343 std::memcpy(&iv[8], reinterpret_cast<const uint8_t*>(&temp), 4);
344 uint32_t increment = (payloadSize + 15) / 16;
345 if (std::numeric_limits<uint32_t>::max() - counter < increment) {
346 NDN_THROW(std::runtime_error("Error incrementing the AES block counter: "
347 "too many blocks have been encrypted for the same request instance"));
348 }
349 else {
350 counter += increment;
351 }
352
353 // The spec of AES encrypted payload TLV used in NDNCERT:
354 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
355 Buffer encryptedPayload(payloadSize);
356 uint8_t tag[16];
357 size_t encryptedPayloadLen = aesGcm128Encrypt(payload, payloadSize, associatedData, associatedDataSize,
358 key, iv.data(), encryptedPayload.data(), tag);
359 auto content = makeEmptyBlock(tlvType);
360 content.push_back(makeBinaryBlock(tlv::InitializationVector, iv.data(), iv.size()));
361 content.push_back(makeBinaryBlock(tlv::AuthenticationTag, tag, 16));
362 content.push_back(makeBinaryBlock(tlv::EncryptedPayload, encryptedPayload.data(), encryptedPayloadLen));
363 content.encode();
364 return content;
365}
366
367Buffer
368decodeBlockWithAesGcm128(const Block& block, const uint8_t* key, const uint8_t* associatedData, size_t associatedDataSize)
369{
370 // The spec of AES encrypted payload TLV used in NDNCERT:
371 // https://github.com/named-data/ndncert/wiki/NDNCERT-Protocol-0.3#242-aes-gcm-encryption
372 block.parse();
373 const auto& encryptedPayloadBlock = block.get(tlv::EncryptedPayload);
374 Buffer result(encryptedPayloadBlock.value_size());
375 auto resultLen = aesGcm128Decrypt(encryptedPayloadBlock.value(), encryptedPayloadBlock.value_size(),
376 associatedData, associatedDataSize, block.get(tlv::AuthenticationTag).value(),
377 key, block.get(tlv::InitializationVector).value(), result.data());
378 if (resultLen != encryptedPayloadBlock.value_size()) {
379 NDN_THROW(std::runtime_error("Error when decrypting the AES Encrypted Block: "
380 "Decrypted payload is of an unexpected size"));
381 }
382 return result;
383}
384
385} // namespace ndncert
386} // namespace ndn