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(&params[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