Oddity in AES encryption?

Clay Shields clay at cs.georgetown.edu
Tue Oct 22 00:42:27 CEST 2019


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;
}


More information about the Gcrypt-devel mailing list