Cómo se guarda el PIN
El PIN nunca se guarda en texto plano. Al configurar el PIN (storeSignature()):
- Los dígitos del PIN del usuario se leen desde
pinArray[16](cada byte contiene un valor de dígito 0–9). derivePinKey()computa:SHA-256(pinArray[16] ∥ chip_serial[9]).chip_seriales el serial único de 9 bytes leído desde la Config Zone del ATECC608A.
- El hash resultante de 32 bytes se escribe en EEPROM en
0x0048–0x0067. - El mismo hash de 32 bytes también se escribe en el slot 9 del ATECC (para uso potencial futuro con CheckMac).
Secuencia de unlock
Cada intento de unlock ejecuta los siguientes pasos enverifySignature():
Counter0, ni lectura de umbral, ni borrado automático. Un diseño anterior usaba el Counter0 monotónico del ATECC608A para borrar la bóveda tras 50 PINs incorrectos; eso se eliminó. La defensa real es el backoff persistente descrito abajo.
Rate-limiting persistente — la defensa real contra fuerza bruta
No hay borrado automático tras un número de intentos fallidos; la bóveda nunca se destruye por PINs incorrectos. En su lugar, cada intento se ralentiza con un backoff exponencial cuyo contador vive en EEPROM (0x0002) y por tanto sobrevive a la pérdida de alimentación.
El detalle clave es cuándo se aplica el retardo. En cada arranque, readConfigurationFlag() llama a waitFromEeprom() antes de que la pantalla de PIN acepte ninguna entrada. Así, un atacante no puede saltarse la penalización cortando la corriente a mitad de la cuenta atrás: tras cada intento fallido, el retardo acumulado se reimpone en el siguiente encendido. Una vez el contador supera ~10 fallos, cada intento adicional cuesta ≈ 43 minutos, de modo que la fuerza bruta online es impracticable (un PIN de 4 dígitos tardaría del orden de un año) — todo ello sin destruir jamás los datos del usuario.
eraseAll() sigue existiendo, pero solo lo dispara manualmente el usuario (reset de fábrica / PIN olvidado) — nunca automáticamente por PINs incorrectos.
Salvedad offline. El rate-limit solo se aplica a los intentos hechos a través del dispositivo. El hash del PIN es legible por I²C (EEPROM0x0048y slot 9 del ATECC conIsSecret=0), así que un atacante que alcance físicamente el bus I²C puede copiar el hash y el serial del chip y crackear el PIN offline sin ningún retardo. Lo que lo impide es la encapsulación en resina que bloquea el acceso al bus — más usar un PIN largo. No es el backoff.
Backoff exponencial — tabla de retardos
Guardado en EEPROM0x0002 y reaplicado al arrancar; se resetea solo con un PIN correcto:
Fórmula:
wait = 5 × 2^(min(intentos, 10) − 1) segundos, con tope en 2 560 s.
Durante el delay, el OLED muestra una barra de progreso y cuenta atrás. El dispositivo no acepta entrada nueva hasta que expire el temporizador.
Gestión segura de entrada
- Los dígitos se almacenan en
pinArray[16]en SRAM y se limpian tras la verificación. - Los eventos táctiles se ignoran durante la espera de lockout (
waitFromEeprom()). - SerialUSB no puede inyectar dígitos del PIN — solo se acepta entrada capacitiva táctil física.
- La comparación del PIN usa un acumulador XOR a tiempo constante (
diff |= stored[i] ^ derived[i]) para evitar timing side-channels.
Cambiar el PIN
Iniciado vía Menú → Change PIN →storeSignature():
- Cuenta atrás de 3 segundos en pantalla (permite aborto seguro).
derivePinKey(pinArray, derived)computa el nuevo hash.- El nuevo hash de 32 bytes se escribe en el slot 9 del ATECC.
- El nuevo hash de 32 bytes se escribe en EEPROM
0x0048. - Se limpia el contador de intentos fallidos (EEPROM
0x0002). - El ping al ATECC confirma que el chip sigue vivo. La clave AES del slot 8 no se toca durante la configuración del PIN — se aprovisiona una vez al primer arranque y es irrevocable.
- El IV se carga o se genera.
- Se escribe el flag de config (
0x42). - Todos los slots de credenciales se re-inicializan silenciosamente con blancos cifrados.
Cambiar el PIN no cambia la clave maestra AES ni re-cifra las credenciales existentes. La clave AES vive dentro del slot 8 del ATECC y se genera una vez por dispositivo; no se puede rotar. El ciphertext existente es descifrable con el mismo chip mientras no se destruya.
PIN olvidado
ZeroKeyUSB no tiene mecanismo de recuperación de PIN. La única opción es un reset de fábrica (eraseAll()), que:
- Muestra una cuenta atrás de 3 segundos.
- Carga el IV del dispositivo.
- Sobrescribe los 61 slots de credenciales × 4 páginas con blancos cifrados.
- Limpia los metadatos TOTP.