From 9f171f9ecab551695b237014e657f147264110e6 Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Tue, 27 Oct 2026 22:34:19 +0100 Subject: [PATCH] Changes for different openssl versions #54 --- .gitignore | 1 + configure.ac | 28 +++++- include/afflib/afflib.h | 2 + include/afflib/afflib_i.h | 24 +++-- lib/Makefile.am | 1 - lib/afflib.cpp | 67 ++++++++++---- lib/afflib_pages.cpp | 4 +- lib/crypto.cpp | 186 +++++++++++++++++++++++++++++++------- lib/qemu/aes.h | 1 - lib/qemu/block-qcow.c | 76 +++++++++------- lib/qemu/block-qcow2.c | 76 +++++++++------- lib/s3.cpp | 4 +- lib/s3_glue.cpp | 8 +- m4/ax_openssl_version.m4 | 120 ++++++++++++++++++++++++ tools/affcompare.cpp | 2 - tools/affconvert.cpp | 48 +++++++--- tools/affcopy.cpp | 7 +- tools/affinfo.cpp | 58 ++++++++---- tools/affix.cpp | 2 - tools/affrecover.cpp | 2 - tools/affstats.cpp | 2 - 21 files changed, 536 insertions(+), 183 deletions(-) delete mode 100644 lib/qemu/aes.h create mode 100644 m4/ax_openssl_version.m4 diff --git a/.gitignore b/.gitignore index e1ee6e4..7dcc347 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ config.log config.status config.sub configure +INSTALL install-sh libtool ltmain.sh diff --git a/configure.ac b/configure.ac index 3abbaa2..ebb91dc 100644 --- a/configure.ac +++ b/configure.ac @@ -30,6 +30,8 @@ AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/ax_pthread.m4]) AX_PTHREAD() +m4_include([m4/ax_openssl_version.m4]) + if test x"${cross_compiling}" = "xno" ; then # Bring additional directories where things might be found into our # search path. I don't know why autoconf doesn't do this by default @@ -213,16 +215,37 @@ AM_CONDITIONAL(HAVE_PYTHON, test "$enable_python" = yes) ## Always check for headers, then libs, then functions ### -AC_CHECK_HEADERS([openssl/aes.h openssl/bio.h openssl/evp.h openssl/hmac.h openssl/md5.h openssl/rand.h openssl/rsa.h openssl/sha.h openssl/pem.h openssl/x509.h]) +AC_CHECK_HEADERS([openssl/opensslv.h openssl/bio.h openssl/evp.h openssl/hmac.h openssl/rand.h openssl/rsa.h openssl/pem.h openssl/x509.h]) AC_CHECK_LIB([crypto],[EVP_get_digestbyname],, AC_MSG_ERROR([OpenSSL developer library 'libssl-dev' or 'openssl-devel' not installed; cannot continue.])) AC_CHECK_LIB([md],[MD5]) # if libmd is available, get it -AC_CHECK_FUNCS([MD5 SHA1 AES_encrypt RAND_bytes des_read_pw_string EVP_read_pw_string EVP_MD_size]) AC_CHECK_FUNCS([PEM_read_bio_RSA_PUBKEY]) +dnl Detect the OpenSSL version in use, and whether the deprecated low-level +dnl MD5/SHA1/AES APIs are still visible in the installed headers. +dnl Works with OpenSSL 1.0.2, 1.1.1, 3.x and 4.x. +AX_OPENSSL_VERSION + +dnl The crypto code is written against the stable EVP API (EVP_MD_CTX, +dnl EVP_CIPHER_CTX), which is identical on OpenSSL 1.0.2, 1.1.1, 3.x and 4.x. +dnl Define HAVE_MD5 / HAVE_SHA1 / HAVE_AES_ENCRYPT -- previously derived from +dnl the now-deprecated low-level MD5/SHA1/AES symbols -- from probes of the +dnl EVP symbols instead. This keeps the build clean on OpenSSL 3.x and 4.x +dnl (including no-deprecated builds) and working on OpenSSL 1.0.2/1.1.1. +AC_CHECK_FUNCS([EVP_MD_CTX_new EVP_MD_CTX_free EVP_DigestInit_ex EVP_DigestUpdate EVP_DigestFinal_ex]) +AC_CHECK_FUNCS([EVP_CIPHER_CTX_new EVP_CIPHER_CTX_free EVP_EncryptInit_ex EVP_EncryptUpdate EVP_EncryptFinal_ex EVP_DecryptInit_ex EVP_DecryptUpdate EVP_DecryptFinal_ex EVP_CIPHER_CTX_set_padding]) +AC_CHECK_FUNCS([EVP_md5 EVP_sha1 EVP_sha256 EVP_aes_128_cbc EVP_aes_256_cbc EVP_aes_256_ecb RAND_bytes]) + +AS_IF([test "$ac_cv_func_EVP_md5" = "yes" && test "$ac_cv_func_EVP_MD_CTX_new" = "yes"], + [AC_DEFINE([HAVE_MD5],1,[Define to 1 if OpenSSL provides MD5 through the EVP API])]) +AS_IF([test "$ac_cv_func_EVP_sha1" = "yes" && test "$ac_cv_func_EVP_MD_CTX_new" = "yes"], + [AC_DEFINE([HAVE_SHA1],1,[Define to 1 if OpenSSL provides SHA1 through the EVP API])]) +AS_IF([test "$ac_cv_func_EVP_aes_256_cbc" = "yes" && test "$ac_cv_func_EVP_CIPHER_CTX_new" = "yes"], + [AC_DEFINE([HAVE_AES_ENCRYPT],1,[Define to 1 if OpenSSL provides AES-256 through the EVP API])]) + ################################################################ ## FUSE: Filesystem in Userspace AC_ARG_ENABLE(fuse, @@ -296,6 +319,7 @@ AC_MSG_NOTICE([PYTHON Bindings: ${enable_python}]) AC_MSG_NOTICE([QEMU Image Drivers: ${enable_qemu}]) AC_MSG_NOTICE([FUSE: ${enable_fuse}]) AC_MSG_NOTICE([LIBEXPAT: ${have_expat} (needed for AFF signatures)]) +AC_MSG_NOTICE([OpenSSL: ${OPENSSL_VERSION} (EVP-based digest and cipher APIs)]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([CFLAGS: ${CFLAGS}]) AC_MSG_NOTICE([CPPFLAGS: ${CPPFLAGS}]) diff --git a/include/afflib/afflib.h b/include/afflib/afflib.h index ef1a069..10bee5c 100644 --- a/include/afflib/afflib.h +++ b/include/afflib/afflib.h @@ -336,6 +336,8 @@ int af_display_as_hex(const char *segname); // afflib recommends displaying this /* Crypto */ /* AFF Base Encryption */ +int af_MD5(const unsigned char *buf,size_t buflen,unsigned char md[16]); // return 0 if success, -1 if no cipher +int af_sha1(const unsigned char *buf,size_t buflen,unsigned char md[20]); // return 0 if success, -1 if no cipher int af_SHA256(const unsigned char *buf,size_t buflen,unsigned char md[32]); // return 0 if success, -1 if no cipher int af_set_aes_key(AFFILE *af,const unsigned char *userKey,const int bits); int af_cannot_decrypt(AFFILE *af); // encrypted pages are present which cannot be decrypted diff --git a/include/afflib/afflib_i.h b/include/afflib/afflib_i.h index efa2019..1a7627f 100644 --- a/include/afflib/afflib_i.h +++ b/include/afflib/afflib_i.h @@ -105,11 +105,23 @@ void __cdecl AFDbgPrint (PCSTR Format,...); #endif #ifdef HAVE_LIBCRYPTO -#include +#include #include // a standard part of OpenSSL #include -#include -#include +#include // X509 is used by struct af_crypto +/* The AES block size (16 bytes). Formerly taken from ; + * that header is deprecated/removed in OpenSSL 3.x/4.x no-deprecated builds, + * so define it locally (AES blocks are always 16 bytes). + */ +#define AFF_AES_BLOCK_SIZE 16 + +/* Support OpenSSL before 1.1.0 (renamed create/destroy to new/free) */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define EVP_MD_CTX_new EVP_MD_CTX_create +#define EVP_MD_CTX_free EVP_MD_CTX_destroy +#define EVP_CIPHER_CTX_new EVP_CIPHER_CTX_create +#define EVP_CIPHER_CTX_free EVP_CIPHER_CTX_destroy +#endif #endif #ifdef HAVE_PTHREAD @@ -432,9 +444,9 @@ struct af_crypto { uint32_t sealing_key_set:1; // encryption key has been set uint32_t auto_encrypt:1; // encrypt segments when we write uint32_t auto_decrypt:1; // automatically decrypto when we read -#ifdef AES_BLOCK_SIZE - AES_KEY ekey; // encrypt key - AES_KEY dkey; // decrypt key +#ifdef HAVE_AES_ENCRYPT + EVP_CIPHER_CTX *ekey; // EVP cipher context holding the encryption key (AES-256-CBC); NULL until set + EVP_CIPHER_CTX *dkey; // EVP cipher context holding the decryption key (AES-256-CBC); NULL until set #endif #ifdef HAVE_OPENSSL_EVP_H EVP_PKEY *sign_privkey; // signing private key (to write signatures) diff --git a/lib/Makefile.am b/lib/Makefile.am index 6f6de4f..e9b9be8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -28,7 +28,6 @@ libafflib_la_SOURCES = $(AFFLIB_SOURCES) QEMU_SOURCES = \ vnode_qemu.cpp \ vnode_qemu.h \ - qemu/aes.h \ qemu/aff-block-vmdk.h \ qemu/block-bochs.c \ qemu/block-cloop.c \ diff --git a/lib/afflib.cpp b/lib/afflib.cpp index a9d13d0..d304663 100644 --- a/lib/afflib.cpp +++ b/lib/afflib.cpp @@ -646,35 +646,49 @@ void af_aes_decrypt(AFFILE *af,const char *segname,unsigned char *data,size_t *d if(datalen==0) return; // can't decrypt; no clue how long it is /* An encrypted segment was retrieved; decrypt and trunc the length as necessary */ - uint32_t extra = (*datalen) % AES_BLOCK_SIZE; - uint32_t pad = (AES_BLOCK_SIZE - extra) % AES_BLOCK_SIZE; + uint32_t extra = (*datalen) % AFF_AES_BLOCK_SIZE; + uint32_t pad = (AFF_AES_BLOCK_SIZE - extra) % AFF_AES_BLOCK_SIZE; if(data==0){ // just wants to find out new length if(extra>0){ - *datalen -= AES_BLOCK_SIZE; + *datalen -= AFF_AES_BLOCK_SIZE; } return; } - if(extra!=0 && *datalen < AES_BLOCK_SIZE){ + if(extra!=0 && *datalen < AFF_AES_BLOCK_SIZE){ *datalen = 0; // something is wrong return; } if(data==0){ // just report the new size - if(extra!=0) *datalen -= AES_BLOCK_SIZE; // a block was added + if(extra!=0) *datalen -= AFF_AES_BLOCK_SIZE; // a block was added return; } - *datalen -= extra; // *datalen is now a multiple of AES_BLOCK_SIZE + *datalen -= extra; // *datalen is now a multiple of AFF_AES_BLOCK_SIZE /* Create an IV */ - unsigned char iv[AES_BLOCK_SIZE]; + unsigned char iv[AFF_AES_BLOCK_SIZE]; memset(iv,0,sizeof(iv)); strlcpy((char *)iv,segname,sizeof(iv)); - /* Decrypt! */ + /* Decrypt in place, using the stable EVP API (the legacy + * AES_cbc_encrypt is deprecated since OpenSSL 3.0 and hidden in + * no-deprecated OpenSSL 3.4+/4.0 builds). The EVP result is + * byte-identical to the legacy call for whole-block inputs. + */ AF_READLOCK(af); - AES_cbc_encrypt(data,data,*datalen,&af->crypto->dkey,iv,AES_DECRYPT); + { + int outlen = 0; + if(EVP_DecryptInit_ex(af->crypto->dkey,NULL,NULL,NULL,iv)==1){ // re-init: same cipher+key, fresh IV + outlen = 0; + EVP_DecryptUpdate(af->crypto->dkey,data,&outlen,data,(int)*datalen); + { + int flen = 0; + EVP_DecryptFinal_ex(af->crypto->dkey,data+outlen,&flen); // writes 0 bytes (no padding, whole blocks) + } + } + } AF_UNLOCK(af); *datalen -= pad; // remove the padding #endif @@ -700,9 +714,9 @@ int af_get_seg(AFFILE *af,const char *segname,uint32_t *arg,unsigned char *data, af_aes_decrypt(af,segname,data,datalen); return 0; } - if(r==AF_ERROR_DATASMALL && datalen && (*datalen % AES_BLOCK_SIZE != 0)){ + if(r==AF_ERROR_DATASMALL && datalen && (*datalen % AFF_AES_BLOCK_SIZE != 0)){ /* Not enough space was provided to decrypt, probably because this was blocked out */ - size_t bigger_data_len = datalen_orig + AES_BLOCK_SIZE; + size_t bigger_data_len = datalen_orig + AFF_AES_BLOCK_SIZE; unsigned char *bigger_data = (unsigned char *)malloc(bigger_data_len); if(!bigger_data) return -1; // Malloc failed r = (*af->v->get_seg)(af,aesname,arg,bigger_data,&bigger_data_len); @@ -756,7 +770,7 @@ int af_get_next_seg(AFFILE *af,char *segname,size_t segname_len,uint32_t *arg, AF_UNLOCK(af); return 0; } - if(r==AF_ERROR_DATASMALL && datalen && (*datalen % AES_BLOCK_SIZE !=0)){ + if(r==AF_ERROR_DATASMALL && datalen && (*datalen % AFF_AES_BLOCK_SIZE !=0)){ *datalen = datalen_orig; AF_UNLOCK(af); return af_get_seg(af,segname,arg,data,datalen); @@ -810,7 +824,7 @@ int af_update_segf(AFFILE *af, const char *segname, char aesname[AF_MAX_NAME_LEN]; if(AF_SEALING(af) && ((flag & AF_SIGFLAG_NOSEAL)==0) && af->crypto->auto_encrypt){ /* Create an IV */ - unsigned char iv[AES_BLOCK_SIZE]; + unsigned char iv[AFF_AES_BLOCK_SIZE]; memset(iv,0,sizeof(iv)); strlcpy((char *)iv,segname,sizeof(iv)); @@ -822,14 +836,29 @@ int af_update_segf(AFFILE *af, const char *segname, /* Figure out if we need to padd out for encryption. Allocate space and */ - uint32_t extra = (datalen) % AES_BLOCK_SIZE; - uint32_t pad = (AES_BLOCK_SIZE - extra) % AES_BLOCK_SIZE; + uint32_t extra = (datalen) % AFF_AES_BLOCK_SIZE; + uint32_t pad = (AFF_AES_BLOCK_SIZE - extra) % AFF_AES_BLOCK_SIZE; newdata = (unsigned char *)malloc(datalen+pad+extra); + memcpy(newdata,(const unsigned char *)data,datalen); memset(newdata+datalen,pad+extra,pad); // PKCS7 uses 01 for one pad byte, 02 02 for two, etc. - /* Encrypt */ - AES_cbc_encrypt((const unsigned char *)data, - newdata, - datalen+pad,&af->crypto->ekey,iv,AES_ENCRYPT); + memset(newdata+datalen+pad,0,extra); // zero the trailing alignment region + /* Encrypt, using the stable EVP API (the legacy AES_cbc_encrypt is + * deprecated since OpenSSL 3.0 and hidden in no-deprecated OpenSSL + * 3.4+/4.0 builds). Encrypting into a separate buffer also removes + * the legacy in-place-style over-read of 'pad' bytes past the caller's + * data buffer. + */ + { + int ctlen = 0; + if(EVP_EncryptInit_ex(af->crypto->ekey,NULL,NULL,NULL,iv)==1){ // re-init: same cipher+key, fresh IV + ctlen = 0; + EVP_EncryptUpdate(af->crypto->ekey,newdata,&ctlen,newdata,(int)(datalen+pad)); + { + int flen = 0; + EVP_EncryptFinal_ex(af->crypto->ekey,newdata+ctlen,&flen); // writes 0 bytes (no padding, whole blocks) + } + } + } data = newdata; // we will write this out datalen += pad + extra; } diff --git a/lib/afflib_pages.cpp b/lib/afflib_pages.cpp index ba5cc7a..5f26229 100644 --- a/lib/afflib_pages.cpp +++ b/lib/afflib_pages.cpp @@ -376,7 +376,7 @@ int af_update_page(AFFILE *af,int64_t pagenum,unsigned char *data,int datalen) if(af->write_md5){ unsigned char md5_buf[16]; char md5name_buf[32]; - MD5(data,datalen,md5_buf); + af_MD5((const unsigned char *)data,datalen,md5_buf); snprintf(md5name_buf,sizeof(md5name_buf),AF_PAGE_MD5,pagenum); af_update_segf(af,md5name_buf,0,md5_buf,sizeof(md5_buf),AF_SIGFLAG_NOSIG); // ignore failure } @@ -386,7 +386,7 @@ int af_update_page(AFFILE *af,int64_t pagenum,unsigned char *data,int datalen) if(af->write_sha1){ unsigned char sha1_buf[20]; char sha1name_buf[32]; - SHA1(data,datalen,sha1_buf); + af_sha1((const unsigned char *)data,datalen,sha1_buf); snprintf(sha1name_buf,sizeof(sha1name_buf),AF_PAGE_SHA1,pagenum); af_update_segf(af,sha1name_buf,0,sha1_buf,sizeof(sha1_buf),AF_SIGFLAG_NOSIG); // ignore failure } diff --git a/lib/crypto.cpp b/lib/crypto.cpp index 3e38267..faacbca 100644 --- a/lib/crypto.cpp +++ b/lib/crypto.cpp @@ -25,11 +25,9 @@ using namespace std; #include #endif -/* Support OpenSSL before 1.1.0 */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) -#define EVP_MD_CTX_new EVP_MD_CTX_create -#define EVP_MD_CTX_free EVP_MD_CTX_destroy -#endif +/* Note: support for OpenSSL before 1.1.0 (EVP_MD_CTX_new/free and + * EVP_CIPHER_CTX_new/free aliases) is provided in include/afflib/afflib_i.h + */ /**************************************************************** @@ -76,7 +74,57 @@ void af_crypto_allocate(AFFILE *af) } -/** compute SHA256. +/** compute MD5 using the stable EVP API (the one-shot MD5() function is + * deprecated since OpenSSL 3.0). + * Return 0 if success, -1 if error. + */ +int af_MD5(const unsigned char *data,size_t datalen,unsigned char md[16]) +{ + const EVP_MD *md5 = EVP_get_digestbyname("MD5"); + if(!md5) return -1; + + uint32_t md5_buflen = 16; + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if(!ctx) return -1; + if(EVP_DigestInit_ex(ctx,md5,NULL)!=1){ + EVP_MD_CTX_free(ctx); + return -1; + } + EVP_DigestUpdate(ctx,data,datalen); + if(EVP_DigestFinal_ex(ctx,md,&md5_buflen)!=1){ // EVP_DigestFinal_ex returns 1 for success + EVP_MD_CTX_free(ctx); + return -1; + } + EVP_MD_CTX_free(ctx); + return 0; +} + +/** compute SHA1 using the stable EVP API (the one-shot SHA1() function is + * deprecated since OpenSSL 3.0). + * Return 0 if success, -1 if error. + */ +int af_sha1(const unsigned char *data,size_t datalen,unsigned char md[20]) +{ + const EVP_MD *sha1 = EVP_get_digestbyname("SHA1"); + if(!sha1) return -1; + + uint32_t sha1_buflen = 20; + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if(!ctx) return -1; + if(EVP_DigestInit_ex(ctx,sha1,NULL)!=1){ + EVP_MD_CTX_free(ctx); + return -1; + } + EVP_DigestUpdate(ctx,data,datalen); + if(EVP_DigestFinal_ex(ctx,md,&sha1_buflen)!=1){ // EVP_DigestFinal_ex returns 1 for success + EVP_MD_CTX_free(ctx); + return -1; + } + EVP_MD_CTX_free(ctx); + return 0; +} + +/** compute SHA256 using the stable EVP API. * Return 0 if success, -1 if error. */ int af_SHA256(const unsigned char *data,size_t datalen,unsigned char md[32]) @@ -86,11 +134,15 @@ int af_SHA256(const unsigned char *data,size_t datalen,unsigned char md[32]) uint32_t sha256_buflen = 32; EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - EVP_DigestInit(ctx,sha256); + if(!ctx) return -1; + if(EVP_DigestInit_ex(ctx,sha256,NULL)!=1){ + EVP_MD_CTX_free(ctx); + return -1; + } EVP_DigestUpdate(ctx,data,datalen); - if(EVP_DigestFinal(ctx,md,&sha256_buflen)!=1){ // EVP_DigestFinal returns 1 for success - EVP_MD_CTX_free(ctx); - return -1; + if(EVP_DigestFinal_ex(ctx,md,&sha256_buflen)!=1){ // EVP_DigestFinal_ex returns 1 for success + EVP_MD_CTX_free(ctx); + return -1; } EVP_MD_CTX_free(ctx); return 0; @@ -98,9 +150,11 @@ int af_SHA256(const unsigned char *data,size_t datalen,unsigned char md[32]) void af_crypto_deallocate(AFFILE *af) { -#ifdef AES_BLOCK_SIZE - memset(&af->crypto->ekey,0,sizeof(af->crypto->ekey)); - memset(&af->crypto->dkey,0,sizeof(af->crypto->dkey)); +#ifdef HAVE_AES_ENCRYPT + EVP_CIPHER_CTX_free(af->crypto->ekey); // EVP_CIPHER_CTX_free(NULL) is a no-op + af->crypto->ekey = 0; + EVP_CIPHER_CTX_free(af->crypto->dkey); // EVP_CIPHER_CTX_free(NULL) is a no-op + af->crypto->dkey = 0; #endif #ifdef HAVE_PEM_READ_BIO_RSA_PUBKEY if(af->crypto->sign_privkey){ @@ -131,12 +185,41 @@ int af_set_aes_key(AFFILE *af,const unsigned char *userKey,const int bits) } return AF_ERROR_KEY_SET; // key is already set } - int r; - r = AES_set_encrypt_key(userKey,bits,&af->crypto->ekey); - if(r) return r; - r = AES_set_decrypt_key(userKey,bits,&af->crypto->dkey); - if(r) return r; + /* Set up the EVP cipher contexts. The legacy AES_set_encrypt_key/ + * AES_set_decrypt_key calls are deprecated since OpenSSL 3.0 and + * hidden in no-deprecated OpenSSL 3.4+/4.0 builds, so we use the + * stable EVP API instead; it performs the same key expansion. + */ + const unsigned char *key = userKey; + size_t keylen = (size_t)bits/8; + if(key==0 || (bits%8)!=0 || keylen<16 || keylen>32) return -1; // invalid key length + const EVP_CIPHER *cipher = + (bits==128) ? EVP_aes_128_cbc() : + (bits==192) ? EVP_aes_192_cbc() : + (bits==256) ? EVP_aes_256_cbc() : 0; + if(!cipher) return -1; + + EVP_CIPHER_CTX *ekey = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX *dkey = EVP_CIPHER_CTX_new(); + if(!ekey || !dkey){ + EVP_CIPHER_CTX_free(ekey); + EVP_CIPHER_CTX_free(dkey); + return -1; + } + if(EVP_EncryptInit_ex(ekey,cipher,NULL,key,NULL)!=1 || // NULL cipher keeps context intact; sets no IV yet + EVP_DecryptInit_ex(dkey,cipher,NULL,key,NULL)!=1){ + EVP_CIPHER_CTX_free(ekey); + EVP_CIPHER_CTX_free(dkey); + return -1; + } + /* No padding: the AFF segment format adds and removes its own + * padding blocks, exactly like the legacy AES_*_encrypt path. */ + EVP_CIPHER_CTX_set_padding(ekey,0); + EVP_CIPHER_CTX_set_padding(dkey,0); + + af->crypto->ekey = ekey; + af->crypto->dkey = dkey; af->crypto->sealing_key_set = 1; af->crypto->auto_encrypt = 1; // default @@ -172,14 +255,31 @@ int af_save_aes_key_with_passphrase(AFFILE *af,const char *passphrase, const u_c memcpy(affkey_seg.version,(u_char *)&version_number,4); memcpy(affkey_seg.affkey_aes256,affkey,32); - /* Use the hash to encrypt the key and all zeros */ - AES_KEY ekey; - AES_set_encrypt_key(passphrase_hash,256,&ekey); - AES_encrypt(affkey_seg.affkey_aes256, - affkey_seg.affkey_aes256,&ekey); - AES_encrypt(affkey_seg.affkey_aes256+AES_BLOCK_SIZE, - affkey_seg.affkey_aes256+AES_BLOCK_SIZE,&ekey); - AES_encrypt(affkey_seg.zeros_aes256,affkey_seg.zeros_aes256,&ekey); + /* Use the hash to encrypt the key and all zeros. + * (AES-256-ECB, three 16-byte blocks, via the stable EVP API -- + * the legacy AES_set_encrypt_key/AES_encrypt are deprecated since + * OpenSSL 3.0. The result is byte-identical to the legacy code.) + */ + EVP_CIPHER_CTX *ectx = EVP_CIPHER_CTX_new(); + if(!ectx){ + memset((unsigned char *)&affkey_seg,0,sizeof(affkey_seg)); // erase the temp data + return -1; + } + EVP_CIPHER_CTX_set_padding(ectx,0); // exactly 16-byte blocks; no padding + int blen = 0, ok = 0; + if(EVP_EncryptInit_ex(ectx,EVP_aes_256_ecb(),NULL,passphrase_hash,NULL)==1){ + ok = EVP_EncryptUpdate(ectx,affkey_seg.affkey_aes256,&blen, + affkey_seg.affkey_aes256,AFF_AES_BLOCK_SIZE)==1 + && EVP_EncryptUpdate(ectx,affkey_seg.affkey_aes256+AFF_AES_BLOCK_SIZE,&blen, + affkey_seg.affkey_aes256+AFF_AES_BLOCK_SIZE,AFF_AES_BLOCK_SIZE)==1 + && EVP_EncryptUpdate(ectx,affkey_seg.zeros_aes256,&blen, + affkey_seg.zeros_aes256,AFF_AES_BLOCK_SIZE)==1; + } + EVP_CIPHER_CTX_free(ectx); + if(!ok){ + memset((unsigned char *)&affkey_seg,0,sizeof(affkey_seg)); // erase the temp data + return -1; + } /* Write this to a segment */ if(af_update_seg(af,AF_AFFKEY,0,(const u_char *)&affkey_seg,sizeof(affkey_seg))) return -1; @@ -253,15 +353,31 @@ int af_get_aes_key_from_passphrase(AFFILE *af,const char *passphrase, return AF_ERROR_NO_SHA256; } - /* Try to decrypt the key */ - - AES_KEY dkey; - AES_set_decrypt_key(passphrase_hash,256,&dkey); - AES_decrypt(affkey_seg.affkey_aes256, - affkey_seg.affkey_aes256,&dkey); - AES_decrypt(affkey_seg.affkey_aes256+AES_BLOCK_SIZE, - affkey_seg.affkey_aes256+AES_BLOCK_SIZE,&dkey); - AES_decrypt(affkey_seg.zeros_aes256,affkey_seg.zeros_aes256,&dkey); + /* Try to decrypt the key. + * (AES-256-ECB, three 16-byte blocks, via the stable EVP API -- + * the legacy AES_set_decrypt_key/AES_decrypt are deprecated since + * OpenSSL 3.0. The result is byte-identical to the legacy code.) + */ + EVP_CIPHER_CTX *dctx = EVP_CIPHER_CTX_new(); + if(!dctx){ + memset((unsigned char *)&affkey_seg,0,sizeof(affkey_seg)); // erase the temp data + return AF_ERROR_NO_AES; + } + EVP_CIPHER_CTX_set_padding(dctx,0); // exactly 16-byte blocks; no padding + int dlen = 0, dok = 0; + if(EVP_DecryptInit_ex(dctx,EVP_aes_256_ecb(),NULL,passphrase_hash,NULL)==1){ + dok = EVP_DecryptUpdate(dctx,affkey_seg.affkey_aes256,&dlen, + affkey_seg.affkey_aes256,AFF_AES_BLOCK_SIZE)==1 + && EVP_DecryptUpdate(dctx,affkey_seg.affkey_aes256+AFF_AES_BLOCK_SIZE,&dlen, + affkey_seg.affkey_aes256+AFF_AES_BLOCK_SIZE,AFF_AES_BLOCK_SIZE)==1 + && EVP_DecryptUpdate(dctx,affkey_seg.zeros_aes256,&dlen, + affkey_seg.zeros_aes256,AFF_AES_BLOCK_SIZE)==1; + } + EVP_CIPHER_CTX_free(dctx); + if(!dok){ + memset((unsigned char *)&affkey_seg,0,sizeof(affkey_seg)); // erase the temp data + return AF_ERROR_NO_AES; + } /* See if its zero? */ for(u_int i=0;i diff --git a/lib/qemu/block-qcow.c b/lib/qemu/block-qcow.c index c7b526f..96ee6a2 100644 --- a/lib/qemu/block-qcow.c +++ b/lib/qemu/block-qcow.c @@ -24,7 +24,7 @@ #include "qemu-common.h" #include "block_int.h" #include -#include "aes.h" +#include /**************************************************************/ /* QEMU COW block driver with compression and encryption support */ @@ -71,8 +71,8 @@ typedef struct BDRVQcowState { uint64_t cluster_cache_offset; uint32_t crypt_method; /* current crypt method, 0 if no key yet */ uint32_t crypt_method_header; - AES_KEY aes_encrypt_key; - AES_KEY aes_decrypt_key; + EVP_CIPHER_CTX *aes_encrypt_key; + EVP_CIPHER_CTX *aes_decrypt_key; } BDRVQcowState; static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); @@ -189,50 +189,56 @@ static int qcow_set_key(BlockDriverState *bs, const char *key) } s->crypt_method = s->crypt_method_header; - if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + /* Expand the key into EVP cipher contexts. (The legacy + * AES_set_encrypt_key/AES_set_decrypt_key calls are deprecated since + * OpenSSL 3.0 and hidden in no-deprecated OpenSSL 3.4+/4.0 builds; + * the EVP API performs the same key expansion.) */ + EVP_CIPHER_CTX_free(s->aes_encrypt_key); + s->aes_encrypt_key = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_free(s->aes_decrypt_key); + s->aes_decrypt_key = EVP_CIPHER_CTX_new(); + if (!s->aes_encrypt_key || !s->aes_decrypt_key) return -1; - if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + /* No padding: each sector is a multiple of the 16-byte block size */ + EVP_CIPHER_CTX_set_padding(s->aes_encrypt_key, 0); + EVP_CIPHER_CTX_set_padding(s->aes_decrypt_key, 0); + if (EVP_EncryptInit_ex(s->aes_encrypt_key, EVP_aes_128_cbc(), + NULL, keybuf, NULL) != 1 || + EVP_DecryptInit_ex(s->aes_decrypt_key, EVP_aes_128_cbc(), + NULL, keybuf, NULL) != 1) return -1; -#if 0 - /* test */ - { - uint8_t in[16]; - uint8_t out[16]; - uint8_t tmp[16]; - for(i=0;i<16;i++) - in[i] = i; - AES_encrypt(in, tmp, &s->aes_encrypt_key); - AES_decrypt(tmp, out, &s->aes_decrypt_key); - for(i = 0; i < 16; i++) - printf(" %02x", tmp[i]); - printf("\n"); - for(i = 0; i < 16; i++) - printf(" %02x", out[i]); - printf("\n"); - } -#endif return 0; } /* The crypt function is compatible with the linux cryptoloop algorithm for < 4 GB images. NOTE: out_buf == in_buf is - supported */ + supported. + 'key' is the EVP_CIPHER_CTX created by qcow_set_key; 'enc' is 1 to + encrypt and 0 to decrypt (mirroring the legacy AES_ENCRYPT/AES_DECRYPT). */ static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, int enc, - const AES_KEY *key) + EVP_CIPHER_CTX *key) { union { uint64_t ll[2]; uint8_t b[16]; } ivec; - int i; + int i, len; for(i = 0; i < nb_sectors; i++) { ivec.ll[0] = cpu_to_le64(sector_num); ivec.ll[1] = 0; - AES_cbc_encrypt(in_buf, out_buf, 512, key, - ivec.b, enc); + if (enc) { + /* re-init: same cipher and key, fresh per-sector IV */ + EVP_EncryptInit_ex(key, NULL, NULL, NULL, ivec.b); + EVP_EncryptUpdate(key, out_buf, &len, in_buf, 512); + } else { + EVP_DecryptInit_ex(key, NULL, NULL, NULL, ivec.b); + EVP_DecryptUpdate(key, out_buf, &len, in_buf, 512); + } + /* 512 bytes is whole blocks and padding is off, so nothing is + pending and EVP_*Final would write 0 bytes. */ sector_num++; in_buf += 512; out_buf += 512; @@ -356,7 +362,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, encrypt_sectors(s, start_sect + i, s->cluster_data, s->cluster_data + 512, 1, 1, - &s->aes_encrypt_key); + s->aes_encrypt_key); if (bdrv_pwrite(s->hd, cluster_offset + i * 512, s->cluster_data, 512) != 512) return -1; @@ -476,7 +482,7 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num, return -1; if (s->crypt_method) { encrypt_sectors(s, sector_num, buf, buf, n, 0, - &s->aes_decrypt_key); + s->aes_decrypt_key); } } nb_sectors -= n; @@ -506,7 +512,7 @@ static int qcow_write(BlockDriverState *bs, int64_t sector_num, return -1; if (s->crypt_method) { encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, - &s->aes_encrypt_key); + s->aes_encrypt_key); ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, s->cluster_data, n * 512); } else { @@ -558,7 +564,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) if (s->crypt_method) { encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf, acb->n, 0, - &s->aes_decrypt_key); + s->aes_decrypt_key); } } @@ -682,7 +688,7 @@ static void qcow_aio_write_cb(void *opaque, int ret) } } encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, - acb->n, 1, &s->aes_encrypt_key); + acb->n, 1, s->aes_encrypt_key); src_buf = acb->cluster_data; } else { src_buf = acb->buf; @@ -733,6 +739,10 @@ static void qcow_close(BlockDriverState *bs) qemu_free(s->cluster_cache); qemu_free(s->cluster_data); bdrv_delete(s->hd); + EVP_CIPHER_CTX_free(s->aes_encrypt_key); + s->aes_encrypt_key = NULL; + EVP_CIPHER_CTX_free(s->aes_decrypt_key); + s->aes_decrypt_key = NULL; } static int qcow_create(const char *filename, int64_t total_size, diff --git a/lib/qemu/block-qcow2.c b/lib/qemu/block-qcow2.c index 1eaa214..d0b0ff3 100644 --- a/lib/qemu/block-qcow2.c +++ b/lib/qemu/block-qcow2.c @@ -24,7 +24,7 @@ #include "qemu-common.h" #include "block_int.h" #include -#include "aes.h" +#include #include /* @@ -143,8 +143,8 @@ typedef struct BDRVQcowState { uint32_t crypt_method; /* current crypt method, 0 if no key yet */ uint32_t crypt_method_header; - AES_KEY aes_encrypt_key; - AES_KEY aes_decrypt_key; + EVP_CIPHER_CTX *aes_encrypt_key; + EVP_CIPHER_CTX *aes_decrypt_key; uint64_t snapshots_offset; int snapshots_size; int nb_snapshots; @@ -316,50 +316,56 @@ static int qcow_set_key(BlockDriverState *bs, const char *key) } s->crypt_method = s->crypt_method_header; - if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0) + /* Expand the key into EVP cipher contexts. (The legacy + * AES_set_encrypt_key/AES_set_decrypt_key calls are deprecated since + * OpenSSL 3.0 and hidden in no-deprecated OpenSSL 3.4+/4.0 builds; + * the EVP API performs the same key expansion.) */ + EVP_CIPHER_CTX_free(s->aes_encrypt_key); + s->aes_encrypt_key = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_free(s->aes_decrypt_key); + s->aes_decrypt_key = EVP_CIPHER_CTX_new(); + if (!s->aes_encrypt_key || !s->aes_decrypt_key) return -1; - if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0) + /* No padding: each sector is a multiple of the 16-byte block size */ + EVP_CIPHER_CTX_set_padding(s->aes_encrypt_key, 0); + EVP_CIPHER_CTX_set_padding(s->aes_decrypt_key, 0); + if (EVP_EncryptInit_ex(s->aes_encrypt_key, EVP_aes_128_cbc(), + NULL, keybuf, NULL) != 1 || + EVP_DecryptInit_ex(s->aes_decrypt_key, EVP_aes_128_cbc(), + NULL, keybuf, NULL) != 1) return -1; -#if 0 - /* test */ - { - uint8_t in[16]; - uint8_t out[16]; - uint8_t tmp[16]; - for(i=0;i<16;i++) - in[i] = i; - AES_encrypt(in, tmp, &s->aes_encrypt_key); - AES_decrypt(tmp, out, &s->aes_decrypt_key); - for(i = 0; i < 16; i++) - printf(" %02x", tmp[i]); - printf("\n"); - for(i = 0; i < 16; i++) - printf(" %02x", out[i]); - printf("\n"); - } -#endif return 0; } /* The crypt function is compatible with the linux cryptoloop algorithm for < 4 GB images. NOTE: out_buf == in_buf is - supported */ + supported. + 'key' is the EVP_CIPHER_CTX created by qcow_set_key; 'enc' is 1 to + encrypt and 0 to decrypt (mirroring the legacy AES_ENCRYPT/AES_DECRYPT). */ static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, int enc, - const AES_KEY *key) + EVP_CIPHER_CTX *key) { union { uint64_t ll[2]; uint8_t b[16]; } ivec; - int i; + int i, len; for(i = 0; i < nb_sectors; i++) { ivec.ll[0] = cpu_to_le64(sector_num); ivec.ll[1] = 0; - AES_cbc_encrypt(in_buf, out_buf, 512, key, - ivec.b, enc); + if (enc) { + /* re-init: same cipher and key, fresh per-sector IV */ + EVP_EncryptInit_ex(key, NULL, NULL, NULL, ivec.b); + EVP_EncryptUpdate(key, out_buf, &len, in_buf, 512); + } else { + EVP_DecryptInit_ex(key, NULL, NULL, NULL, ivec.b); + EVP_DecryptUpdate(key, out_buf, &len, in_buf, 512); + } + /* 512 bytes is whole blocks and padding is off, so nothing is + pending and EVP_*Final would write 0 bytes. */ sector_num++; in_buf += 512; out_buf += 512; @@ -382,7 +388,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, encrypt_sectors(s, start_sect + n_start, s->cluster_data, s->cluster_data, n, 1, - &s->aes_encrypt_key); + s->aes_encrypt_key); } ret = bdrv_write(s->hd, (cluster_offset >> 9) + n_start, s->cluster_data, n); @@ -746,7 +752,7 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num, return -1; if (s->crypt_method) { encrypt_sectors(s, sector_num, buf, buf, n, 0, - &s->aes_decrypt_key); + s->aes_decrypt_key); } } nb_sectors -= n; @@ -775,7 +781,7 @@ static int qcow_write(BlockDriverState *bs, int64_t sector_num, return -1; if (s->crypt_method) { encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, - &s->aes_encrypt_key); + s->aes_encrypt_key); ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, s->cluster_data, n * 512); } else { @@ -827,7 +833,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) if (s->crypt_method) { encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf, acb->n, 0, - &s->aes_decrypt_key); + s->aes_decrypt_key); } } @@ -969,7 +975,7 @@ static void qcow_aio_write_cb(void *opaque, int ret) } } encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, - acb->n, 1, &s->aes_encrypt_key); + acb->n, 1, s->aes_encrypt_key); src_buf = acb->cluster_data; } else { src_buf = acb->buf; @@ -1016,6 +1022,10 @@ static void qcow_close(BlockDriverState *bs) qemu_free(s->cluster_data); refcount_close(bs); bdrv_delete(s->hd); + EVP_CIPHER_CTX_free(s->aes_encrypt_key); + s->aes_encrypt_key = NULL; + EVP_CIPHER_CTX_free(s->aes_decrypt_key); + s->aes_decrypt_key = NULL; } /* XXX: use std qcow open function ? */ diff --git a/lib/s3.cpp b/lib/s3.cpp index bbc9d38..b41f652 100644 --- a/lib/s3.cpp +++ b/lib/s3.cpp @@ -26,7 +26,7 @@ #include #ifdef HAVE_OPENSSL_MD5_H -#include +#include #endif @@ -450,7 +450,7 @@ void s3_bandwidth(int argc,char **argv) /* Now verify the MD5 */ unsigned char md5[16]; memset(md5,0,sizeof(md5)); - MD5((const unsigned char *)r->base,r->len,md5); + af_MD5((const unsigned char *)r->base,r->len,md5); if(memcmp(r->ETag,md5,16)!=0){ fprintf(out," FAILED != "); for(int i=0;i<16;i++) fprintf(out,"%02x",md5[i]); diff --git a/lib/s3_glue.cpp b/lib/s3_glue.cpp index 8544cf9..a05f45a 100644 --- a/lib/s3_glue.cpp +++ b/lib/s3_glue.cpp @@ -27,8 +27,8 @@ #include #include -#ifdef HAVE_OPENSSL_MD5_H -#include +#ifdef HAVE_MD5 +#include #endif #ifdef HAVE_OPENSSL_HMAC_H @@ -43,7 +43,7 @@ #include #endif -#if !defined(HAVE_OPENSSL_MD5_H) +#if !defined(HAVE_MD5) #error S3 support requires MD5 support #endif @@ -641,7 +641,7 @@ int object_put(string bucket,string path, { unsigned char md5[16]; memset(md5,0,sizeof(md5)); - MD5((const unsigned char *)buf,buflen,md5); + af_MD5((const unsigned char *)buf,buflen,md5); for(int i=0;i0){ diff --git a/m4/ax_openssl_version.m4 b/m4/ax_openssl_version.m4 new file mode 100644 index 0000000..cfdbc0f --- /dev/null +++ b/m4/ax_openssl_version.m4 @@ -0,0 +1,120 @@ +# -------------------------------------------------------------------------- +# AX_OPENSSL_VERSION +# +# Detect the OpenSSL version in use, whether the deprecated low-level +# APIs (MD5_*, SHA1_*, AES_*) are still visible in the installed headers, +# and expose the results to the C code. +# +# Works with OpenSSL 1.0.2, 1.1.1, 3.x and 4.x. The probe compiles and +# runs a program that reads ; when execution is not +# possible (e.g. cross-compiling), the version is reported as unknown. +# +# Defined macros: +# OPENSSL_VERSION_MAJOR / OPENSSL_VERSION_MINOR / OPENSSL_VERSION_PATCH +# HAVE_OPENSSL_1_1_1 (version >= 1.1.1 and < 3.0) +# HAVE_OPENSSL3 (version >= 3.0.0) +# HAVE_OPENSSL4 (version >= 4.0.0) +# HAVE_OPENSSL_DEPRECATED_MD5_SHA1 (MD5_Init/SHA1_Init declarations visible) +# HAVE_OPENSSL_DEPRECATED_AES (AES_set_encrypt_key/AES_cbc_encrypt visible) +# +# Substituted variable: +# OPENSSL_VERSION (e.g. "3.0.13"; "unknown" when not determinable) +# +# Copyright (c) 2026 AFFLIB. Public domain / BSD-style, like the rest of AFFLIB. +# -------------------------------------------------------------------------- +AC_DEFUN([AX_OPENSSL_VERSION],[ + +dnl -- Determine major/minor/patch by compiling and running a small probe -- +AC_MSG_CHECKING([OpenSSL version]) +AC_LANG_SAVE +AC_LANG([C]) +probe_file=conftest.openssl_version +rm -f $probe_file +AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +]], [[ + FILE *f = fopen("conftest.openssl_version","w"); + int maj, min, pat; +#ifdef OPENSSL_VERSION_MAJOR + maj = OPENSSL_VERSION_MAJOR; + min = OPENSSL_VERSION_MINOR; + pat = OPENSSL_VERSION_PATCH; +#else + /* older OpenSSL: decode the 0xMNN00PPSL layout of OPENSSL_VERSION_NUMBER */ + maj = (int)(OPENSSL_VERSION_NUMBER >> 28); + min = (int)((OPENSSL_VERSION_NUMBER >> 20) & 0xFF); + pat = (int)((OPENSSL_VERSION_NUMBER >> 12) & 0xF); +#endif + if(!f) return 1; + fprintf(f,"%d %d %d %s\n",maj,min,pat,OPENSSL_VERSION_TEXT); + fclose(f); + return 0; +]])], +[ax_openssl_version=`cat $probe_file 2>/dev/null`], +[ax_openssl_version=""]) +rm -f $probe_file +AC_LANG_RESTORE + +if test -n "$ax_openssl_version" ; then + ax_openssl_major=`echo $ax_openssl_version | cut -d' ' -f1` + ax_openssl_minor=`echo $ax_openssl_version | cut -d' ' -f2` + ax_openssl_patch=`echo $ax_openssl_version | cut -d' ' -f3` + ax_openssl_text=`echo $ax_openssl_version | sed -e 's/^[[0-9]][[0-9]]* [[0-9]][[0-9]]* [[0-9]][[0-9]]* //'` + OPENSSL_VERSION=$ax_openssl_major.$ax_openssl_minor.$ax_openssl_patch + AC_MSG_RESULT([$OPENSSL_VERSION (${ax_openssl_text})]) + AC_DEFINE_UNQUOTED([OPENSSL_VERSION_MAJOR],[$ax_openssl_major],[OpenSSL major version]) + AC_DEFINE_UNQUOTED([OPENSSL_VERSION_MINOR],[$ax_openssl_minor],[OpenSSL minor version]) + AC_DEFINE_UNQUOTED([OPENSSL_VERSION_PATCH],[$ax_openssl_patch],[OpenSSL patch version]) + if test x"$ax_openssl_major.$ax_openssl_minor" = x"1.1" && test $ax_openssl_patch -ge 1 ; then + AC_DEFINE([HAVE_OPENSSL_1_1_1],1,[Defined if OpenSSL 1.1.1 or newer (but < 3.0)]) + fi + if test $ax_openssl_major -ge 3 ; then + AC_DEFINE([HAVE_OPENSSL3],1,[Defined if OpenSSL 3.0 or newer]) + if test $ax_openssl_major -ge 4 ; then + AC_DEFINE([HAVE_OPENSSL4],1,[Defined if OpenSSL 4.0 or newer]) + fi + fi +else + OPENSSL_VERSION=unknown + AC_MSG_RESULT([unknown (could not run the version probe)]) +fi +AC_SUBST([OPENSSL_VERSION]) + +dnl -- Check whether the deprecated low-level APIs are still visible ---- +dnl (i.e. the headers were not built/compiled with OPENSSL_NO_DEPRECATED_3_0) +dnl Take the address of the functions -- never call them. + +AC_LANG_SAVE +AC_LANG([C]) + +AC_MSG_CHECKING([whether the deprecated OpenSSL MD5/SHA1 APIs are visible]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +]], [[ +void (*ax_probe_md5)(MD5_CTX *) = MD5_Init; +void (*ax_probe_sha1)(SHA_CTX *) = SHA1_Init; +ax_probe_md5 = 0; +ax_probe_sha1 = 0; +]])],[ + AC_DEFINE([HAVE_OPENSSL_DEPRECATED_MD5_SHA1],1,[Deprecated MD5_Init/SHA1_Init declarations are visible]) + AC_MSG_RESULT([yes])], +[AC_MSG_RESULT([no (hidden by OPENSSL_NO_DEPRECATED_3_0)])]) + +AC_MSG_CHECKING([whether the deprecated OpenSSL AES APIs are visible]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +]], [[ +int (*ax_probe_aes_set)(const unsigned char *, int, AES_KEY *) = AES_set_encrypt_key; +void (*ax_probe_aes_cbc)(const unsigned char *, unsigned char *, size_t, + const AES_KEY *, unsigned char *, int) = AES_cbc_encrypt; +ax_probe_aes_set = 0; +ax_probe_aes_cbc = 0; +]])],[ + AC_DEFINE([HAVE_OPENSSL_DEPRECATED_AES],1,[Deprecated AES_set_encrypt_key/AES_cbc_encrypt declarations are visible]) + AC_MSG_RESULT([yes])], +[AC_MSG_RESULT([no (hidden by OPENSSL_NO_DEPRECATED_3_0)])]) + +AC_LANG_RESTORE +])dnl AX_OPENSSL_VERSION diff --git a/tools/affcompare.cpp b/tools/affcompare.cpp index 993d558..11988ef 100644 --- a/tools/affcompare.cpp +++ b/tools/affcompare.cpp @@ -35,8 +35,6 @@ using namespace aff; #include #include #include -#include -#include #include #ifdef HAVE_CSTRING diff --git a/tools/affconvert.cpp b/tools/affconvert.cpp index ea957e3..d9b85d2 100644 --- a/tools/affconvert.cpp +++ b/tools/affconvert.cpp @@ -15,8 +15,7 @@ #include "afflib_i.h" // we do enough mucking, we need the internal version #include "utils.h" -#include -#include +#include #ifdef WIN32 #include "unix4win32.h" @@ -320,11 +319,29 @@ int convert(const char *infile,char *outfile) *** stuff that we keep at the beginning of the file... ****************************************************************/ - MD5_CTX md5; - MD5_Init(&md5); - - SHA_CTX sha; - SHA1_Init(&sha); + /* Set up the MD5/SHA1 hash machinery via the stable EVP API + * (the low-level MD5/SHA1 state functions are deprecated + * since OpenSSL 3.0) */ + EVP_MD_CTX *md5ctx = EVP_MD_CTX_new(); + EVP_MD_CTX *sha1ctx = EVP_MD_CTX_new(); + if(!md5ctx || !sha1ctx){ + af_close(a_out); + af_close(a_in); + return -1; + } + { + const EVP_MD *md5_md = EVP_get_digestbyname("MD5"); + const EVP_MD *sha1_md = EVP_get_digestbyname("SHA1"); + if(!md5_md || !sha1_md || + EVP_DigestInit_ex(md5ctx,md5_md,NULL)!=1 || + EVP_DigestInit_ex(sha1ctx,sha1_md,NULL)!=1){ + EVP_MD_CTX_free(md5ctx); + EVP_MD_CTX_free(sha1ctx); + af_close(a_out); + af_close(a_in); + return -1; + } + } /* Setup writing */ if(a_in->image_pagesize){ @@ -429,8 +446,8 @@ int convert(const char *infile,char *outfile) if(pagenum != prev_pagenum + 1) hash_valid = false; if(hash_valid && vni.supports_metadata){ - MD5_Update(&md5,data,data_len); - SHA1_Update(&sha,data,data_len); + EVP_DigestUpdate(md5ctx,data,data_len); + EVP_DigestUpdate(sha1ctx,data,data_len); prev_pagenum = pagenum; } last_byte_in_image = (int64_t)image_pagesize * pagenum + (int64_t)data_len; @@ -455,8 +472,8 @@ int convert(const char *infile,char *outfile) err(1,"af_write"); } if(vni.supports_metadata){ - MD5_Update(&md5,data,data_len); - SHA1_Update(&sha,data,data_len); + EVP_DigestUpdate(md5ctx,data,data_len); + EVP_DigestUpdate(sha1ctx,data,data_len); } } if(data_len<0) err(1,"af_read"); @@ -475,19 +492,22 @@ int convert(const char *infile,char *outfile) if(hash_valid && vni.supports_metadata){ u_char md5_buf[32],sha1_buf[40]; char buf[256]; - MD5_Final(md5_buf,&md5); + EVP_DigestFinal_ex(md5ctx,md5_buf,NULL); if(af_update_seg(a_out,AF_MD5,0,md5_buf,16) && errno!=ENOTSUP){ err(1,"Could not update AF_MD5"); } if(!opt_quiet) printf("md5: %s\n",af_hexbuf(buf,sizeof(buf),md5_buf,16,1)); - - SHA1_Final(sha1_buf,&sha); + + EVP_DigestFinal_ex(sha1ctx,sha1_buf,NULL); if(af_update_seg(a_out,AF_SHA1,0,sha1_buf,20) && errno!=ENOTSUP){ err(1,"Could not update AF_SHA1"); } if(!opt_quiet) printf("sha1: %s\n",af_hexbuf(buf,sizeof(buf),sha1_buf,20,1)); } + EVP_MD_CTX_free(md5ctx); + EVP_MD_CTX_free(sha1ctx); + /* Finish the hash calculations and write to the db */ if(!opt_quiet){ printf("bytes converted: %" I64d " \n",total_bytes_converted); diff --git a/tools/affcopy.cpp b/tools/affcopy.cpp index 31593e2..464bb14 100644 --- a/tools/affcopy.cpp +++ b/tools/affcopy.cpp @@ -31,8 +31,7 @@ using namespace aff; #include #include -#include -#include +#include #include #ifdef HAVE_UNISTD_H @@ -372,7 +371,7 @@ int affcopy(char *infile,vector &outfiles) /* Calculate the MD5 of this segment and remember it in the map */ md5blob md5; - MD5(segbuf,seglen,md5.buf); + af_MD5((const unsigned char *)segbuf,seglen,md5.buf); hashMap[segname] = md5; /* See if this is a page; if so, it is handled specially */ @@ -526,7 +525,7 @@ int affcopy(char *infile,vector &outfiles) * (But don't do this for preened segments. */ unsigned char md5_read[16]; - MD5(segbuf,seglen,md5_read); + af_MD5((const unsigned char *)segbuf,seglen,md5_read); if(memcmp(hashMap[segname].buf,md5_read,16)!=0){ unlink_outfiles(outfiles); errx(1,"Hash read from %s for segment %s doesn't validate.", diff --git a/tools/affinfo.cpp b/tools/affinfo.cpp index 743f58b..8b60bc6 100644 --- a/tools/affinfo.cpp +++ b/tools/affinfo.cpp @@ -18,8 +18,7 @@ #include #include -#include -#include +#include #include #include @@ -189,21 +188,35 @@ void validate(const char *infile) last = *i; } - /* Set up the hash machinery */ - MD5_CTX md5; - MD5_Init(&md5); + /* Set up the hash machinery (via the stable EVP API; the low-level + * MD5 and SHA1 state functions are deprecated since OpenSSL 3.0) */ + EVP_MD_CTX *md5ctx = EVP_MD_CTX_new(); + EVP_MD_CTX *sha1ctx = EVP_MD_CTX_new(); + if(opt_validate & VALIDATE_MD5){ + const EVP_MD *md5_md = EVP_get_digestbyname("MD5"); + if(!md5_md || EVP_DigestInit_ex(md5ctx,md5_md,NULL)!=1){ + af_close(af); + return; + } + } + if(opt_validate & VALIDATE_SHA1){ + const EVP_MD *sha1_md = EVP_get_digestbyname("SHA1"); + if(!sha1_md || EVP_DigestInit_ex(sha1ctx,sha1_md,NULL)!=1){ + EVP_MD_CTX_free(md5ctx); + EVP_MD_CTX_free(sha1ctx); + af_close(af); + return; + } + } - SHA_CTX sha; - SHA1_Init(&sha); - uint64_t total_bytes = 0; while(!af_eof(af)){ unsigned char buf[65536]; // a decent size size_t bytes = af_read(af,buf,sizeof(buf)); if(bytes==0) break; // reached sparse region of file total_bytes += bytes; - if(opt_validate & VALIDATE_MD5) MD5_Update(&md5,buf,bytes); - if(opt_validate & VALIDATE_SHA1) SHA1_Update(&sha,buf,bytes); + if(opt_validate & VALIDATE_MD5) EVP_DigestUpdate(md5ctx,buf,bytes); + if(opt_validate & VALIDATE_SHA1) EVP_DigestUpdate(sha1ctx,buf,bytes); } /* Finish the hash calculations and write to the db */ @@ -213,8 +226,8 @@ void validate(const char *infile) size_t md5len = sizeof(md5_stored); unsigned char md5_computed[16]; char buf[256]; - - MD5_Final(md5_computed,&md5); + + EVP_DigestFinal_ex(md5ctx,md5_computed,NULL); printf("computed md5: %s\n", af_hexbuf(buf,sizeof(buf),md5_computed,16,opt_hexbuf)); if(af_get_seg(af,AF_MD5,0,md5_stored,&md5len)==0){ @@ -241,7 +254,7 @@ void validate(const char *infile) unsigned char sha1_computed[20]; char buf[256]; - SHA1_Final(sha1_computed,&sha); + EVP_DigestFinal_ex(sha1ctx,sha1_computed,NULL); printf("computed sha1: %s \n",af_hexbuf(buf,sizeof(buf),sha1_computed,20,opt_hexbuf)); if(af_get_seg(af,AF_SHA1,0,sha1_stored,&sha1len)==0){ printf(" stored sha1: %s ",af_hexbuf(buf,sizeof(buf),sha1_stored,20,opt_hexbuf)); @@ -258,7 +271,10 @@ void validate(const char *infile) printf("(no SHA1 in AFF file)\n"); } } - + + EVP_MD_CTX_free(md5ctx); + EVP_MD_CTX_free(sha1ctx); + af_close(af); } @@ -415,10 +431,14 @@ void print_info(AFFILE *af,const char *segname) cc = output_line + strlen(output_line); if(opt_preview_md5){ - u_char md5[32]; - MD5(data,data_len,md5); - memcpy(data,md5,32); - data_len = 32; + /* Show an MD5 preview of the segment (16 bytes). + * (Previously this used an oversized 32-byte buffer and copied 16 + * bytes of stack garbage after the hash; fixed while migrating + * off the deprecated one-shot MD5() API.) */ + u_char md5[16]; + af_MD5((const unsigned char *)data,data_len,md5); + memcpy(data,md5,16); + data_len = 16; } if(display_as_hex(segname,display_len)){ char buf[82]; @@ -481,7 +501,7 @@ void print_info(AFFILE *af,const char *segname) goto skip1; } - MD5(page_data,page_data_len,hash_calc); + af_MD5((const unsigned char *)page_data,page_data_len,hash_calc); if(memcmp(hash_buf,hash_calc,sizeof(hash_buf))!=0){ char hb[32]; printf("** HASH INVALID **\n%30s Calculated %s\n","", diff --git a/tools/affix.cpp b/tools/affix.cpp index da76e87..ca098ad 100644 --- a/tools/affix.cpp +++ b/tools/affix.cpp @@ -16,8 +16,6 @@ #include #include -#include -#include #include #ifdef HAVE_UNISTD_H diff --git a/tools/affrecover.cpp b/tools/affrecover.cpp index bc220cb..9573faf 100644 --- a/tools/affrecover.cpp +++ b/tools/affrecover.cpp @@ -15,8 +15,6 @@ #include #include -#include -#include #include #ifdef HAVE_UNISTD_H diff --git a/tools/affstats.cpp b/tools/affstats.cpp index 6005640..d9efe27 100644 --- a/tools/affstats.cpp +++ b/tools/affstats.cpp @@ -48,8 +48,6 @@ #include "afflib.h" #include "afflib_i.h" -#include -#include #include #include