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);