AES GCM. EVP_EncryptInit_ex2() if iv == NULL should clear PROV_GCM_CTX::iv_state to IV_STATE_UNINITIALISED
Dmitriy Shumaev
tmp4login at gmail.com
Thu Oct 19 22:39:03 UTC 2023
Hi there.
I'm new to openssl. I wrote a simple test application based on a demo.
I use AES in GCM mode with IV generated internally.
And I'm trying to reuse EVP_CIPHER_CTX - I call EVP_CIPHER_CTX_new() only
twice for encoder and decoder operations on the same key.
Each encode operation starts with EVP_EncryptInit_ex2(ctx_enc_, 0, 0, 0, 0);
The first encode|decode operations work well.
But second encode fails on EVP_EncryptUpdate(ctx_enc_, 0, ...) (set AAD)
with error: "AC360000:error:1C800066:Provider
routines:ossl_gcm_stream_update:cipher operation
failed:providers\implementations\ciphers\ciphercommon_gcm.c:320:"
I think it is all because of EVP_EncryptInit_ex2(ctx_enc_, 0, 0, 0, 0);
does NOT clear PROV_GCM_CTX::iv_state to IV_STATE_UNINITIALISED. It stays
IV_STATE_FINISHED (just a thought, not checked as I use openssl as a lib).
So the questions are:
- Is it an error that EVP_EncryptInit_ex2(ctx_enc_, 0, 0, 0, 0) does NOT
clear PROV_GCM_CTX::iv_state?
- How can I clear PROV_GCM_CTX::iv_state manually?
Thanks in advance.
Shumaev D.A.
<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
Никаких
вирусов.www.avast.com
<https://www.avast.com/sig-email?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail>
<#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mta.openssl.org/pipermail/openssl-users/attachments/20231020/96abd98c/attachment.htm>
-------------- next part --------------
/*
* This project is based on openssl example [openssl-3.1.3\demos\cipher\aesgcm.c]
*/
#include "TestOpenSSL.h"
#include <openssl/core_names.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/applink.c> // BIO_dump_fp(3)
#include <stdio.h>
// select cipher mode
#define CIPHER_MODE_GCM
#if defined(CIPHER_MODE_CBC)
#define CIPHER_MODE "AES-256-CBC"
#define IV_SIZE sizeof(iv_)
#define IV iv_
#define AAD_SIZE 0
#define AAD nullptr
#define AAD2_SIZE 0
#define AAD2 nullptr
#define TAG nullptr
#elif defined(CIPHER_MODE_GCM)
#define CIPHER_MODE "AES-256-GCM"
#define IV_SIZE 0
#define IV iv_get_gen
#define AAD_SIZE sizeof(aad_)
#define AAD aad_
#define AAD2_SIZE sizeof(aad2_)
#define AAD2 aad2_
#define TAG tag
#else
#error "Wrong cipher mode in #define CIPHER_MODE_XXX"
#endif
/* AES key */
static const unsigned char key_[] = {
0xee, 0xbc, 0x1f, 0x57, 0x48, 0x7f, 0x51, 0x92, 0x1c, 0x04, 0x65, 0x66,
0x5f, 0x8a, 0xe6, 0xd1, 0x65, 0x8b, 0xb2, 0x6d, 0xe6, 0xf8, 0xa0, 0x69,
0xa3, 0x52, 0x02, 0x93, 0xa5, 0x72, 0x07, 0x8f
};
/* Unique initialisation vector */
static unsigned char iv_[EVP_MAX_IV_LENGTH] = {
0x99, 0xaa, 0x3e, 0x68, 0xed, 0x81, 0x73, 0xa0, 0xee, 0xd0, 0x66, 0x84
};
/* Example plaintext to encrypt */
static const unsigned char pt_[] = {
0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xeb, 0x31, 0xb2, 0xea,
0xcc, 0x2b, 0xf2, 0xa5, 0xcb, 0xae, 0x35, 0x17, 0x00
};
// pt2_ = pt_ except for LS bit in 8th byte
static const unsigned char pt2_[] = {
0xf5, 0x6e, 0x87, 0x05, 0x5b, 0xc3, 0x2d, 0x0e, 0xe8, 0x31, 0xb2, 0xea,
0xcc, 0x2b, 0xf2, 0xa5, 0xcb, 0xae, 0x35, 0x17, 0x00
};
#ifdef CIPHER_MODE_CBC
// Expected ciphertext value
static const unsigned char ct_cbc[] = {
0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e,
0xb9, 0xf2, 0x17, 0x36
};
#endif
#ifdef CIPHER_MODE_GCM
// Expected ciphertext value
static const unsigned char ct_gcm[] = {
0xf7, 0x26, 0x44, 0x13, 0xa8, 0x4c, 0x0e, 0x7c, 0xd5, 0x36, 0x86, 0x7e,
0xb9, 0xf2, 0x17, 0x36
};
// Example of Additional Authenticated Data (AAD), i.e. unencrypted data
// which can be authenticated using the generated Tag value.
static const unsigned char aad_[] = {
0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43,
0x7f, 0xec, 0x78, 0xde
};
// tag2_ = tag_ except for 1 LS bit
static const unsigned char aad2_[] = {
0x4d, 0x23, 0xc3, 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43,
0x7f, 0xec, 0x78, 0xdf
};
// Expected AEAD Tag value
static const unsigned char tag_[] = {
0x67, 0xba, 0x05, 0x10, 0x26, 0x2a, 0xe4, 0x87, 0xd7, 0x37, 0xee, 0x62,
0x98, 0xf7, 0x7e, 0x0c
};
#endif
OSSL_LIB_CTX * libctx_ = nullptr;
const char * propq_ = nullptr;
EVP_CIPHER_CTX * ctx_enc_ = nullptr;
EVP_CIPHER_CTX * ctx_dec_ = nullptr;
EVP_CIPHER * cipher_ = nullptr;
bool init_done_ = false;
int clean (void);
int init (size_t key_size, const unsigned char * key)
{
int ret = 0;
// try init (ctx, cipher, encrypt operation)
do {
if ( ! key) {
ret = 1;
break;
}
if (init_done_) {
ret = 2;
break;
}
if (nullptr == (ctx_enc_ = EVP_CIPHER_CTX_new())) {
ret = 11;
break;
}
if (nullptr == (ctx_dec_ = EVP_CIPHER_CTX_new())) {
ret = 12;
break;
}
if (nullptr == (cipher_ = EVP_CIPHER_fetch(libctx_, CIPHER_MODE, propq_))) {
ret = 15;
break;
}
if (key_size != EVP_CIPHER_get_key_length(cipher_)) {
ret = 1;
break;
}
if ( ! EVP_EncryptInit_ex2(ctx_enc_, cipher_, key, nullptr, nullptr)) {
ret = 17;
break;
}
if ( ! EVP_DecryptInit_ex2(ctx_dec_, cipher_, key, nullptr, nullptr)) {
ret = 18;
break;
}
} while (false);
if (ret) {
if (ret != 2) {
clean();
}
printf("Init error = %d with %s mode\n", ret, CIPHER_MODE);
ERR_print_errors_fp(stderr);
} else {
init_done_ = true;
printf("Init successful with %s mode\n", CIPHER_MODE);
}
return ret;
}
int clean (void)
{
init_done_ = false;
EVP_CIPHER_free(cipher_);
cipher_ = nullptr;
EVP_CIPHER_CTX_free(ctx_enc_);
ctx_enc_ = nullptr;
EVP_CIPHER_CTX_free(ctx_dec_);
ctx_dec_ = nullptr;
printf("Cleanup done\n");
return 0;
}
// If (iv_size && *iv_size == 0) then IV MUST be generated internally by cipher algorithm realization.
// In this case iv MUST point to memory block with size at least = EVP_MAX_IV_LENGTH.
int encrypt (size_t pt_size, const unsigned char * pt,
size_t * iv_size, unsigned char * iv,
size_t aad_size, const unsigned char * aad,
size_t * enc_size, unsigned char * enc,
size_t tag_size, unsigned char * tag)
{
int ret = 0;
int enc_len = 0, tmp_len;
OSSL_PARAM params [] = {OSSL_PARAM_END, OSSL_PARAM_END};
unsigned char * iv_enc_init = nullptr;
// try to encrypt
do {
if ( ! init_done_ || ! pt || ! enc || ! enc_size || *enc_size < pt_size + EVP_MAX_BLOCK_LENGTH || ! iv || ! iv_size || ( ! aad && tag) || (aad && ! tag)) {
ret = 1;
break;
}
// reuse already inited ctx_
// "For EVP_CIPH_GCM_MODE the IV will be generated internally if it is not specified."
if (iv_size && *iv_size != 0) {
params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, iv_size);
iv_enc_init = iv;
}
if ( ! EVP_EncryptInit_ex2(ctx_enc_, nullptr, nullptr, iv_enc_init, nullptr)) {///params)) {
ret = 17;
break;
}
// AAD
if (aad) {
if ( ! EVP_EncryptUpdate(ctx_enc_, nullptr, &enc_len, aad, aad_size)) {
ret = 19;
break;
}
}
// Enc
if ( ! EVP_EncryptUpdate(ctx_enc_, enc, &enc_len, pt, pt_size)) {
ret = 21;
break;
}
// Finish enc
if ( ! EVP_EncryptFinal_ex(ctx_enc_, enc + enc_len, &tmp_len)) {
ret = 23;
break;
}
enc_len += tmp_len;
// Get tag
if (tag) {
params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, tag, tag_size);
if ( ! EVP_CIPHER_CTX_get_params(ctx_enc_, params)) {
ret = 25;
break;
}
}
// Get IV
if (iv_size && *iv_size == 0) {
*iv_size = EVP_MAX_IV_LENGTH; // IV buffer size
params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_IV, iv, *iv_size);
if ( ! EVP_CIPHER_CTX_get_params(ctx_enc_, params)) {
ret = 25;
break;
}
*iv_size = EVP_CIPHER_CTX_get_iv_length(ctx_enc_); // actual IV size got
}
} while (false);
// print result
if (ret) {
printf("Encrypt error = %d\n", ret);
ERR_print_errors_fp(stderr);///std::string str_err = getOpenSSLError();
} else {
*enc_size = enc_len;
printf("AES GCM Encrypt:\n");
printf("Plaintext:\n");
BIO_dump_fp(stdout, pt, pt_size);
printf("IV:\n");
BIO_dump_fp(stdout, iv, *iv_size);
printf("Ciphertext:\n");
BIO_dump_fp(stdout, enc, *enc_size);
if (tag) {
printf("AAD:\n");
BIO_dump_fp(stdout, aad, aad_size);
printf("Tag:\n");
BIO_dump_fp(stdout, tag, tag_size);
}
}
return ret;
}
int decrypt (size_t enc_size, const unsigned char * enc,
size_t iv_size, unsigned char * iv,
size_t aad_size, const unsigned char * aad,
size_t * dec_size, unsigned char * dec,
size_t tag_size, unsigned char * tag)
{
int ret = 0;
int dec_len = 0, tmp_len;
OSSL_PARAM params [] = {OSSL_PARAM_END, OSSL_PARAM_END};
int final_res;///gcm_tag_verify;///authenticated; // For GCM mode it is also the result of tag verification (authenticationtop)
// try to decrypt
do {
if ( ! init_done_ || ! enc || ! dec || ! dec_size || *dec_size < enc_size || ! iv || iv_size == 0 || ( ! aad && tag) || (aad && ! tag)) {
ret = 1;
break;
}
// reuse already inited ctx_
//params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, &iv_size);
if ( ! EVP_DecryptInit_ex2(ctx_dec_, nullptr, nullptr, iv, nullptr)) {/// params)) {
ret = 18;
break;
}
// AAD
if (aad) {
if ( ! EVP_DecryptUpdate(ctx_dec_, nullptr, &dec_len, aad, aad_size)) {
ret = 20;
break;
}
}
// Dec
if ( ! EVP_DecryptUpdate(ctx_dec_, dec, &dec_len, enc, enc_size)) {
ret = 22;
break;
}
// Set expected tag value
if (tag) {
params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, tag, tag_size);
if ( ! EVP_CIPHER_CTX_set_params(ctx_dec_, params)) {
ret = 26;
break;
}
if (1 == OSSL_PARAM_modified(¶ms[0])) {
ret = 26;
break;
}
}
// Finish dec (for GCM it also validates tag)
final_res = EVP_DecryptFinal_ex(ctx_dec_, dec + dec_len, &tmp_len);
if (final_res <= 0) {
ret = 24;
break;
}
dec_len += tmp_len;
} while (false);
// print result
if (ret) {
printf("Decrypt error = %d\n", ret);
ERR_print_errors_fp(stderr);///std::string str_err = getOpenSSLError();
} else {
*dec_size = dec_len;
printf("AES GCM Decrypt:\n");
printf("Ciphertext:\n");
BIO_dump_fp(stdout, enc, enc_size);
printf("IV:\n");
BIO_dump_fp(stdout, iv, iv_size);
printf("Plaintext:\n");
BIO_dump_fp(stdout, dec, *dec_size);
if (tag) {
printf("AAD:\n");
BIO_dump_fp(stdout, aad, aad_size);
printf("Tag:\n");
BIO_dump_fp(stdout, tag, tag_size);
}
}
return ret;
}
int main (int argc, char ** argv)
{
int ret;
size_t iv_size = IV_SIZE;
unsigned char iv_get_gen[EVP_MAX_IV_LENGTH];
size_t enc_size = 1024, enc_size2 = 2014;
unsigned char enc[1024];
unsigned char enc2[1024];
size_t dec_size = 1024;
unsigned char dec[1024];
unsigned char tag[16];
do {
ret = init(sizeof(key_), key_);
if (ret)
break;
ret = encrypt(sizeof(pt_), pt_, &iv_size, IV, AAD_SIZE, AAD, &enc_size, enc, 16, TAG);
if (ret)
break;
ret = decrypt(enc_size, enc, iv_size, IV, AAD_SIZE, AAD, &dec_size, dec, 16, TAG);
if (ret)
break;
iv_size = IV_SIZE;
ret = encrypt(sizeof(pt_), pt_, &iv_size, IV, AAD_SIZE, AAD, &enc_size2, enc2, 16, TAG);
if (ret)
break;
} while (false);
clean();
return 0;
}
-------------- next part --------------
# CMake version 3.18 supports openssl3
cmake_minimum_required (VERSION 3.18)
add_executable (TestOpenSSL "TestOpenSSL.cpp" "TestOpenSSL.h")
# use openssl lib
# > cmake -–help-module-list # will provide all supported packages to find.
# > cmake --help-module FindOpenSSL # will provide all variables and
message (STATUS "Looking for openssl ...")
#set(OPENSSL_ROOT_DIR "C:/Qt/Tools/OpenSSL-v313-x64")
####find_package (OpenSSL REQUIRED PATHS C:/Qt/Tools/OpenSSL-v313-x64/)
find_package (OpenSSL 3.0 REQUIRED)
if (${OPENSSL_FOUND})
message (STATUS "version = " ${OPENSSL_VERSION})
message (STATUS "include dir = " ${OPENSSL_INCLUDE_DIR})
message (STATUS "crypto lib = " ${OPENSSL_CRYPTO_LIBRARY})
message (STATUS "crypto libs = " ${OPENSSL_CRYPTO_LIBRARIES})
message (STATUS "ssl lib = " ${OPENSSL_SSL_LIBRARY})
message (STATUS "ssl libs = " ${OPENSSL_SSL_LIBRARIES})
message (STATUS "openssl libs = " ${OPENSSL_LIBRARIES})
if (${OPENSSL_VERSION} VERSION_LESS "3.0")
message (STATUS "openssl found, but old")
else ()
message (STATUS "openssl found")
endif ()
else ()
message (STATUS "openssl NOT found")
endif ()
# [https://www.programmersought.com/article/25754533664/]
# (include_directores or target_include_directories and add_definition and add_library(ies)) are not required as target_link_libraries does it
#include_directories(${OPENSSL_INCLUDE_DIR})
###target_link_libraries(TestOpenSSL ${OPENSSL_SSL_LIBRARY})
target_link_libraries(TestOpenSSL OpenSSL::SSL)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET TestOpenSSL PROPERTY CXX_STANDARD 20)
endif()
More information about the openssl-users
mailing list