Skip to main content
ZeroKeyUSB cifra cada bloque de credencial usando AES-128 en modo CBC. El ECB de bloque único se hace en el ATECC608A usando una clave que nunca sale del chip; el MCU SAMD21 envuelve las llamadas al chip con el encadenamiento CBC y gestiona la E/S a la EEPROM.

Material de clave

La clave maestra AES es un valor aleatorio de 16 bytes generado por el TRNG del ATECC608A al primer arranque, escrito al slot 8 del chip, y luego bloqueado por el lock de la zona de datos. Como IsSecret=1 se aplica antes de bloquear la zona de datos, incluso un atacante con acceso físico I²C no puede leer la clave AES del chip — el comando Read se niega a devolverla.

¿Por qué el chip y no el MCU?

El firmware anterior corría AES software en el SAMD21 con la clave guardada en EEPROM, porque la variante MAHDA-T del ATECC608A se entrega con el comando AES hardware deshabilitado. Habilitarlo requiere:
  1. Escribir el bit AES_Enable (byte 13, bit 0) de la Config Zone.
  2. Configurar el slot 8 con KeyType=6 (AES).
  3. Bloquear la Config Zone para que esos ajustes tomen efecto.
El firmware ahora hace los tres pasos la primera vez que arranca. El compromiso: los bloques AES ahora cruzan el bus I²C, lo que es más lento (~10 ms por bloque frente a ~0,1 ms en software). Para una lectura de credencial que descifra 3×32 bytes son ≈60 ms — imperceptible para el usuario.

Implementación del encadenamiento CBC

Las funciones cbcEncrypt32 / cbcDecrypt32 en zerokey-security.cpp procesan cada campo de credencial de 32 bytes como dos bloques de 16 bytes encadenados contra el IV del dispositivo:

Cifrado

Descifrado

El MCU solo ve bloques de plaintext (entrada al cifrar, salida del descifrar) y bloques de ciphertext (salida del cifrar, entrada al descifrar). Nunca ve la clave AES.

Formato wire de cada llamada AES

Para cada bloque de 16 bytes: Cada llamada tarda ~10 ms incluyendo el overhead I²C.

Padding

Cada campo de credencial (sitio, usuario, contraseña) son hasta 32 bytes en RAM. Antes del cifrado:
  1. Los caracteres espacio (0x20) finales se reemplazan con 0xFF desde el final hacia dentro.
  2. El campo llena directamente el buffer de página de 32 bytes; cualquier cola sin usar queda en 0xFF.
Al descifrar, bufferToString() quita los bytes 0xFF y lee hasta \0 o 0xFF.

Flujo por operación

lock() — cifrar y escribir credenciales

  1. Reemplazar espacios finales en currentSite, currentUser, currentPass con 0xFF.
  2. Cargar el IV desde EEPROM (loadIVfromEEPROM()).
  3. Para cada uno de los 3 campos:
    • Copiar hasta 32 bytes al buffer de 32 bytes, rellenando cualquier cola sin usar con 0xFF.
    • Llamar a cbcEncrypt32(iv, plain, encrypted) — dos llamadas AES al ATECC por debajo.
    • Escribir el ciphertext de 32 bytes a la página correcta de EEPROM.

unlock() — descifrar y cargar credenciales

  1. Cargar el IV desde EEPROM.
  2. Comprobación de auto-curación: si el slot 0 página 0 es 0xFF crudo, llamar a silentEraseAll().
  3. Para cada uno de los 3 campos:
    • Leer el ciphertext de 32 bytes desde EEPROM.
    • Llamar a cbcDecrypt32(iv, encrypted, decrypted) — dos llamadas AES al ATECC.
    • Copiar los 32 bytes a currentSite / currentUser / currentPass.

Reporte de errores

Cuando un round-trip AES falla, el firmware preserva el código de respuesta del chip y lo muestra en el OLED en lugar de un error genérico. El formato es AES E<n> RC<x> SS<XX> más una segunda línea LC=<x> LV=<x> KT=<n> que muestra el estado de lock y key-type del chip en el momento del fallo: RC es el código de nivel driver (-1 wake, -2 I²C, -3 CRC, -4 chip status error, -5 timeout). SS es el byte de status crudo del chip (0x0F execution error, 0x03 parse, 0x07 self-test, …). La combinación te dice exactamente por qué el chip rechazó la llamada. Locked + KT=1 (en lugar de 6) significa que el chip está permanentemente mal configurado para AES; ese es el único modo de fallo que no se puede limpiar con un reinicio.

Consideraciones de seguridad

La clave AES no se puede rotar ni recuperar una vez bloqueada la zona de datos. Si el elemento seguro falla, cada credencial cifrada bajo él se vuelve ilegible. Usa el comando de backup USB-CDC en un host de confianza antes de depender del dispositivo a largo plazo.