ZeroKeyUSB usa un PIN maestro (1–16 dígitos) para autenticar al usuario. El proceso de verificación combina una comparación software de hash a tiempo constante con un contador monotónico hardware en el ATECC608A para bloquear intentos de fuerza bruta tanto a nivel software como hardware.Documentation Index
Fetch the complete documentation index at: https://docs.zerokeyusb.com/llms.txt
Use this file to discover all available pages before exploring further.
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():
Contador hardware — límite hard
Counter0 en el ATECC608A es un contador hardware monotónico. Sus propiedades clave:- Incrementa en 1 con cada llamada a
counterIncrement()— una vez por intento de PIN. - No se puede decrementar, resetear ni rebajar por software, ciclo de alimentación ni reset hardware.
- Persiste a través de pérdidas de alimentación dentro de la memoria no volátil del chip.
- El umbral guardado en EEPROM
0x0020es un uint32 (little-endian) que representa el valor del contador al que ocurre el siguiente borrado.
| Evento | Efecto en contador | Efecto en umbral |
|---|---|---|
| Intento de PIN incorrecto | +1 | sin cambios |
| PIN correcto | +1 | resetea a Counter0 + 50 |
| Counter ≥ umbral | — | dispara eraseAll() |
Delays adaptativos — backoff soft
Guardado en EEPROM0x0002. Este contador a nivel UX se resetea en un PIN correcto:
| Intentos fallidos | Tiempo de espera |
|---|---|
| 0 | ninguno |
| 1 | 5 s |
| 2 | 10 s |
| 3 | 20 s |
| 4 | 40 s |
| 5 | 80 s |
| 6 | 160 s |
| 7 | 320 s |
| 8 | 640 s |
| 9 | 1 280 s |
| ≥ 10 | 2 560 s (≈ 43 min) |
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 lee Counter0 actual; el umbral se pone a
Counter0 + 50. - 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 62 slots de credenciales × 4 páginas con blancos cifrados.
- Limpia los metadatos TOTP.