Mailing List Archive

Oddity in AES encryption?
Hi,

I am working on a C programming project that involves encrypting files with AES-256-CBC. I was testing libgcrypt and was comparing it to other libraries when I noticed that the the output of libgcrypt seems to reverse the order of the final two blocks compared to the openssl library. I then used test values from NIST 800-38a and found that the final two blocks produced by libgcrypt are swapped from what the standards recommend. Libgcrypt is able to decrypt its own output but fails to decrypt the output of openssl.

Using the test vectors from F.2.5 of NIST Special Publication 800-38A (https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), I get:

SSl Ciphertext :
0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{..
0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,}
0020 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a
0030 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j..

gcrypt Ciphertext is:
0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{..
0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,}
0020 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j..
0030 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a

I believe that according to the NIST specs, libgcrypt has swapped the blocks labelled 0020 and 0030.

Perhaps I have done something wrong or unusual in my use of the library?

Thanks,

Clay

—— Code

// uses SSL library to dump hax values to screen
// Replace print functions or compile with ssl:
// gcc gcrypt-test.c -o gcrypt-test `/usr/bin/libgcrypt-config --libs --cflags` -lcrypto

// code from: https://www.tnichols.org/2015/09/27/Encrypting-and-Signing-Using-libgcrypt/

#include <openssl/conf.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> // size_t
#include <gcrypt.h>

#define AES256_KEY_SIZE 32
#define AES256_BLOCK_SIZE 16

typedef struct {
unsigned char * ct;
int ct_len;
} encryption;

typedef struct {
unsigned char * pt;
int pt_len;
} decryption;

void handlegcrypterr(int err){
fprintf(stderr, "cipher_open: %s/%s\n", gcry_strsource(err), gcry_strerror(err));
abort();
}

void print_encryption(encryption * e){
BIO_dump_fp (stdout, (const char *)e->ct, e->ct_len);
}

encryption ** encrypt_gcrypt(unsigned char *plaintext,
int plaintext_len,
unsigned char *key,
unsigned char *iv){

gcry_cipher_hd_t handle;
gcry_error_t err;

// 256-bit AES using cipher-block chaining; with ciphertext stealing, no manual padding is required
err = gcry_cipher_open(&handle,
GCRY_CIPHER_AES256,
GCRY_CIPHER_MODE_CBC,
GCRY_CIPHER_CBC_CTS);
if (err) { handlegcrypterr(err); }

err = gcry_cipher_setkey(handle, key, AES256_KEY_SIZE);
if (err) { handlegcrypterr(err); }

err = gcry_cipher_setiv(handle, iv, AES256_BLOCK_SIZE);
if (err) { handlegcrypterr(err); }

// Find number of blocks required for data
int blocks_required = plaintext_len / AES256_BLOCK_SIZE;
if (plaintext_len % AES256_BLOCK_SIZE != 0) {
blocks_required++;
}

// Make new buffer of size blocks_required * AES256_BLOCK_SIZE for in-place encryption
unsigned char * ciphertext = malloc(blocks_required * AES256_BLOCK_SIZE);

memcpy(ciphertext, plaintext, plaintext_len);

// Encryption is performed in-place
err = gcry_cipher_encrypt(handle, ciphertext, AES256_BLOCK_SIZE * blocks_required, NULL, 0);
if (err) { handlegcrypterr(err); }

free(handle);

encryption ** result = malloc(sizeof(encryption *));
*result = (encryption *) malloc(sizeof(encryption));

(*result)->ct = ciphertext;
(*result)->ct_len = blocks_required * AES256_BLOCK_SIZE;

return result;
}

decryption ** decrypt_gcrypt(encryption *ciphertext,
unsigned char *key,
unsigned char *iv){

gcry_cipher_hd_t handle;
gcry_error_t err;

// 256-bit AES using cipher-block chaining; with ciphertext stealing, no manual padding is required
err = gcry_cipher_open(&handle,
GCRY_CIPHER_AES256,
GCRY_CIPHER_MODE_CBC,
GCRY_CIPHER_CBC_CTS);
if (err) { handlegcrypterr(err); }

err = gcry_cipher_setkey(handle, key, AES256_KEY_SIZE);
if (err) { handlegcrypterr(err); }

err = gcry_cipher_setiv(handle, iv, AES256_BLOCK_SIZE);
if (err) { handlegcrypterr(err); }

// Find number of blocks required for data
int blocks_required = ciphertext->ct_len / AES256_BLOCK_SIZE;
if (ciphertext->ct_len % AES256_BLOCK_SIZE != 0) {
blocks_required++;
}

// Make new buffer of size blocks_required * AES256_BLOCK_SIZE for in-place encryption
unsigned char * plaintext = malloc(blocks_required * AES256_BLOCK_SIZE);

// Encryption is performed in-place
// put the plaintext where we need it
memcpy(plaintext, ciphertext->ct, ciphertext->ct_len);

err = gcry_cipher_decrypt(handle, plaintext, AES256_BLOCK_SIZE * blocks_required, NULL, 0);
if (err) { handlegcrypterr(err); }

free(handle);

decryption ** result = malloc(sizeof(decryption *));
*result = (decryption *) malloc(sizeof(decryption));

(*result)->pt = plaintext;
(*result)->pt_len = blocks_required * AES256_BLOCK_SIZE;

return result;
}
int main (void)
{

// Test values taken from NIST 800-38a, section F.2.5
// https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf

/* A 256 bit key */
unsigned char key[32] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4};

/* A 128 bit IV */
unsigned char iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};

/* Message to be encrypted */
const unsigned int plaintext_size = 64;
unsigned char plaintext[64] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10};

encryption ** gcrypt_encrypted = encrypt_gcrypt (plaintext, plaintext_size, key, iv);

printf("\ngcrypt Ciphertext is:\n");
print_encryption(*gcrypt_encrypted);

/* Decrypt the ciphertext */
decryption ** gcrypt_decrypted = decrypt_gcrypt(*gcrypt_encrypted, key, iv);
printf("\ngcrypt Plaintext is:\n");
print_decryption(*gcrypt_decrypted);

return 0;
}
_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gcrypt-devel
Re: Oddity in AES encryption? [ In reply to ]
Hello,

On 22.10.2019 1.42, Clay Shields wrote:
>
> Hi,
>
> I am working on a C programming project that involves encrypting files with AES-256-CBC. I was testing libgcrypt and was comparing it to other libraries when I noticed that the the output of libgcrypt seems to reverse the order of the final two blocks compared to the openssl library. I then used test values from NIST 800-38a and found that the final two blocks produced by libgcrypt are swapped from what the standards recommend. Libgcrypt is able to decrypt its own output but fails to decrypt the output of openssl.

Oddity is actually caused by ciphertext stealing. With CTS enabled, the last two blocks of CBC ciphertext get swapped when input length is multiply of cipher's blocksize. Running your example without "GCRY_CIPHER_CBC_CTS", I get:

gcrypt Ciphertext is:
0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{..
0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,}
0020 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a
0030 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j..

-Jussi

>
> Using the test vectors from F.2.5 of NIST Special Publication 800-38A (https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), I get:
>
> SSl Ciphertext :
> 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{..
> 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,}
> 0020 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a
> 0030 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j..
>
> gcrypt Ciphertext is:
> 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{..
> 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,}
> 0020 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j..
> 0030 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a
>
> I believe that according to the NIST specs, libgcrypt has swapped the blocks labelled 0020 and 0030.
>
> Perhaps I have done something wrong or unusual in my use of the library?
>
> Thanks,
>
> Clay
>

_______________________________________________
Gcrypt-devel mailing list
Gcrypt-devel@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gcrypt-devel