crypto-helper: add authenticated GCM 128 encryption
Change-Id: I8bf0bdd25658e076a9f732f836bc7b07a767fec9
diff --git a/src/crypto-support/crypto-helper.cpp b/src/crypto-support/crypto-helper.cpp
index 0792bfc..297bbef 100644
--- a/src/crypto-support/crypto-helper.cpp
+++ b/src/crypto-support/crypto-helper.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2017-2019, Regents of the University of California.
+/**
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -259,6 +259,127 @@
return done_len;
}
+int
+aes_gcm_128_encrypt(const uint8_t* plaintext, size_t plaintext_len, const uint8_t* associated, size_t associated_len,
+ const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag)
+{
+ EVP_CIPHER_CTX *ctx;
+ int len;
+ int ciphertext_len;
+
+ // Create and initialise the context
+ if (!(ctx = EVP_CIPHER_CTX_new())) {
+ handleErrors("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()");
+ }
+
+ // Initialise the encryption operation.
+ if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
+ handleErrors("Cannot initialise the encryption operation when calling EVP_EncryptInit_ex()");
+ }
+
+ // Set IV length if default 12 bytes (96 bits) is not appropriate
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
+ handleErrors("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl()");
+ }
+
+ // Initialise key and IV
+ if (1 != EVP_EncryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
+ handleErrors("Cannot initialize key and IV when calling EVP_EncryptInit_ex()");
+ }
+
+ // Provide any AAD data. This can be called zero or more times as required
+ if (1 != EVP_EncryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
+ handleErrors("Cannot set associated authentication data when calling EVP_EncryptUpdate()");
+ }
+
+ // Provide the message to be encrypted, and obtain the encrypted output.
+ // EVP_EncryptUpdate can be called multiple times if necessary
+ if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
+ handleErrors("Cannot encrypt when calling EVP_EncryptUpdate()");
+ }
+ ciphertext_len = len;
+
+ // Finalise the encryption. Normally ciphertext bytes may be written at
+ // this stage, but this does not occur in GCM mode
+ if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
+ handleErrors("Cannot finalise the encryption when calling EVP_EncryptFinal_ex()");
+ }
+ ciphertext_len += len;
+
+ // Get the tag
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
+ handleErrors("Cannot get tag when calling EVP_CIPHER_CTX_ctrl()");
+ }
+
+ // Clean up
+ EVP_CIPHER_CTX_free(ctx);
+ return ciphertext_len;
+}
+
+int
+aes_gcm_128_decrypt(const uint8_t* ciphertext, size_t ciphertext_len, const uint8_t* associated, size_t associated_len,
+ const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext)
+{
+ EVP_CIPHER_CTX* ctx;
+ int len;
+ int plaintext_len;
+ int ret;
+
+ // Create and initialise the context
+ if (!(ctx = EVP_CIPHER_CTX_new())) {
+ handleErrors("Cannot create and initialise the context when calling EVP_CIPHER_CTX_new()");
+ }
+
+ // Initialise the decryption operation.
+ if (!EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), nullptr, nullptr, nullptr)) {
+ handleErrors("Cannot initialise the decryption operation when calling EVP_DecryptInit_ex()");
+ }
+
+ // Set IV length. Not necessary if this is 12 bytes (96 bits)
+ if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, nullptr)) {
+ handleErrors("Cannot set IV length when calling EVP_CIPHER_CTX_ctrl");
+ }
+
+ // Initialise key and IV
+ if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, key, iv)) {
+ handleErrors("Cannot initialise key and IV when calling EVP_DecryptInit_ex()");
+ }
+
+ // Provide any AAD data. This can be called zero or more times as required
+ if (!EVP_DecryptUpdate(ctx, nullptr, &len, associated, associated_len)) {
+ handleErrors("Cannot set associated authentication data when calling EVP_EncryptUpdate()");
+ }
+
+ // Provide the message to be decrypted, and obtain the plaintext output.
+ // EVP_DecryptUpdate can be called multiple times if necessary
+ if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
+ handleErrors("Cannot decrypt when calling EVP_DecryptUpdate()");
+ }
+ plaintext_len = len;
+
+ // Set expected tag value. Works in OpenSSL 1.0.1d and later
+ if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag)) {
+ handleErrors("Cannot set tag value when calling EVP_CIPHER_CTX_ctrl");
+ }
+
+ // Finalise the decryption. A positive return value indicates success,
+ // anything else is a failure - the plaintext is not trustworthy.
+ ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
+
+ // Clean up
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (ret > 0) {
+ // Success
+ plaintext_len += len;
+ return plaintext_len;
+ }
+ else {
+ // Verify failed
+ return -1;
+ }
+}
+
void
handleErrors(const std::string& errorInfo)
{
diff --git a/src/crypto-support/crypto-helper.hpp b/src/crypto-support/crypto-helper.hpp
index 7bf6572..f75396b 100644
--- a/src/crypto-support/crypto-helper.hpp
+++ b/src/crypto-support/crypto-helper.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -55,7 +55,6 @@
uint8_t*
deriveSecret(const std::string& peerKeyStr);
- //unique_ptr<ECDH_CTX_T> context;
unique_ptr<ECDH_CTX> context;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
@@ -71,9 +70,44 @@
int saltLen, uint8_t* okm, int okm_len,
const uint8_t* info=INFO, int info_len=INFO_LEN);
-int ndn_compute_hmac_sha256 (const uint8_t *data, const unsigned data_length,
- const uint8_t *key, const unsigned key_length,
- uint8_t *prk);
+int
+ndn_compute_hmac_sha256(const uint8_t *data, const unsigned data_length,
+ const uint8_t *key, const unsigned key_length,
+ uint8_t *prk);
+
+/**
+ * Authentication GCM 128 Encryption
+ * @p plaintext, input, plaintext
+ * @p plaintext_len, input, size of plaintext
+ * @p associated, input, associated authentication data
+ * @p associated_len, input, size of associated authentication data
+ * @p key, input, 16 bytes AES key
+ * @p iv, input, 12 bytes IV
+ * @p ciphertext, output
+ * @p tag, output, 16 bytes tag
+ * @return the size of ciphertext
+ * @throw CryptoError when there is an error in the process of encryption
+ */
+int
+aes_gcm_128_encrypt(const uint8_t* plaintext, size_t plaintext_len, const uint8_t* associated, size_t associated_len,
+ const uint8_t* key, const uint8_t* iv, uint8_t* ciphertext, uint8_t* tag);
+
+/**
+ * Authentication GCM 128 Decryption
+ * @p ciphertext, input, ciphertext
+ * @p ciphertext_len, input, size of ciphertext
+ * @p associated, input, associated authentication data
+ * @p associated_len, input, size of associated authentication data
+ * @p tag, input, 16 bytes tag
+ * @p key, input, 16 bytes AES key
+ * @p iv, input, 12 bytes IV
+ * @p plaintext, output
+ * @return the size of plaintext or -1 if the verification fails
+ * @throw CryptoError when there is an error in the process of encryption
+ */
+int
+aes_gcm_128_decrypt(const uint8_t* ciphertext, size_t ciphertext_len, const uint8_t* associated, size_t associated_len,
+ const uint8_t* tag, const uint8_t* key, const uint8_t* iv, uint8_t* plaintext);
void
handleErrors(const std::string& errorInfo);
diff --git a/tests/unit-tests/crypto-helper.t.cpp b/tests/unit-tests/crypto-helper.t.cpp
index b6f8bd3..8f8d7c0 100644
--- a/tests/unit-tests/crypto-helper.t.cpp
+++ b/tests/unit-tests/crypto-helper.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -187,6 +187,113 @@
expected + sizeof(expected));
}
+BOOST_AUTO_TEST_CASE(AesGcm1)
+{
+ // Test case from NIST Cryptographic Algorithm Validation Program
+ // https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/CAVP-TESTING-BLOCK-CIPHER-MODES
+ // Count = 0
+ // Key = cf063a34d4a9a76c2c86787d3f96db71
+ // IV = 113b9785971864c83b01c787
+ // CT =
+ // AAD =
+ // Tag = 72ac8493e3a5228b5d130a69d2510e42
+ // PT =
+ const uint8_t key[] = {0xcf, 0x06, 0x3a, 0x34, 0xd4, 0xa9, 0xa7, 0x6c,
+ 0x2c, 0x86, 0x78, 0x7d, 0x3f, 0x96, 0xdb, 0x71};
+ const uint8_t iv[] = {0x11, 0x3b, 0x97, 0x85, 0x97, 0x18,
+ 0x64, 0xc8, 0x3b, 0x01, 0xc7, 0x87};
+ const uint8_t expected_tag[] = {0x72, 0xac, 0x84, 0x93, 0xe3, 0xa5,
+ 0x22, 0x8b, 0x5d, 0x13, 0x0a, 0x69,
+ 0xd2, 0x51, 0x0e, 0x42};
+
+ uint8_t ciphertext[256] = {0};
+ uint8_t tag[16] = {0};
+ int size = aes_gcm_128_encrypt(nullptr, 0, nullptr, 0, key, iv, ciphertext, tag);
+ BOOST_CHECK(size == 0);
+ BOOST_CHECK_EQUAL_COLLECTIONS(tag, tag + 16, expected_tag, expected_tag + sizeof(expected_tag));
+
+ uint8_t decrypted[256] = {0};
+ size = aes_gcm_128_decrypt(ciphertext, size, nullptr, 0, tag, key, iv, decrypted);
+ BOOST_CHECK(size == 0);
+}
+
+BOOST_AUTO_TEST_CASE(AesGcm2)
+{
+ // Test case from NIST Cryptographic Algorithm Validation Program
+ // https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/CAVP-TESTING-BLOCK-CIPHER-MODES
+ // Count = 1
+ // Key = 2370e320d4344208e0ff5683f243b213
+ // IV = 04dbb82f044d30831c441228
+ // CT =
+ // AAD = d43a8e5089eea0d026c03a85178b27da
+ // Tag = 2a049c049d25aa95969b451d93c31c6e
+ // PT =
+ const uint8_t key[] = {0x23, 0x70, 0xe3, 0x20, 0xd4, 0x34, 0x42, 0x08,
+ 0xe0, 0xff, 0x56, 0x83, 0xf2, 0x43, 0xb2, 0x13};
+ const uint8_t iv[] = {0x04, 0xdb, 0xb8, 0x2f, 0x04, 0x4d,
+ 0x30, 0x83, 0x1c, 0x44, 0x12, 0x28};
+ const uint8_t aad[] = {0xd4, 0x3a, 0x8e, 0x50, 0x89, 0xee, 0xa0, 0xd0,
+ 0x26, 0xc0, 0x3a, 0x85, 0x17, 0x8b, 0x27, 0xda};
+ const uint8_t expected_tag[] = {0x2a, 0x04, 0x9c, 0x04, 0x9d, 0x25,
+ 0xaa, 0x95, 0x96, 0x9b, 0x45, 0x1d,
+ 0x93, 0xc3, 0x1c, 0x6e};
+
+ uint8_t ciphertext[256] = {0};
+ uint8_t tag[16] = {0};
+ int size = aes_gcm_128_encrypt(nullptr, 0, aad, sizeof(aad), key, iv, ciphertext, tag);
+ BOOST_CHECK(size == 0);
+ BOOST_CHECK_EQUAL_COLLECTIONS(tag, tag + 16, expected_tag, expected_tag + sizeof(expected_tag));
+
+ uint8_t decrypted[256] = {0};
+ size = aes_gcm_128_decrypt(ciphertext, size, aad, sizeof(aad), tag, key, iv, decrypted);
+ BOOST_CHECK(size == 0);
+}
+
+BOOST_AUTO_TEST_CASE(AesGcm3)
+{
+ // Test case from NIST Cryptographic Algorithm Validation Program
+ // https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/CAVP-TESTING-BLOCK-CIPHER-MODES
+ // Count = 0
+ // Key = bc22f3f05cc40db9311e4192966fee92
+ // IV = 134988e662343c06d3ab83db
+ // CT = 4c0168ab95d3a10ef25e5924108389365c67d97778995892d9fd46897384af61fc559212b3267e90fe4df7bfd1fbed46f4b9ee
+ // AAD = 10087e6ed81049b509c31d12fee88c64
+ // Tag = 771357958a316f166bd0dacc98ea801a
+ // PT = 337c1bc992386cf0f957617fe4d5ec1218ae1cc40369305518eb177e9b15c1646b142ff71237efaa58790080cd82e8848b295c
+ const uint8_t key[] = {0xbc, 0x22, 0xf3, 0xf0, 0x5c, 0xc4, 0x0d, 0xb9,
+ 0x31, 0x1e, 0x41, 0x92, 0x96, 0x6f, 0xee, 0x92};
+ const uint8_t iv[] = {0x13, 0x49, 0x88, 0xe6, 0x62, 0x34,
+ 0x3c, 0x06, 0xd3, 0xab, 0x83, 0xdb};
+ const uint8_t aad[] = {0x10, 0x08, 0x7e, 0x6e, 0xd8, 0x10, 0x49, 0xb5,
+ 0x09, 0xc3, 0x1d, 0x12, 0xfe, 0xe8, 0x8c, 0x64};
+ const uint8_t expected_ciphertext[] = {
+ 0x4c, 0x01, 0x68, 0xab, 0x95, 0xd3, 0xa1, 0x0e, 0xf2, 0x5e, 0x59,
+ 0x24, 0x10, 0x83, 0x89, 0x36, 0x5c, 0x67, 0xd9, 0x77, 0x78, 0x99,
+ 0x58, 0x92, 0xd9, 0xfd, 0x46, 0x89, 0x73, 0x84, 0xaf, 0x61, 0xfc,
+ 0x55, 0x92, 0x12, 0xb3, 0x26, 0x7e, 0x90, 0xfe, 0x4d, 0xf7, 0xbf,
+ 0xd1, 0xfb, 0xed, 0x46, 0xf4, 0xb9, 0xee};
+ const uint8_t expected_tag[] = {0x77, 0x13, 0x57, 0x95, 0x8a, 0x31,
+ 0x6f, 0x16, 0x6b, 0xd0, 0xda, 0xcc,
+ 0x98, 0xea, 0x80, 0x1a};
+ const uint8_t plaintext[] = {
+ 0x33, 0x7c, 0x1b, 0xc9, 0x92, 0x38, 0x6c, 0xf0, 0xf9, 0x57, 0x61,
+ 0x7f, 0xe4, 0xd5, 0xec, 0x12, 0x18, 0xae, 0x1c, 0xc4, 0x03, 0x69,
+ 0x30, 0x55, 0x18, 0xeb, 0x17, 0x7e, 0x9b, 0x15, 0xc1, 0x64, 0x6b,
+ 0x14, 0x2f, 0xf7, 0x12, 0x37, 0xef, 0xaa, 0x58, 0x79, 0x00, 0x80,
+ 0xcd, 0x82, 0xe8, 0x84, 0x8b, 0x29, 0x5c};
+
+ uint8_t ciphertext[256] = {0};
+ uint8_t tag[16] = {0};
+ int size = aes_gcm_128_encrypt(plaintext, sizeof(plaintext), aad, sizeof(aad), key, iv, ciphertext, tag);
+ BOOST_CHECK_EQUAL_COLLECTIONS(ciphertext, ciphertext + size,
+ expected_ciphertext, expected_ciphertext + sizeof(expected_ciphertext));
+ BOOST_CHECK_EQUAL_COLLECTIONS(tag, tag + 16, expected_tag, expected_tag + sizeof(expected_tag));
+
+ uint8_t decrypted[256] = {0};
+ size = aes_gcm_128_decrypt(ciphertext, size, aad, sizeof(aad), tag, key, iv, decrypted);
+ BOOST_CHECK(memcmp(decrypted, plaintext, sizeof(plaintext)) == 0);
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests