2010-06-29 20 views
17

Soy nuevo en OpenSSL. ¿Alguien puede darme una pista sobre cómo inicializar el modo AES CTR desde un archivo C? Sé que esta es la firma del método, pero tengo problemas con los parámetros, no hay mucha documentación ni un ejemplo claro de cómo hacer un simple cifrado. Agradecería si alguien pudiera ejemplificar una llamada a este método. ¡Gracias por adelantado!AES CTR 256 Modo de operación de cifrado en OpenSSL

void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out, 
    const unsigned long length, const AES_KEY *key, 
    unsigned char ivec[AES_BLOCK_SIZE], 
    unsigned char ecount_buf[AES_BLOCK_SIZE], 
    unsigned int *num); 

Hola Caf realmente aprecio su respuesta rápida ha sido muy útil, y defenetly el mejor ejemplo que he encontrado en la web. Intento abrir un archivo con una longitud indeterminada, encriptarlo y escribir otro archivo con el texto cifrado generado, luego abrir el archivo cifrado y recuperar el texto sin formato. Necesito usar un archivo de una cantidad considerable de MB porque me gustaría comparar el rendimiento de la CPU. Sin embargo, sigo teniendo un problema al descifrar. De alguna manera, al descifrar un número considerable de archivos de texto (1504 KB) no se descifrará por completo, y la mitad se encuentra en texto sin formato y la otra mitad aún cifrada. Creo que esto podría estar relacionado con el tamaño del iv o la forma en que estoy llamando al contador. Aquí es lo que tengo hasta ahora:

#include <openssl/aes.h> 
#include <stdio.h> 
#include <string.h> 

struct ctr_state { 
    unsigned char ivec[16]; 
    unsigned int num; 
    unsigned char ecount[16]; 
}; 

FILE *fp; 
FILE *rp; 
FILE *op; 
size_t count; 
char * buffer; 
AES_KEY key; 

int bytes_read, bytes_written; 
unsigned char indata[AES_BLOCK_SIZE]; 
unsigned char outdata[AES_BLOCK_SIZE]; 
unsigned char ckey[] = "thiskeyisverybad"; // It is 128bits though.. 
unsigned char iv[8] = {0};//This should be generated by RAND_Bytes I will take into consideration your previous post 
struct ctr_state state; 

int init_ctr(struct ctr_state *state, const unsigned char iv[8]){  
    state->num = 0; 
    memset(state->ecount, 0, 16);  
    memset(state->ivec + 8, 0, 8); 
    memcpy(state->ivec, iv, 8); 
} 

void encrypt(){ 
    //Opening files where text plain text is read and ciphertext stored  
    fp=fopen("input.txt","a+b"); 
    op=fopen("output.txt","w"); 
    if (fp==NULL) {fputs ("File error",stderr); exit (1);} 
    if (op==NULL) {fputs ("File error",stderr); exit (1);}  

    //Initializing the encryption KEY 
    AES_set_encrypt_key(ckey, 128, &key); 

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext 
while (1) {  
    init_ctr(&state, iv); //Counter call 
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, fp); 
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);  
    bytes_written = fwrite(outdata, 1, bytes_read, op); 
    if (bytes_read < AES_BLOCK_SIZE) 
    break; 
    } 

    fclose (fp); 
    fclose (op); 
    free (buffer); 
} 

void decrypt(){ 
    //Opening files where text cipher text is read and the plaintext recovered   
    rp=fopen("recovered.txt","w"); 
    op=fopen("output.txt","a+b"); 
    if (rp==NULL) {fputs ("File error",stderr); exit (1);} 
    if (op==NULL) {fputs ("File error",stderr); exit (1);} 

    //Initializing the encryption KEY 
    AES_set_encrypt_key(ckey, 128, &key); 

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext 
    while (1) {  
    init_ctr(&state, iv);//Counter call 
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, op); 
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num); 
    bytes_written = fwrite(outdata, 1, bytes_read, rp); 
    if (bytes_read < AES_BLOCK_SIZE) 
    break; 
    } 
    fclose (rp); 
    fclose (op); 
    free (buffer); 
} 

int main(int argc, char *argv[]){ 
    encrypt(); 
    //decrypt(); 
    system("PAUSE"); 
    return 0; 
} 

Cada cifrar y descifrar la función también llamados en diferentes carreras así que todo se inicia siempre con los mismos valores. Gracias de nuevo por los consejos que me pueden proporcionar por adelantado & Saludos !!!

+2

Tu problema es que estás reiniciando el contador después de cada bloque. Esto es incorrecto: mueve la llamada 'init_ctr()' fuera de los bucles 'while()' tanto en el cifrado como en el descifrado. 'indata' y' outdata' tampoco tienen que ser de longitud 'AES_BLOCK_SIZE'; pueden ser considerablemente más grandes. – caf

+1

Debería * no * usar 'AES_encrypt' y sus amigos. Es una implementación de solo software, por lo que no disfrutará de soporte de hardware, como AES-NI. Deberías estar usando las funciones 'EVP_ *'. Ver [EVP Symmetric Encryption and Decryption] (http://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption) en la wiki de OpenSSL. De hecho, probablemente debería estar usando el cifrado autenticado porque proporciona * ambos * confidencialidad y autenticidad. Ver [EVP Authenticated Encryption and Decryption] (http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption) en la wiki de OpenSSL. – jww

+0

Si usa las funciones 'EVP_ *', las cifras de interés son 'EVP_aes_128_ctr',' EVP_aes_192_ctr' y 'EVP_aes_256_ctr'. – jww

Respuesta

26

Por lo general, tendrá la intención de llamar al AES_ctr128_encrypt() repetidamente para enviar varios mensajes con la misma clave y IV, y un contador de incremento. Esto significa que necesita para realizar un seguimiento de la 'IVEC', los valores de los Ecount 'entre las llamadas 'num' y - por tanto, crear un struct de mantener estas, y una función de inicialización:

struct ctr_state { 
    unsigned char ivec[16]; /* ivec[0..7] is the IV, ivec[8..15] is the big-endian counter */ 
    unsigned int num; 
    unsigned char ecount[16]; 
}; 

int init_ctr(struct ctr_state *state, const unsigned char iv[8]) 
{ 
    /* aes_ctr128_encrypt requires 'num' and 'ecount' set to zero on the 
    * first call. */ 
    state->num = 0; 
    memset(state->ecount, 0, 16); 

    /* Initialise counter in 'ivec' to 0 */ 
    memset(state->ivec + 8, 0, 8); 

    /* Copy IV into 'ivec' */ 
    memcpy(state->ivec, iv, 8); 
} 

Ahora, cuando se inicia la comunicación con el destino, que necesita para generar una vía intravenosa para utilizar e inicializar el contador:

unsigned char iv[8]; 
struct ctr_state state; 

if (!RAND_bytes(iv, 8)) 
    /* Handle the error */; 

init_ctr(&state, iv); 

a continuación, tendrá que enviar el 8 bytes IV del destino. Usted también necesitará para inicializar un AES_KEY de sus bytes de clave primas:

AES_KEY aes_key; 

if (!AES_set_encrypt_key(key, 128, &aes_key)) 
    /* Handle the error */; 

Ahora puede comenzar el cifrado de datos y enviarlo al destino, con llamadas repetidas a AES_ctr128_encrypt() así:

if (!AES_ctr128_encrypt(msg_in, msg_out, msg_len, &aes_key, state->ivec, state->ecount, &state->num)) 
    /* Handle the error */; 

(msg_in es un puntero a un búfer que contiene el mensaje de texto claro, msg_out es un puntero a un búfer al que debe ir el mensaje cifrado, y msg_len es la longitud del mensaje).

El descifrado es exactamente el mismo, excepto que no se genera el IV con RAND_bytes() - en cambio, toma el valor que le dio el otro lado.

Importante:

  1. hacer no llamada init_ctr() más de una vez durante el proceso de cifrado. El contador y IV deben inicializarse una vez solo antes del inicio de la encriptación.

  2. Bajo ninguna circunstancia, tenga la tentación de obtener el IV en otro lugar que no sea RAND_bytes() en el lado de la encriptación. No lo ajuste a un valor fijo; no use una función hash; no use el nombre del destinatario; no lo lea desde el disco. Generarlo con RAND_bytes() y enviarlo a su destino. Siempre que comience con un contador cero, debe comenzar con un IV completamente nuevo que nunca haya usado antes.

  3. Si es posible que envíe 2 ** 64 bytes sin cambiar la tecla IV y/o la tecla, tendrá que comprobar si el contador se está desbordando.

  4. No omita la comprobación de errores. Si una función falla y la ignora, es muy posible (incluso probable) que su sistema parezca funcionar normalmente, pero en realidad funcionará de manera completamente insegura.

+3

Déjeme agregar un detalle que se me escapó cuando usé esto: el argumento num es la cantidad de bytes en un bloque que usted es, no el contador. Si está encriptando paquetes (por ejemplo), siempre establezca state-> num en cero y ponga su contador en los bytes altos de iv. –

+0

@Mike Elkins: De hecho, puede tratar tanto 'num' como' ecount' como un estado interno opaco de la implementación de CTR de OpenSSL. En la mayoría de los casos, no debería ser necesario modificarlos directamente. – caf

2

Parece que el problema básico con su programa de pruebas es que los valores del modo de las llamadas fopen no es correcto. Creo que se necesita para cambiar sus llamadas fopen en cifrar a esto:

fp=fopen("input.txt","rb"); 
op=fopen("output.txt","wb"); 

Y los de descifrar a:

rp=fopen("recovered.txt","wb"); 
op=fopen("output.txt","rb"); 

Otra cosa vale la pena señalar es que ckey probablemente debería ser declarado como una Búfer de 32 bytes (256 bits). Es cierto que el cifrado de 128 bits solo usa 16 bytes de los datos de la clave. Pero la función OpenSSL AES_set_encrypt_key (al menos en la versión que estoy usando) lee 32 bytes de ese búfer. Solo usa la cantidad adecuada de bytes, pero la lectura ocurre. Eso significa que si el búfer es solo de 16 bytes y ocurre al final de una página adyacente a una página no legible en la memoria, se produciría una infracción de acceso.

Ah, y acabo de notar que hay una llamada extraña a free allí. La llamada free(buffer); no es válida ya que nunca se asignó el búfer. Me doy cuenta de que su código es solo una prueba simple, pero ... bueno, somos programadores y no podemos ayudarnos a nosotros mismos.

Cuestiones relacionadas