En el mundo de los microcontroladores, la encriptación pesada como AES-256 es fantástica, pero consume demasiada memoria Flash, RAM y ciclos de reloj. Cuando necesitas ofuscar datos en tiempo real (mientras despachas gas a 84MHz), necesitas algo brutalmente rápido. Aquí entra la compuerta lógica XOR (OR Exclusivo).
La magia de XOR radica en su tabla de verdad y su propiedad de reversibilidad matemática. La regla de XOR es simple: "Si los bits son iguales, el resultado es 0. Si son diferentes, el resultado es 1".
La característica que hace al XOR el rey de la encriptación simétrica rápida es la siguiente ecuación:
(Dato Original ⊕ Llave Secreta) = Dato Encriptado
Y para recuperarlo, aplicas exactamente la misma operación:
(Dato Encriptado ⊕ Llave Secreta) = Dato Original
No necesitas una función para encriptar y otra compleja para desencriptar. La misma función de 3 líneas de código hace ambas cosas "In-Place" (sobre el mismo arreglo de memoria), sin desperdiciar un solo byte extra de RAM.
Ejercicio 1: Interceptaremos el búfer de transmisión justo antes de que el controlador de DMA del RS485 envíe el paquete Modbus, y aplicaremos una máscara XOR con una Llave Rotativa. Un solo byte de llave es débil (un atacante lo adivinaría de inmediato). Usaremos un arreglo (array) como llave secreta y lo aplicaremos cíclicamente usando el operador módulo (%) para esconder por completo los patrones repetitivos de nuestro protocolo.
/**
* @file xor_cipher.c
* @brief Encriptador/Desencriptador Simétrico XOR para Tramas UART/RS485
*/
#include "stm32f401xe.h"
#include <stdint.h>
// =========================================================================
// LLAVE SECRETA SIMÉTRICA (Debe coincidir en el STM32 y en el Servidor SCADA)
// En un sistema real avanzado, esta llave puede generarse dinámicamente
// usando el número de serie único del STM32 (UID Register).
// =========================================================================
static const uint8_t LLAVE_SECRETA[] = { 0xA5, 0x3C, 0x81, 0x18, 0xFE, 0x42 };
static const uint8_t TAM_LLAVE = sizeof(LLAVE_SECRETA);
/**
* @brief Aplica el cifrado o descifrado XOR a un búfer de datos.
* La función opera "In-Place", sobrescribiendo el búfer original
* para no desperdiciar memoria RAM creando arreglos paralelos.
*
* @param buffer Puntero al arreglo de bytes que contiene el paquete a procesar.
* @param longitud Cantidad de bytes en el paquete.
*/
void Aplicar_Cifrado_XOR(uint8_t *buffer, uint16_t longitud) {
// Si la longitud es 0 o la llave no existe, abortamos por seguridad
if (longitud == 0 || TAM_LLAVE == 0) return;
for (uint16_t i = 0; i < longitud; i++) {
// El operador ^ en C es el XOR bit a bit (Bitwise XOR).
// Usamos (i % TAM_LLAVE) para iterar cíclicamente sobre la llave.
// Ej: Si el paquete tiene 10 bytes y la llave 6,
// para el byte 7 volverá a usar el índice 0 de la llave.
buffer[i] ^= LLAVE_SECRETA[i % TAM_LLAVE];
}
}
// =========================================================================
// INTEGRACIÓN EN EL PIPELINE DE TRANSMISIÓN/RECEPCIÓN (Modbus)
// =========================================================================
// Dependencias simuladas
extern void DMA_Iniciar_Transmision_RS485(uint8_t *datos, uint16_t len); // Día 138
extern bool Modbus_Validar_CRC(uint8_t* paquete, uint16_t len); // Día 149
/**
* @brief Envía un paquete Modbus encriptado al Servidor
*/
void Transmitir_Paquete_Seguro(uint8_t *paquete_modbus, uint16_t longitud) {
// 1. Opcional: El CRC debe calcularse ANTES de encriptar
// (Para que el servidor lo verifique después de desencriptar).
// Anexar_CRC16_Al_Final(paquete_modbus, &longitud);
// 2. OFUSCACIÓN CRIPTOGRÁFICA
// Destruimos la legibilidad del paquete en la RAM.
Aplicar_Cifrado_XOR(paquete_modbus, longitud);
// 3. ENVÍO FÍSICO AL HARDWARE
// Los datos que viajan por el cable RS485 son irreconocibles.
DMA_Iniciar_Transmision_RS485(paquete_modbus, longitud);
}
/**
* @brief Procesa un paquete crudo recién llegado de la red
*/
void Recibir_Paquete_Seguro(uint8_t *paquete_entrante, uint16_t longitud) {
// 1. DESENCRIPTADO IN-PLACE
// Usamos EXACTAMENTE la misma función. El XOR de la basura
// revelará los datos legibles originales de vuelta a la RAM.
Aplicar_Cifrado_XOR(paquete_entrante, longitud);
// 2. VALIDACIÓN DE INTEGRIDAD Y AUTENTICIDAD
// Si un atacante inyecta ruido o una trama cifrada con la llave incorrecta,
// al desencriptarse generará basura. Esa basura jamás pasará la prueba
// estricta del polinomio matemático del CRC-16.
if (Modbus_Validar_CRC(paquete_entrante, longitud)) {
// ¡El mensaje es auténtico, íntegro y proveniente de una fuente autorizada!
// procesar_comandos_modbus(paquete_entrante);
} else {
// Intento de hackeo o ruido eléctrico severo. Descartar paquete en silencio.
}
}
int main(void) {
// ... Súper Bucle normal ...
return 0;
}
Objetivo del día: Seguridad Perimetral y Mitigación de Ataques (MitM).
En el Día 139 implementamos el Protocolo Modbus RTU. Un hacker que intercepte esos cables verá comandos evidentes como `01 06 00 00 04 1A` (Escribir Precio a $10.50). Con un equipo básico, podría inyectar su propio comando `01 06 00 00 00 01` ordenando al Despachador bajar el precio a 1 centavo, llenar su camión de gas, y largarse.
Al implementar el **Cifrado de Flujo XOR** justo antes del envío por el chip MAX485, el comando `01 06` se convierte en basura indescifrable como `A4 3A`. Como estamos usando una llave rotativa en lugar de un solo byte estático, dos comandos idénticos enviados consecutivamente se verán diferentes en la red. Si el atacante intenta inyectar sus propios comandos `A4 3A`, cuando tu STM32 aplique la llave para "desencriptarlo", generará datos corruptos que el motor de validación **CRC-16 (Día 149)** destruirá matemáticamente de inmediato, evadiendo el ataque por completo sin sobrecargar el procesador como lo harían las librerías mbedTLS o AES.
%) provee una ofuscación efectiva con un costo computacional virtualmente nulo (1 a 2 ciclos de reloj por byte).