Bug: Decrypting *cfb1 encrypted data via BIO_read() leads to corruption OpenSSL versions impacted: I have reproduced this issue in 0.9.8 and 1.0.0 (beta) OS tested: RHEL Linux 5.0
Below is a program demonstrating that decrypting data encrypted by *cfb1 algorithms can lead to corruption of the decrypted data (if its length is more than EVP_MAX_BLOCK_LENGTH*2 (64 bytes). The issue does not seem to happen with "des-cfb1" (since that one is not using the underlying CRYPTO_cfb128_1_encrypt() function that seems to be the culprit). I guess this is due to the overlapping buffers mentioned in this fix: http://cvs.openssl.org/filediff?f=openssl/crypto/evp/bio_enc.c&v1=1.17&v2=1.18 However due the nature of this issue I do not think increasing BUF_OFFSET further would help - since the function in question (CRYPTO_cfb128_1_encrypt()) might corrupt the decrypted data as its size increases in relation to BUF_OFFSET. The problem seems to have been introduced with this checkin: http://cvs.openssl.org/filediff?f=openssl/crypto/aes/aes_cfb.c&v1=1.3&v2=1.4 I think the solution to this is to remove the line that clears the "out" memory buffer before running the decryption loop (I do not think it is really needed, at least the "equivalent" function in e_des.c does not have it), i.e. (diff against cfb128.c in HEAD (and 1.0.0) branch): *** cfb128.c 29 Dec 2008 12:35:48 -0000 1.3 --- cfb128.c 6 Feb 2010 06:58:19 -0000 *************** *** 224,230 **** assert(in && out && key && ivec && num); assert(*num == 0); - memset(out,0,(bits+7)/8); for(n=0 ; n<bits ; ++n) { c[0]=(in[n/8]&(1 << (7-n%8))) ? 0x80 : 0; István Noszticzius ----------------------------------------------------------------------------- // // DEMO of *cfb1 decryption corruption // #include <stdio.h> #include <openssl/evp.h> #include <openssl/bio.h> static void hexdump(char* header, unsigned char *data, int data_len) { int i; printf("%s (%d):\n", header, data_len); for(i = 0; i < data_len; i++) { printf("%02X%s", data[i], i < data_len-1 ? " " : ""); if (!((i+1) % 16) || i == data_len-1) printf("\n"); } } int main(int argc, char** argv) { ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); const EVP_CIPHER *cipher= EVP_get_cipherbyname("aes-128-cfb1"); // // This data Can be generated by: // echo "123456789012345678901234567890123456789012345678901234567890123456" |\ // openssl enc -e -aes-128-cfb1 -K 3132333435363738 -iv 3132333435363738 |\ unsigned char input_data[] = "\x25\x39\x7a\x36\xc9\x23\xc5\xc9" "\x26\xa3\x17\x7f\x28\xdb\xae\x3f" "\x6b\x1c\x82\xa3\x59\xc1\x09\x7a" "\x3d\xed\xe6\xb4\x4b\xf9\xb9\xa9" "\xe3\xa9\xc5\x1f\xbe\xa5\x03\xf3" "\x6a\x34\xa8\x05\x2e\x7f\xc5\x91" "\x35\x22\xe8\x8c\x64\x77\x77\xbe" "\x7c\x3b\x93\xdf\x7d\x81\x54\x57" "\xd5\xeb\x1a"; unsigned char key[16] = "\x31\x32\x33\x34\x35\x36\x37\x38" "\x00\x00\x00\x00\x00\x00\x00\x00"; unsigned char iv[16] = "\x31\x32\x33\x34\x35\x36\x37\x38" "\x00\x00\x00\x00\x00\x00\x00\x00"; BIO *input_bio = // -1 to cut off trailing 0 BIO_new_mem_buf(input_data, sizeof(input_data)-1); BIO *output_bio = BIO_new(BIO_s_mem()); BIO *decryption_bio = BIO_new(BIO_f_cipher()); BIO_set_cipher(decryption_bio, cipher, key, iv, 0); #if 1 // // Decrypting data while reading: Does not work // (i.e. results in corruption) input_bio = BIO_push(decryption_bio, input_bio); #else // // Decrypting data while writing: Works // output_bio = BIO_push(decryption_bio, output_bio); #endif unsigned char tmpbuff[256]; for (;;) { int inl = BIO_read(input_bio, tmpbuff, sizeof(tmpbuff)); if (inl <= 0) break; BIO_write(output_bio, tmpbuff, inl); } unsigned char* output_data; int outl = BIO_get_mem_data(output_bio, &output_data); hexdump("Decrypted Data", output_data, outl); return(0); } ______________________________________________________________________ OpenSSL Project http://www.openssl.org Development Mailing List openssl-dev@openssl.org Automated List Manager majord...@openssl.org