Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ config.log
config.status
config.sub
configure
INSTALL
install-sh
libtool
ltmain.sh
Expand Down
28 changes: 26 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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}])
Expand Down
2 changes: 2 additions & 0 deletions include/afflib/afflib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 18 additions & 6 deletions include/afflib/afflib_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,23 @@ void __cdecl AFDbgPrint (PCSTR Format,...);
#endif

#ifdef HAVE_LIBCRYPTO
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/rsa.h> // a standard part of OpenSSL
#include <openssl/rand.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/x509.h> // X509 is used by struct af_crypto
/* The AES block size (16 bytes). Formerly taken from <openssl/aes.h>;
* 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
Expand Down Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion lib/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
67 changes: 48 additions & 19 deletions lib/afflib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));

Expand All @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/afflib_pages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
Loading