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.
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.
/* * 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; }
# 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()