Directorio de artículos
1. AES y CBC
AES significa Estándar de cifrado avanzado y es un algoritmo de cifrado de clave simétrica que se utiliza para cifrar y descifrar datos electrónicos. Los ejemplos de este blog utilizan claves de 128 bits para cifrar y descifrar datos.
El modo CBC es un modo de cifrado de cifrado de bloque comúnmente utilizado en algoritmos de cifrado simétrico. El diagrama de estructura específico de CBC es el siguiente:
cuando escribimos un algoritmo y necesitamos implementar este proceso, ¿cómo debemos hacerlo? Al consultar los dos enlaces siguientes, podemos adquirir algo de experiencia.
https://github.com/kokke/tiny-AES-c/blob/master/aes.c
https://github.com/kokke/tiny-AES-c/blob/master/test.c
2. aes.c
En aes.c, el algoritmo AES correspondiente se implementa utilizando una variedad de modos de cifrado de bloques diferentes. Aquí nos centramos en la implementación del CBC.
El primero es AES_CBC_encrypt_buffer
el método.
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
size_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
Este código es una implementación de función que utiliza el modo CBC del algoritmo AES para el cifrado.
El nombre de la función AES_CBC_encrypt_buffer
acepta tres parámetros: ctx
un puntero al contexto AES, buf
el búfer de datos que se cifrará length
y la longitud de los datos que se cifrarán.
La lógica principal de la función es la siguiente:
- Inicialice un puntero
Iv
que apunte al vector de inicialización en el contextoctx
. - Utilice un bucle para iterar a través
buf
de los datos en el búfer de datos, procesando una longitud de bloque AES (AES_BLOCKLEN) de datos a la vez. - En cada iteración, primero se aplica XOR al búfer de datos
buf
con el vector de inicialización , que es una de las características del modo CBC que puede aumentar la seguridad del cifrado.Iv
- Tome el búfer de datos XOR como entrada y cifrelo
buf
usando la función de cifrado del algoritmo AES ( ), usando la clave redonda en el contexto ( ).Cipher
ctx
RoundKey
- Utilice el búfer de datos cifrados actual
buf
como vector de inicialización para la siguiente iteración. - Actualice el puntero del búfer de datos
buf
para que apunte al siguiente bloque AES. - Repita hasta que se hayan procesado todos los datos.
- Finalmente, el último bloque de datos cifrados se utiliza como vector de inicialización la próxima vez que se llama a la función y se almacena en el
ctx
vector de inicialización (Iv
) en el contexto.
Los métodos correspondientes XorWithIv
son los siguientes.
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
buf[i] ^ Iv[i]
Se puede ver que cada vez que se llama a este método, la posición correspondiente de buf será reemplazada AES_BLOCKLEN
por texto cifrado.
3. prueba.c
En el programa de prueba podemos ver que está definido el vector de inicialización Iv, la clave correspondiente a AES con diferentes dígitos, así como el texto plano a cifrar y el resultado de salida esperado.
static int test_encrypt_cbc(void)
{
#if defined(AES256)
uint8_t key[] = {
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 };
uint8_t out[] = {
0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b };
#elif defined(AES192)
uint8_t key[] = {
0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b };
uint8_t out[] = {
0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd };
#elif defined(AES128)
uint8_t key[] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
uint8_t out[] = {
0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 };
#endif
uint8_t iv[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
uint8_t in[] = {
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 };
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CBC_encrypt_buffer(&ctx, in, 64);
printf("CBC encrypt: ");
if (0 == memcmp((char*) out, (char*) in, 64)) {
printf("SUCCESS!\n");
return(0);
} else {
printf("FAILURE!\n");
return(1);
}
}
Al final de este método, se utiliza el método memcmp para comparar la entrada y la salida para determinar si AES_CBC se ejecuta correctamente y ha logrado la función de verificación.