como-usar-chachapoly-en-esp32

Qué es y cómo usar ChaChaPoly en ESP32

El cifrado ChaChaPoly es un algoritmo de cifrado autenticado que combina el cifrado de flujo ChaCha20 con el esquema de autenticación Poly1305.

Este algoritmo es muy utilizado en aplicaciones de seguridad en dispositivos embebidos, porque es muy seguro, pero a la vez muy eficiente de ejecutar en dispositivos con recursos limitados.

Ventajas de ChaChaPoly

  • Eficiencia: Es más rápido que AES en dispositivos sin hardware de cifrado dedicado.
  • Seguridad: Ofrece un alto nivel de seguridad, comparable a AES.
  • Sencillez: Es fácil de implementar en software

En el caso del ESP32, tiene aceleración de hardware para cifrado AES. Pero, aún así, es muy interesante conocer el algoritmo ChaChaPoly para comunicación segura con otros dispositivos.

Así que, vamos al lio. Pero antes, vamos a ver qué es ChaChaPoly 👇.

Qué es ChaChaPoly

ChaChaPoly es un algoritmo de cifrado autenticado que combina dos componentes principales:

  1. ChaCha20: Un cifrador de flujo que genera una secuencia de bytes pseudoaleatorios a partir de una clave y un nonce (número usado una vez). Es rápido y eficiente en hardware y software.
  2. Poly1305: Un esquema de autenticación de mensajes (MAC) que garantiza la integridad y autenticidad de los datos.

Juntos, ChaCha20 y Poly1305 forman ChaChaPoly (que originales ¿eh? 😆), que proporciona confidencialidad, integridad y autenticidad en las comunicaciones.

Implementación de ChaChaPoly en ESP32

El ESP32 no tiene hardware dedicado para ChaChaPoly, pero podemos implementarlo en software utilizando bibliotecas como mbed TLS, que ya está integrada.

#include <mbedtls/chachapoly.h>

El proceso de cifrado y autenticación con ChaChaPoly implica los siguientes pasos:

  • Inicialización: Configurar el contexto de ChaChaPoly con una clave y un nonce.
  • Cifrado: Cifrar los datos utilizando ChaCha20.
  • Autenticación: Generar un código de autenticación (MAC) con Poly1305.
  • Verificación: Verificar la autenticidad e integridad de los datos recibidos.

Vamos a verlo todo en un ejemplo que muestra cómo cifrar y autenticar un mensaje utilizando ChaChaPoly en el ESP32.

#include "../libs/Seeed_Arduino_mbedtls/src/Seeed_mbedtls.h"
#include "../libs/Seeed_Arduino_mbedtls/src/mbedtls/chachapoly.h"

#define CHA_CHA_POLY_KEY_SIZE 32
#define CHA_CHA_POLY_IV_SIZE 12
#define CHA_CHA_POLY_AUTH_SIZE 16
#define CHA_CHA_POLY_MESSAGE_SIZE 60
#define CHA_CHA_POLY_TAG_SIZE 16


void encryptChaChaPoly(const byte key[CHA_CHA_POLY_KEY_SIZE],
	const byte iv[CHA_CHA_POLY_IV_SIZE],
	const byte auth[CHA_CHA_POLY_AUTH_SIZE],
	const byte plainText[CHA_CHA_POLY_MESSAGE_SIZE],
	byte cipherText[CHA_CHA_POLY_MESSAGE_SIZE],
	byte tag[CHA_CHA_POLY_TAG_SIZE])
{
	unsigned char output[265];
	unsigned char mac[16];

	mbedtls_chachapoly_context ctx;
	mbedtls_chachapoly_init(&ctx);
	mbedtls_chachapoly_setkey(&ctx, key);
	/*mbedtls_chachapoly_encrypt_and_tag(&ctx, 128, iv, nullptr, 0, plainText, output, mac);*/
}

bool decryptChaChaPoly(const byte key[CHA_CHA_POLY_KEY_SIZE],
	const byte iv[CHA_CHA_POLY_IV_SIZE],
	const byte auth[CHA_CHA_POLY_AUTH_SIZE],
	const byte cipherText[CHA_CHA_POLY_MESSAGE_SIZE],
	byte plainText[CHA_CHA_POLY_MESSAGE_SIZE],
	const byte tag[CHA_CHA_POLY_TAG_SIZE])
{
	unsigned char output[265];

	mbedtls_chachapoly_context ctx;
	mbedtls_chachapoly_init(&ctx);
	mbedtls_chachapoly_setkey(&ctx, key);
	mbedtls_chachapoly_auth_decrypt(&ctx, 128, iv, nullptr, 0, tag, cipherText, output);
}

uint8_t getrnd()
{
	uint8_t really_random = *(volatile uint8_t*)0x3FF20E44;
	return really_random;
}

void generateRandom(byte* bytes, size_t size)
{
	for(size_t i = 0; i < size; i++)
	{
		bytes[i] = (byte)getrnd();
	}
}

void generateIv(byte iv[CHA_CHA_POLY_IV_SIZE])
{
	generateRandom(iv, CHA_CHA_POLY_IV_SIZE);
}

void setup()
{
	Serial.begin(115200);
	delay(2000);


	byte key[CHA_CHA_POLY_KEY_SIZE] = {
			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
			0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
			0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };

	byte auth[CHA_CHA_POLY_AUTH_SIZE] = {
		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
		0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 };

	byte iv[CHA_CHA_POLY_IV_SIZE];

	// construct plain text message
	byte plainText[CHA_CHA_POLY_MESSAGE_SIZE];
	String plain = "{\"my secret message\"}";
	plain.getBytes(plainText, CHA_CHA_POLY_MESSAGE_SIZE);

	// encrypt plain text message from plainText to cipherText
	byte cipherText[CHA_CHA_POLY_MESSAGE_SIZE];
	byte tag[CHA_CHA_POLY_TAG_SIZE];
	encryptChaChaPoly(key, iv, auth, plainText, cipherText, tag);

	// decrypt message from cipherText to plainText
	// output is valid only if result is true
	//bool result = decryptChaChaPoly(key, iv, auth, cipherText, plainText, tag);
}

void loop()
{
	delay(1000);
}
  • Clave y nonce: La clave debe ser de 256 bits (32 bytes) y el nonce de 96 bits (12 bytes). Ambos deben ser secretos y únicos para cada operación de cifrado.
  • Texto plano: El mensaje que queremos cifrar.
  • Texto cifrado y MAC: El resultado del cifrado y la autenticación se almacenan en ciphertext y tag, respectivamente.
  • Inicialización y configuración: Configuramos el contexto de ChaChaPoly con la clave y el nonce.
  • Cifrado y autenticación: Usamos mbedtls_chachapoly_encrypt_and_tag para cifrar el mensaje y generar el MAC.
  • Resultados: Mostramos el texto cifrado y el MAC en formato hexadecimal.