¿Por qué la primera llamada a DecryptUpdate en AES-CBC devuelve 16 bytes menos?

4

Al descifrar un archivo mediante el modo CBC del algoritmo AES, la longitud de salida es 16 bytes menor que la de la longitud de entrada. Por ejemplo, si pasa un búfer de 512 bytes, descifra 496 bytes. Sucede solo en modo CBC para la primera llamada de EVP_DecryptUpdate() . ¿Por qué es así?

while((bytes = fread (buffer, 1, 32, fp)) != 0) 
{
    EVP_DecryptUpdate(e, buffer_out, &c_len, buffer, bytes);
    ret = fwrite(buffer_out, 1, c_len, fpout); 
    printf("c_len %d bytes %d ret %d\n", c_len, bytes, ret);
} 
    
pregunta Rak 27.04.2015 - 12:09
fuente

2 respuestas

7

AES opera sobre bloques de 16 bytes. El EVP_DecryptUpdate() operará en bloques de 16. Mientras esté usando EVP_DecryptUpdate() , se supone que le dará más datos para descifrar y retiene el último bloque de datos dentro del contexto de cripta. Esto se debe a que en el modo CBC necesita que el siguiente bloque de cifrado sea el IV para el siguiente bloque de descifrado porque no puede predecir cuándo lo necesitará para calcular el relleno.

Imagen de Wiki

La"longitud descifrada" será exactamente un bloque (16 bytes) menos de lo que le das para la primera iteración porque retiene el segundo bloque de datos. Una vez que descifre la segunda iteración, genera el bloque 2 y el bloque 3 (32 bytes), y retiene el bloque 4. Extrapolando esto al final de su archivo cifrado, se dará cuenta de que le faltaría un bloque. Una vez que llama a EVP_DecryptFinal , el bloque final se descifra (se rellena el relleno si es necesario) y se envía al búfer dado para los 32 bloques completos (512 bytes en su ejemplo).

Código de ejemplo

char *decrypt (char *key,
               char *iv,
               char *encryptedData,
               int encryptedLength)
{
    // Initialisation
    EVP_CIPHER_CTX *cryptCtx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(cryptCtx);
    int decryptedLength = 0;
    int allocateSize = encryptedLength * sizeof(char);
    int lastDecryptLength = 0;
    char *decryptedData = (char *) malloc (allocateSize);
    memset(decryptedData, 0x00, allocateSize);
    int decryptResult = EVP_DecryptInit_ex(cryptCtx,
        EVP_bf_cbc(), NULL, key, iv);

    // EVP_DecryptInit_ex returns 1 if it succeeded.
    if (decryptResult == 1)
    {
        decryptResult = EVP_DecryptUpdate(cryptCtx, decryptedData,
            &decryptedLength, encryptedData, encryptedLength);

        // Note that EVP_DecryptUpdate will alter the value of the third parameter 
        // to be equal to the amount of data that was written. This is not always the    
        // entire length of the decrypted data! To finish the decryption process, use 
        // EVP_DecryptFinal_ex. This will decrypt any remaining data.

        // Cleanup
        if (decryptResult == 1)
        {
            // Stick the final data at the end of the last
            // decrypted data.
            EVP_DecryptFinal_ex(cryptCtx,
                decryptedData + decryptedLength,
                &lastDecryptLength);
            decryptedLength = decryptedLength + lastDecryptLength;
            decryptedData[decryptedLength – 1] = ”;
            printf ("Decrypted size: %d\n", decryptedLength);
            printf ("Decrypted data: \n%s\n\n", decryptedData);
        }
        else
        {
            printf ("EVP_DeccryptUpdate failure.\n");
        }
    }
    else
    {
        printf ("EVP_DecryptInit_ex failure.\n");
    }
    EVP_CIPHER_CTX_free(cryptCtx);
    EVP_cleanup();
    return decryptedData;
}
    
respondido por el RoraΖ 27.04.2015 - 13:14
fuente
1

CBC es un modo de operación de bloque . Una forma de ver los modos de operación es que el cifrado es en realidad un parámetro para el modo de operación. En caso de que se realicen operaciones CBC, se utilizará el tamaño de bloque del cifrado subyacente. Esto es de 16 bytes o 128 bits para AES.

El problema con CBC es que requiere esos 16 bytes completos, incluso si estos bytes no están disponibles en el último bloque de texto plano. De alguna manera estos 16 bytes necesitan estar presentes. Esto generalmente se hace con un relleno compatible con PKCS # 7, aunque existen otras posibilidades como el relleno de bits o el robo de texto cifrado (CTS).

Ahora el relleno PKCS # 7 es determinista. Esto significa que cualquier cosa que esté en el texto plano, el desempaquetado tendrá éxito. Esto no es un problema si hay 1..15 bytes de relleno pero sí significa que el relleno de 0 bytes representa un problema. Si los últimos bytes son idénticos a un relleno correcto, el no relleno no puede distinguir entre el relleno y el texto sin formato (ya que no conoce el tamaño del texto sin formato o el relleno). La solución adoptada por el modo de relleno PKCS # 7 es la de siempre , incluso si el tamaño de entrada es un número de veces el tamaño de bloque. Es por eso que el texto sin formato de 512 bytes es 16 bytes más grande que el texto cifrado: se agregó un bloque completo de relleno durante el cifrado y se eliminó durante el descifrado . En otras palabras, el relleno PKCS # 7 toma 1..16 bytes para AES.

Ahora el problema es que el modo de cifrado CBC no sabe cuándo se ha llegado al final del texto cifrado y, por lo tanto, se ha alcanzado el relleno. Esto significa que no puede devolver directamente los últimos 16 bytes: puede ser el relleno que se debe eliminar. Por lo tanto, almacenará 16 bytes hasta que se llame al método EVP_DecryptFinal(_ex) .

La mayoría de los otros modos de operación convierten el cifrado de bloque en un cifrado de flujo (excepto el modo de operación ECB inseguro). Por lo tanto, no tendría este problema en particular si estuviera usando los modos de operación CTR, CFB u OFB. Los cifrados de flujo cifran cada bit, prácticamente hablando cada byte, del texto sin formato por separado, incluso si el cifrado subyacente tiene un tamaño de bloque mayor.

    
respondido por el Maarten Bodewes 27.04.2015 - 20:11
fuente

Lea otras preguntas en las etiquetas