¿OpenSSL implementó AES no de acuerdo con NIST?

2

He estado usando el código de cifrado / descifrado de enlace .

Los vectores de prueba NIST ( enlace ) para AES no producen el resultado esperado. ¿Puedo saber cuál podría ser el motivo y cómo puedo hacer que funcione según los vectores de prueba del NIST?

Para facilitar la lectura, agregaré el siguiente código: #include

#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>


int main(int arc, char *argv[])
{
/* Set up the key and iv. Do I need to say to not hard code these in a
* real application? :-)
*/

/* A 256 bit key */
unsigned char ***key** = "0000000000000000000000000000000000000000000000000000000000000000";

/* A 128 bit IV */
unsigned char ***iv** = "00000000000000000000000000000000";

/* Message to be encrypted */
unsigned char *plaintext =
"80000000000000000000000000000000";

/* Buffer for ciphertext. Ensure the buffer is long enough for the
 * ciphertext which may be longer than the plaintext, dependant on the
 * algorithm and mode
 */
unsigned char ciphertext[128];

/* Buffer for the decrypted text */
unsigned char decryptedtext[128];

int decryptedtext_len, ciphertext_len;

/* Initialise the library */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);

/* Encrypt the plaintext */
ciphertext_len = encrypt(plaintext, strlen(plaintext), key, iv,
ciphertext);

/* Do something useful with the ciphertext here */
printf("Ciphertext is:\n");
BIO_dump_fp(stdout, ciphertext, ciphertext_len);

/* Decrypt the ciphertext */
decryptedtext_len = decrypt(ciphertext, ciphertext_len, key, iv,
decryptedtext);

/* Add a NULL terminator. We are expecting printable text */
decryptedtext[decryptedtext_len] = '
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>


int main(int arc, char *argv[])
{
/* Set up the key and iv. Do I need to say to not hard code these in a
* real application? :-)
*/

/* A 256 bit key */
unsigned char ***key** = "0000000000000000000000000000000000000000000000000000000000000000";

/* A 128 bit IV */
unsigned char ***iv** = "00000000000000000000000000000000";

/* Message to be encrypted */
unsigned char *plaintext =
"80000000000000000000000000000000";

/* Buffer for ciphertext. Ensure the buffer is long enough for the
 * ciphertext which may be longer than the plaintext, dependant on the
 * algorithm and mode
 */
unsigned char ciphertext[128];

/* Buffer for the decrypted text */
unsigned char decryptedtext[128];

int decryptedtext_len, ciphertext_len;

/* Initialise the library */
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);

/* Encrypt the plaintext */
ciphertext_len = encrypt(plaintext, strlen(plaintext), key, iv,
ciphertext);

/* Do something useful with the ciphertext here */
printf("Ciphertext is:\n");
BIO_dump_fp(stdout, ciphertext, ciphertext_len);

/* Decrypt the ciphertext */
decryptedtext_len = decrypt(ciphertext, ciphertext_len, key, iv,
decryptedtext);

/* Add a NULL terminator. We are expecting printable text */
decryptedtext[decryptedtext_len] = '%pre%';

/* Show the decrypted text */
printf("Decrypted text is:\n");
printf("%s\n", decryptedtext);

/* Clean up */
EVP_cleanup();
ERR_free_strings();

return 0;
}

void handleErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}

int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
unsigned char *iv, unsigned char *ciphertext)
{
EVP_CIPHER_CTX *ctx;

int len;

int ciphertext_len;

/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

/* Initialise the encryption operation. IMPORTANT - ensure you use a key
 * and IV size appropriate for your cipher
 * In this example we are using 256 bit AES (i.e. a 256 bit key). The
 * IV size for *most* modes is the same as the block size. For AES this
 * is 128 bits */
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleErrors();

/* Provide the message to be encrypted, and obtain the encrypted output.
 * EVP_EncryptUpdate can be called multiple times if necessary
 */
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;

/* Finalise the encryption. Further ciphertext bytes may be written at
 * this stage.
 */
if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;

/* Clean up */
EVP_CIPHER_CTX_free(ctx);

return ciphertext_len;
}

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
unsigned char *iv, unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;

int len;

int plaintext_len;

/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

/* Initialise the decryption operation. IMPORTANT - ensure you use a key
 * and IV size appropriate for your cipher
 * In this example we are using 256 bit AES (i.e. a 256 bit key). The
 * IV size for *most* modes is the same as the block size. For AES this
 * is 128 bits */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
handleErrors();

/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;

/* Finalise the decryption. Further plaintext bytes may be written at
 * this stage.
 */
if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
plaintext_len += len;

/* Clean up */
EVP_CIPHER_CTX_free(ctx);

return plaintext_len;
}
'; /* Show the decrypted text */ printf("Decrypted text is:\n"); printf("%s\n", decryptedtext); /* Clean up */ EVP_cleanup(); ERR_free_strings(); return 0; } void handleErrors(void) { ERR_print_errors_fp(stderr); abort(); } int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) { EVP_CIPHER_CTX *ctx; int len; int ciphertext_len; /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors(); /* Initialise the encryption operation. IMPORTANT - ensure you use a key * and IV size appropriate for your cipher * In this example we are using 256 bit AES (i.e. a 256 bit key). The * IV size for *most* modes is the same as the block size. For AES this * is 128 bits */ if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) handleErrors(); /* Provide the message to be encrypted, and obtain the encrypted output. * EVP_EncryptUpdate can be called multiple times if necessary */ if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) handleErrors(); ciphertext_len = len; /* Finalise the encryption. Further ciphertext bytes may be written at * this stage. */ if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors(); ciphertext_len += len; /* Clean up */ EVP_CIPHER_CTX_free(ctx); return ciphertext_len; } int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv, unsigned char *plaintext) { EVP_CIPHER_CTX *ctx; int len; int plaintext_len; /* Create and initialise the context */ if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors(); /* Initialise the decryption operation. IMPORTANT - ensure you use a key * and IV size appropriate for your cipher * In this example we are using 256 bit AES (i.e. a 256 bit key). The * IV size for *most* modes is the same as the block size. For AES this * is 128 bits */ if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) handleErrors(); /* Provide the message to be decrypted, and obtain the plaintext output. * EVP_DecryptUpdate can be called multiple times if necessary */ if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) handleErrors(); plaintext_len = len; /* Finalise the decryption. Further plaintext bytes may be written at * this stage. */ if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors(); plaintext_len += len; /* Clean up */ EVP_CIPHER_CTX_free(ctx); return plaintext_len; }

La salida que obtengo es:

El texto cifrado es:

0000 - 0d d2 52 e5 3c 69 ad 57-f2 93 1c 2b 0b 1b 84 a6 ..R.

0010 - 3e c8 b7 ca c5 67 65 82-16 df ac 51 cc ae d7 20 > .... ge .... Q ...

0020 - 6a 2a 39 dc 8f f9 2a b7-e5 6d 27 47 c5 36 ef 65 j * 9 ... * .. m'G.6.e

El texto descifrado es:

80000000000000000000000000000000

Pero de acuerdo con el Vector de prueba NIST, la salida debe ser: ddc6bf790c15760d8d9aeb6f9a75fd4e

    
pregunta RSH 10.04.2015 - 09:04
fuente

1 respuesta

4

No he probado OpenSSL pero estoy bastante seguro de que implementa AES-CBC correctamente. Sin embargo, su programa, obviamente, utiliza datos diferentes, por lo que no es sorprendente que obtenga resultados diferentes.

Los vectores de prueba se dan en hexadecimal. Por ejemplo

KEY = 0000000000000000000000000000000000000000000000000000000000000000
IV = 00000000000000000000000000000000
PLAINTEXT = 80000000000000000000000000000000
CIPHERTEXT = ddc6bf790c15760d8d9aeb6f9a75fd4e

tiene una clave de 32 bytes (el tamaño correcto para AES-256) en el que todos los bytes tienen el valor 0. Del mismo modo, el 16 bytes IV tiene todos los bytes 0, el texto plano consiste en un byte con el valor 0x80 = 128 seguido de 15 bytes con el valor 0, y el texto cifrado es la cadena de 16 bytes dada.

Su programa está utilizando datos que, en la notación NIST,

KEY = 3030303030303030303030303030303030303030303030303030303030303030
IV = 30303030303030303030303030303030
PLAINTEXT = 38303030303030303030303030303030

Es decir, su clave consta de 64 bytes, cada uno de los cuales es el código ASCII del carácter 0 , etc.

El resultado de su cálculo es correcto, por cierto (por lo que quiero decir, la salida que publicó es correcta para los datos de su programa). Sin embargo, hay varias cosas que debe tener en cuenta sobre los tamaños que serían importantes en un programa real. Su variable key contiene una matriz de 65 bytes (64 bytes 0x30=48='0' (dígito cero) más un byte nulo de terminación): esto funciona porque el cálculo solo usa los primeros 32 bytes de la matriz pero hace que el programa sea confuso. Lo mismo se aplica a la IV. Para el texto simple, calcula la longitud con strlen ; tenga en cuenta que esto solo funciona si los datos no contienen ningún byte nulo, por lo que funciona si sus datos son una cadena C pero no para datos binarios arbitrarios. Enumera 48 bytes de salida, pero alimenta 32 bytes de entrada, por lo que la salida es en realidad solo de 32 bytes.

En caso de que esté confundido acerca del tamaño de salida: cada bloque de entrada en el cifrado CBC produce un bloque de salida. En la práctica, la salida del cifrado CBC es mayor porque no solo contiene la salida del algoritmo CBC, por dos razones. Primero, la IV se envía generalmente como un prefijo al texto cifrado, por lo que hay un bloque adicional al comienzo del texto cifrado que es la IV. En otras palabras, un "mensaje cifrado CBC" generalmente consiste en el IV seguido del resultado real del algoritmo CBC. En segundo lugar, CBC solo especifica cómo cifrar datos cuyo tamaño es un múltiplo del tamaño de bloque (16 bytes para AES, independientemente del tamaño de la clave). Por lo tanto, para cifrar datos de tamaño arbitrario, debe aplicar un algoritmo de relleno para obtener un número entero de bloques y luego aplicar CBC al resultado del algoritmo de relleno. Las funciones de OpenSSL que usa aplican PKCS # 7 padding (una opción común), que resulta en la el tamaño de salida se redondea a un número entero de bloques o, cuando el tamaño de entrada es un número entero de bloques, un bloque de salida adicional. Para resumir:

              CBC
32 bytes ------------> 32 bytes

           PKCS#7 padding                CBC                 preprend IV
32 bytes -----------------> 48 bytes -----------> 48 bytes ---------------> 64 bytes
    
respondido por el Gilles 10.04.2015 - 15:47
fuente

Lea otras preguntas en las etiquetas