# Certifications & Compliance
Source: https://docs.zerokeyusb.com/certifications
Manufacturing standards and laboratory tests that validate ZeroKeyUSB for everyday and professional use.
## Built for global markets
ZeroKeyUSB is produced in facilities that follow **ISO 9001** (quality management) and **ISO 27001** (information security) standards.
Every production batch is traceable — from component sourcing to final encapsulation — ensuring consistent security and durability.
***
## Electrical compliance
| Standard | Scope | Status |
| ----------------------------- | ---------------------------------------------------- | ------------------------------------ |
| **CE** (EN 55032 / EN 55035) | Electromagnetic compatibility for IT equipment. | ✅ Passed, report `EMC-ZKU-2024-CE`. |
| **FCC Part 15, Class B** | Radiated & conducted emissions for consumer devices. | ✅ Passed, report `EMC-ZKU-2024-FCC`. |
| **RoHS Directive 2011/65/EU** | Restriction of hazardous substances. | ✅ All components compliant. |
| **USB-IF Electrical** | USB 2.0 low/full-speed electrical tolerance. | ✅ Verified through third-party lab. |
These certifications guarantee that ZeroKeyUSB can be safely used in homes, offices, and regulated environments.
***
## Environmental robustness
ZeroKeyUSB undergoes stress tests to ensure the resin-encapsulated design survives daily wear:
* **Temperature cycling**: −10 °C to 60 °C, 40 cycles, no failures.
* **Humidity exposure**: 95% RH at 40 °C for 72 hours, no condensation ingress.
* **Salt fog**: 5% NaCl, 24 hours, zero corrosion on exposed contacts.
* **Water resistance**: Meets **IP54** splash protection once connected to USB-C cable.
***
## Data protection practices
While hardware cannot obtain formal “security certifications” without network interfaces, we adopt industry best practices:
* **Secure provisioning**: PIN signature, IV generation, and serial numbers are programmed during final testing on an isolated network.
* **Tamper-evident seals**: Each device ships with a unique holographic seal tied to the serial number.
* **Penetration testing**: Annual third-party audits review firmware, hardware, and supply-chain risks.
Audit summaries are available to enterprise customers under NDA.
***
## Documentation archive
All compliance reports, declarations of conformity, and test certificates are stored in the customer portal.
If you need access:
1. Email **[certs@zerokeyusb.com](mailto:certs@zerokeyusb.com)** with your order number or distributor ID.
2. Receive a secure download link valid for 48 hours.
3. Verify the PDF signatures against the Depbit Lab certificate authority.
***
ZeroKeyUSB is designed to be a long-term security appliance. Certification renewals are scheduled annually to ensure continuous compliance.
# ENS & CCN-STIC positioning
Source: https://docs.zerokeyusb.com/compliance/ens-ccn-stic
How ZeroKeyUSB fits Spain's Esquema Nacional de Seguridad (RD 311/2022) for public-sector buyers, and the CPSTIC / CCN-STIC path as a medium-term goal.
For sales to Spanish public administration — ministries, public universities,
town councils, public hospitals — the key framework is the **Esquema Nacional de
Seguridad (ENS)**, regulated by **Real Decreto 311/2022**. The ENS applies to
public-sector information systems and to the providers that supply them
technology or services.
ZeroKeyUSB is not itself "ENS certified" — ENS conformity applies to systems and
organizations. The device is positioned as a **technical control that helps an
ENS-scoped system meet its requirements**, particularly around identity,
access control and authentication.
## ENS dimensions ZeroKeyUSB supports
The ENS organizes safeguards across operational and protection measures. A
credential device contributes mainly to **access control** and **information
protection**:
| ENS area | Contribution |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Identification & authentication | Strong, unique passwords and TOTP second factors, gated by a Master PIN on a physical device |
| Access control | Credentials are unreachable without the device and PIN; access is granted only on a deliberate action |
| Protection of information | Credentials encrypted at rest with a key held in a secure element; no cloud storage |
| Protection against malware | The vault is off-host and encrypted, reducing the value of an infected endpoint |
| Operation without local software | No agent or driver to install, reducing the managed-system attack surface |
| Traceability of design | Open-source firmware, signed images, and documented [threat model](/compliance/threat-model) support risk documentation |
## The CPSTIC / CCN-STIC path (medium term)
For a security product used by Spanish administration, the reference catalogue is
the **CPSTIC** of the **CCN** (Centro Criptológico Nacional) — the list of
qualified/approved ICT security products for use under the ENS. Inclusion in
CPSTIC typically relies on an evaluation recognized by the CCN, such as **LINCE**
or **Common Criteria**, following the relevant **CCN-STIC** guides.
This is a deliberate, later phase. The groundwork already in place helps:
* **Signed firmware and a hardware-protected bootloader** (secure boot).
* **A defined product category** — "offline secure credential storage device."
* **Architecture and threat documentation** that an evaluation would build on.
* **A secure provisioning process** and permanently locked secure element.
A realistic sequence, when a large public buyer requires it:
1. Scope the product as an evaluable target of evaluation.
2. Prepare architecture, threat model and secure-manufacturing documentation.
3. Engage an accredited lab for **LINCE** (or Common Criteria) as CPSTIC requires.
4. Apply for **CPSTIC** listing.
## Suggested statement
> ZeroKeyUSB is designed to support the access-control and authentication
> requirements of the Spanish National Security Framework (ENS, RD 311/2022) as a
> technical control within a public-sector system. Formal product evaluation
> (LINCE / Common Criteria) and CPSTIC listing are planned for when a qualifying
> public buyer requires them.
# Security frameworks
Source: https://docs.zerokeyusb.com/compliance/frameworks
How ZeroKeyUSB is designed to support recognized security frameworks — ISO/IEC 27001 & 27002, NIST SP 800-63B, and Spain's ENS — without claiming certifications it does not hold.
ZeroKeyUSB is an **offline credential device**: it stores logins encrypted,
outside the host computer and outside the cloud, and types them over USB only
after a physical action on the device.
**Positioning, not certification.** ZeroKeyUSB is **not** certified against the
frameworks on this page. A hardware device with no network interface cannot hold
most of these certifications directly. Instead, its architecture is **designed to
support** the relevant controls, so an organization can use it as part of *their*
compliance. We deliberately avoid claiming "ISO 27001 certified device" or
similar.
## At a glance
| Framework | Relevance to ZeroKeyUSB | How we phrase it |
| --------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| [ISO/IEC 27001 & 27002](/compliance/iso-27001-27002) | Controls for authentication information, access control and cryptography | *Designed to support* controls 5.15–5.18, 8.5, 8.24 |
| [NIST SP 800-63B](/compliance/nist-800-63b) | Good practice for authentication secrets and their storage | *Aligned with* its guidance on strong, unique, protected secrets |
| [ENS — Esquema Nacional de Seguridad](/compliance/ens-ccn-stic) | Spanish public-sector security framework (RD 311/2022) | Helps meet access-control and authentication dimensions; CPSTIC is a medium-term goal |
| FIPS 140-3 / Common Criteria / LINCE | Formal cryptographic-module / product evaluations | Future phase, only when a client requires and funds it |
| CE · RoHS · FCC · EMC | Electrical & material compliance to sell hardware | See [Certifications](/certifications) |
## Approved wording
For a website, dossier or tender response:
> ZeroKeyUSB is designed to support public-sector and enterprise security
> requirements by protecting authentication information offline, outside the host
> computer and outside the cloud. Its architecture is aligned with recognized
> security frameworks such as ISO/IEC 27001, ISO/IEC 27002, NIST SP 800-63B, and
> the principles of the Spanish National Security Framework (ENS).
Shorter:
> ZeroKeyUSB is not a cloud password manager. It is an offline credential device
> designed to reduce password exposure on shared, infected, or unmanaged
> computers, while helping organizations strengthen access control and
> authentication-information management.
## Read next
The architecture and security properties in one document.
What it protects against — and, honestly, what it does not.
Control-by-control support statement.
Alignment with authentication-secret guidance.
# ISO/IEC 27001 & 27002 mapping
Source: https://docs.zerokeyusb.com/compliance/iso-27001-27002
How ZeroKeyUSB is designed to support ISO/IEC 27002:2022 controls for authentication information, access control and cryptography — a support statement, not a certification.
ISO/IEC 27001 certifies an organization's **information security management
system**, not a device. ZeroKeyUSB is **not** "ISO 27001 certified." This page
states how the device is **designed to support** specific ISO/IEC 27002:2022
controls, so it can be part of *your* ISMS evidence.
## Primary control: 5.17 Authentication information
ISO/IEC 27002:2022 **control 5.17** covers the secure handling of authentication
information — passwords, PINs and keys.
> ZeroKeyUSB helps organizations protect authentication information by storing
> credentials offline, encrypted, and outside the host computer, reducing
> exposure to browser password leaks, malware, shared sessions, and cloud
> compromise.
## Control-by-control support
| Control (27002:2022) | Theme | How ZeroKeyUSB supports it |
| -------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **5.15** | Access control | Physical possession + Master PIN gate all credential access; nothing is reachable without the device and PIN. |
| **5.16** | Identity management | Each credential is a distinct stored identity; the device itself carries a unique secure-element serial. |
| **5.17** | Authentication information | Credentials are encrypted at rest (AES-128, key in the secure element), never held by the host or a cloud; the PIN is stored only as a salted hash. |
| **5.18** | Access rights | Access is all-or-nothing per device+PIN and is granted per credential only on a deliberate physical action. |
| **8.5** | Secure authentication | Rate-limited PIN (persistent backoff), constant-time comparison, and support for strong, unique, randomly generated passwords and TOTP second factors. |
| **8.24** | Use of cryptography | AES-128 for data at rest, ECDSA P-256 for firmware authenticity, SHA-256 for PIN hashing, and a hardware TRNG for all key material. |
## Evidence an auditor can verify
* **Open-source firmware** — the encryption, PIN and boot logic are inspectable
(see [Software](/firmware/architecture)).
* **Key custody** — the AES key is generated in and never leaves the ATECC608A.
* **No data egress** — no network interface exists; nothing is transmitted.
* **Documented limits** — the [Threat model](/compliance/threat-model) states the
boundaries, which supports an honest risk assessment.
## Suggested statement for an ISMS
> ZeroKeyUSB is designed to support ISO/IEC 27001 and ISO/IEC 27002 controls for
> authentication information (5.17), access control (5.15/5.18), secure
> authentication (8.5) and use of cryptography (8.24). It is used as a technical
> control within the organization's ISMS; it is not itself certified.
# NIST SP 800-63B alignment
Source: https://docs.zerokeyusb.com/compliance/nist-800-63b
How ZeroKeyUSB aligns with NIST SP 800-63B guidance on authentication secrets and their protection — an alignment statement, not an Authenticator Assurance Level certification.
**Alignment, not accreditation.** NIST SP 800-63B specifies requirements for
*authenticators* in a federal digital-identity context and defines Authenticator
Assurance Levels (AAL). ZeroKeyUSB is a credential store, not an accredited
authenticator, and makes **no AAL claim**. This page shows how it helps
subscribers and organizations follow 800-63B's good practices.
## Where ZeroKeyUSB helps
| 800-63B theme | Guidance (paraphrased) | How ZeroKeyUSB supports it |
| ---------------------------------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| **Strong secrets** | Encourage long, high-entropy secrets; do not force composition rules that weaken them | Generates up to 32-character passwords from a hardware TRNG; supports memorable multi-word passphrases |
| **No reuse** | Discourage reuse across services | Each stored credential is independent; unique random passwords are one tap away |
| **Secret storage** | Verifiers must store secrets protected (hashed/encrypted), not in the clear | Credentials are AES-encrypted off-host; the Master PIN is stored only as `SHA-256(PIN ‖ serial)` — never in the clear |
| **Rate limiting** | Limit failed authentication attempts to resist online guessing | Persistent exponential backoff on the PIN, re-applied on every boot so it cannot be reset by power-cycling |
| **Verifier compromise resistance** | Reduce impact of a compromised endpoint | Credentials never reside on the host and the AES key never leaves the secure element |
| **Memorized-secret handling** | Salt and hash memorized secrets; compare safely | The PIN uses the device serial as salt and a constant-time comparison |
## Honest gaps versus a formal 800-63B authenticator
* ZeroKeyUSB is not a phishing-resistant cryptographic authenticator (e.g. a
FIDO2 key); it types passwords, which are a "memorized secret / look-up secret"
style factor.
* It does not perform online proof-of-possession with a relying party; it
augments password-based auth rather than replacing it.
* The PIN hash is single-pass `SHA-256` (not a heavy KDF) and is readable over
I²C, so its offline-resistance depends on the physical encapsulation and PIN
length — see the [Threat model](/compliance/threat-model).
## Suggested statement
> ZeroKeyUSB's design aligns with NIST SP 800-63B guidance on strong, unique
> authentication secrets, protected secret storage and rate-limited verification.
> It is a credential-protection device, not an accredited authenticator, and
> makes no Authenticator Assurance Level claim.
# Security whitepaper
Source: https://docs.zerokeyusb.com/compliance/security-whitepaper
ZeroKeyUSB's security architecture in one document: offline design, signed firmware, hardware-backed encryption, and PIN protection — with an honest statement of scope.
This whitepaper summarizes how ZeroKeyUSB protects credentials, for security
teams, auditors and procurement. It describes what the device does and,
explicitly, the boundaries of what it protects. For the deeper technical detail,
each section links to the [Software](/firmware/architecture) documentation, which
is fully open-source and verifiable.
## What ZeroKeyUSB is
A hand-held, **offline** credential device based on an ATSAMD21 microcontroller
and an ATECC608A secure element, encapsulated in epoxy resin. It stores logins,
TOTP secrets and short notes, and types them to a host over **USB HID** on a
physical press. It has **no Wi-Fi, Bluetooth, NFC, battery or cloud account**.
## Security architecture
| Layer | Mechanism |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Secure boot** | The bootloader verifies an **ECDSA P-256** signature over the application firmware (via the ATECC608A) before running it. The bootloader region is hardware write-protected (`BOOTPROT`). |
| **Credential encryption** | Credentials are stored **AES-128-CBC** encrypted in an external EEPROM. The AES key is generated by the ATECC608A's hardware TRNG and **stored inside the chip** (`IsSecret=1`) — it never crosses the I²C bus or reaches host software. |
| **PIN** | A 1–16 digit Master PIN is verified as `SHA-256(PIN ‖ chip-serial)` with a constant-time compare, and rate-limited by a **persistent exponential backoff** re-applied on every boot (see [PIN verification](/firmware/security/pin-verification)). |
| **Randomness** | All key material, IVs and generated passwords come from the ATECC608A **hardware TRNG**. |
| **Physical** | The PCB is **epoxy-encapsulated**; there is no debug path to the application after provisioning, and the config/data zones of the secure element are permanently locked. |
| **Airgap** | Credentials are entered/exported only over USB, on user action. No network stack exists. |
## Security properties
* **No cloud, no account, no telemetry.** Nothing is transmitted to any server.
* **Secrets stay off the host.** The host receives keystrokes, never the vault.
* **Tamper-resistant key custody.** The AES key and the device are bound; ciphertext from one unit cannot be decrypted by another.
* **Verifiable firmware.** Only ECDSA-signed images run; the source is open for audit.
## Scope and limitations
A credible security statement names its boundaries. ZeroKeyUSB does **not** claim
to protect against:
* **A trusted-host compromise during use.** When you type a credential, it lands
on the host; if that machine is already compromised, it can capture what is
typed. The device reduces *storage* exposure, not *in-use* exposure.
* **Plaintext backup on an untrusted host.** The USB backup exports credentials
in clear text to the connected computer; do it only on a trusted, offline host.
* **A determined physical/lab attacker.** The PIN hash is readable over the I²C
bus, so an attacker who defeats the epoxy encapsulation and reaches the bus can
attempt an offline PIN crack. The epoxy and a long PIN are the mitigations;
there is no destructive self-wipe.
See the [Threat model](/compliance/threat-model) for the full analysis, and
[Security frameworks](/compliance/frameworks) for how this maps to ISO, NIST and
ENS controls.
# Threat model
Source: https://docs.zerokeyusb.com/compliance/threat-model
What ZeroKeyUSB protects against and what it does not — by asset, adversary and scenario. Written to be defensible in an audit, not to oversell.
A security product is only as trustworthy as its honesty about limits. This
threat model states what ZeroKeyUSB defends, against whom, and where the
boundaries are. It is grounded in the actual firmware, which is open-source and
verifiable.
## Assets
| Asset | Where it lives |
| ------------------------------------ | -------------------------------------------------- |
| Credentials (site / user / password) | AES-encrypted in external EEPROM |
| TOTP secrets and notes | Same, per credential |
| AES master key | Inside the ATECC608A (never leaves the chip) |
| Master PIN | Never stored; only `SHA-256(PIN ‖ serial)` is kept |
| Firmware integrity | Enforced by ECDSA-signed secure boot |
| Bitcoin seed (optional) | AES-encrypted; shown only on screen |
## Adversaries and outcomes
| Adversary | Capability | Result |
| -------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Remote attacker** | Internet, malware pull, phishing infrastructure | **Blocked.** No network interface; nothing to reach. |
| **Malicious/untrusted host** | Controls the computer the device is plugged into | **Limited.** Can capture credentials *as they are typed* and read a *plaintext backup* if the user runs one; cannot read the vault at rest or extract the AES key. |
| **Opportunistic thief (lost/stolen device)** | Physical possession, normal use | **Blocked in practice.** Needs the PIN; the persistent backoff makes online guessing impractical without wiping data. |
| **Physical/lab attacker** | Decapsulates the resin, probes the I²C bus | **Partially mitigated.** Can read the PIN hash and attempt an **offline** crack; strength then depends on epoxy quality and PIN length. |
| **Supply-chain / firmware tamper** | Tries to run modified firmware | **Blocked.** The bootloader runs only ECDSA-signed images; the bootloader is `BOOTPROT`-locked. |
## What it protects against
* **Browser and cloud password leaks** — credentials never live in a browser or
a cloud vault.
* **Malware that scrapes stored passwords** — the vault is off-host and encrypted
with a key the host never sees.
* **Credential theft at rest** — dumping the EEPROM yields only ciphertext.
* **Cloning** — the AES key is device-bound inside the secure element.
* **Unsigned/rogue firmware** — rejected at boot.
* **Online PIN brute force** — exponential backoff, re-applied at every boot so it
cannot be skipped by power-cycling; the vault is never destroyed by wrong PINs.
## What it does not protect against
* **A compromised host during use.** Keystrokes typed to an infected computer can
be captured there. ZeroKeyUSB reduces storage exposure, not the risk of typing
into a machine that is already owned by an attacker.
* **Plaintext USB backup on an untrusted machine.** The export sends credentials
in clear text to the host. Perform backups only on a trusted, offline computer.
* **Offline PIN cracking after physical bus access.** The PIN hash is readable
over I²C (a known trade-off of the secure-element SKU). An attacker who removes
the epoxy and reaches the bus can attempt an offline `SHA-256(PIN ‖ serial)`
search. Mitigations: the epoxy encapsulation and using a long PIN. There is no
destructive self-wipe.
* **Coercion / shoulder-surfing of the PIN.** Standard operational-security
concerns apply.
## Design consequences for buyers
* Use ZeroKeyUSB on **managed or personal** machines you trust for the moment of
typing; it is strongest exactly where stored-password managers are weakest
(shared, infected, unmanaged hosts).
* Treat the **backup file** as sensitive and generate it offline.
* Choose a **long PIN** for high-value deployments; it is the last line against a
lab-grade physical attack.
This analysis maps directly onto the control statements in
[ISO 27001/27002](/compliance/iso-27001-27002) and [NIST SP 800-63B](/compliance/nist-800-63b).
# Certificaciones y cumplimiento
Source: https://docs.zerokeyusb.com/es/certifications
Estándares de fabricación y pruebas de laboratorio que validan ZeroKeyUSB para uso cotidiano y profesional.
## Construido para mercados globales
ZeroKeyUSB se produce en instalaciones que siguen los estándares **ISO 9001** (gestión de calidad) e **ISO 27001** (seguridad de la información).
Cada lote de producción es trazable — desde el sourcing de componentes hasta el encapsulado final — garantizando seguridad y durabilidad consistentes.
***
## Cumplimiento eléctrico
| Estándar | Alcance | Estado |
| ----------------------------- | ------------------------------------------------------------- | --------------------------------------- |
| **CE** (EN 55032 / EN 55035) | Compatibilidad electromagnética para equipos IT. | ✅ Aprobado, informe `EMC-ZKU-2024-CE`. |
| **FCC Part 15, Clase B** | Emisiones radiadas y conducidas para dispositivos de consumo. | ✅ Aprobado, informe `EMC-ZKU-2024-FCC`. |
| **Directiva RoHS 2011/65/EU** | Restricción de sustancias peligrosas. | ✅ Todos los componentes cumplen. |
| **USB-IF Electrical** | Tolerancia eléctrica USB 2.0 low/full-speed. | ✅ Verificado por laboratorio externo. |
Estas certificaciones garantizan que ZeroKeyUSB se puede usar de forma segura en hogares, oficinas y entornos regulados.
***
## Robustez ambiental
ZeroKeyUSB pasa pruebas de estrés para garantizar que el diseño encapsulado en resina sobrevive al uso diario:
* **Ciclado térmico**: −10 °C a 60 °C, 40 ciclos, sin fallos.
* **Exposición a humedad**: 95% RH a 40 °C durante 72 horas, sin entrada de condensación.
* **Niebla salina**: 5% NaCl, 24 horas, sin corrosión en los contactos expuestos.
* **Resistencia al agua**: Cumple la protección contra salpicaduras **IP54** una vez conectado al cable USB-C.
***
## Prácticas de protección de datos
Aunque el hardware no puede obtener "certificaciones de seguridad" formales sin interfaces de red, adoptamos las mejores prácticas del sector:
* **Aprovisionamiento seguro**: La firma del PIN, generación de IV y números de serie se programan durante el testeo final en una red aislada.
* **Sellos a prueba de manipulación**: Cada dispositivo se entrega con un sello holográfico único ligado al número de serie.
* **Pen-testing**: Auditorías anuales externas revisan firmware, hardware y riesgos de cadena de suministro.
Los resúmenes de auditoría están disponibles para clientes enterprise bajo NDA.
***
## Archivo de documentación
Todos los informes de cumplimiento, declaraciones de conformidad y certificados de prueba se guardan en el portal de clientes.
Si necesitas acceso:
1. Envía un email a **[certs@zerokeyusb.com](mailto:certs@zerokeyusb.com)** con tu número de pedido o ID de distribuidor.
2. Recibe un enlace de descarga seguro válido durante 48 horas.
3. Verifica las firmas del PDF contra la autoridad certificadora de Depbit Lab.
***
ZeroKeyUSB está diseñado para ser un appliance de seguridad a largo plazo. Las renovaciones de certificación están programadas anualmente para garantizar cumplimiento continuo.
# Posicionamiento ENS y CCN-STIC
Source: https://docs.zerokeyusb.com/es/compliance/ens-ccn-stic
Cómo encaja ZeroKeyUSB en el Esquema Nacional de Seguridad (RD 311/2022) para compradores del sector público español, y la vía CPSTIC / CCN-STIC como objetivo a medio plazo.
Para vender a la administración pública española —ministerios, universidades
públicas, ayuntamientos, hospitales públicos— el marco clave es el **Esquema
Nacional de Seguridad (ENS)**, regulado por el **Real Decreto 311/2022**. El ENS
aplica a los sistemas de información del sector público y a los proveedores que les
suministran tecnología o servicios.
ZeroKeyUSB no está "certificado ENS" en sí mismo — la conformidad con el ENS
aplica a sistemas y organizaciones. El dispositivo se posiciona como un **control
técnico que ayuda a un sistema en ámbito ENS a cumplir sus requisitos**, en
particular en identidad, control de acceso y autenticación.
## Dimensiones del ENS que ZeroKeyUSB apoya
El ENS organiza las salvaguardas en medidas operativas y de protección. Un
dispositivo de credenciales contribuye sobre todo al **control de acceso** y a la
**protección de la información**:
| Área del ENS | Contribución |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Identificación y autenticación | Contraseñas fuertes y únicas y segundos factores TOTP, controlados por un PIN maestro en un dispositivo físico |
| Control de acceso | Las credenciales son inalcanzables sin el dispositivo y el PIN; el acceso se concede solo ante una acción deliberada |
| Protección de la información | Credenciales cifradas en reposo con una clave alojada en un elemento seguro; sin almacenamiento en la nube |
| Protección frente a malware | La bóveda está fuera del host y cifrada, reduciendo el valor de un endpoint infectado |
| Operación sin software local | Sin agente ni driver que instalar, reduciendo la superficie de ataque del sistema gestionado |
| Trazabilidad del diseño | Firmware open-source, imágenes firmadas y un [modelo de amenazas](/es/compliance/threat-model) documentado apoyan la documentación de riesgos |
## La vía CPSTIC / CCN-STIC (medio plazo)
Para un producto de seguridad usado por la administración española, el catálogo de
referencia es el **CPSTIC** del **CCN** (Centro Criptológico Nacional) — la lista de
productos de seguridad TIC cualificados/aprobados para uso en el marco del ENS. La
inclusión en CPSTIC suele apoyarse en una evaluación reconocida por el CCN, como
**LINCE** o **Common Criteria**, siguiendo las guías **CCN-STIC** pertinentes.
Es una fase posterior y deliberada. La base ya existente ayuda:
* **Firmware firmado y bootloader protegido por hardware** (arranque seguro).
* **Una categoría de producto definida** — "dispositivo de almacenamiento seguro
offline de credenciales".
* **Documentación de arquitectura y amenazas** sobre la que construiría una
evaluación.
* **Un proceso de aprovisionamiento seguro** y un elemento seguro bloqueado
permanentemente.
Una secuencia realista, cuando un gran comprador público lo requiera:
1. Definir el producto como objeto de evaluación (TOE) evaluable.
2. Preparar documentación de arquitectura, modelo de amenazas y fabricación segura.
3. Contratar un laboratorio acreditado para **LINCE** (o Common Criteria) según
exija el CPSTIC.
4. Solicitar el listado en **CPSTIC**.
## Declaración sugerida
> ZeroKeyUSB está diseñado para apoyar los requisitos de control de acceso y
> autenticación del Esquema Nacional de Seguridad (ENS, RD 311/2022) como control
> técnico dentro de un sistema del sector público. La evaluación formal de producto
> (LINCE / Common Criteria) y el listado en CPSTIC están previstos para cuando un
> comprador público cualificado los requiera.
# Marcos de seguridad
Source: https://docs.zerokeyusb.com/es/compliance/frameworks
Cómo ZeroKeyUSB está diseñado para apoyar marcos de seguridad reconocidos —ISO/IEC 27001 y 27002, NIST SP 800-63B y el ENS español— sin afirmar certificaciones que no tiene.
ZeroKeyUSB es un **dispositivo de credenciales offline**: guarda los inicios de
sesión cifrados, fuera del ordenador y fuera de la nube, y los teclea por USB solo
tras una acción física en el dispositivo.
**Posicionamiento, no certificación.** ZeroKeyUSB **no** está certificado contra
los marcos de esta página. Un dispositivo hardware sin interfaz de red no puede
tener directamente la mayoría de estas certificaciones. En su lugar, su
arquitectura está **diseñada para apoyar** los controles relevantes, de modo que
una organización pueda usarlo como parte de *su* cumplimiento. Evitamos
deliberadamente decir "dispositivo certificado ISO 27001" o similar.
## De un vistazo
| Marco | Relevancia para ZeroKeyUSB | Cómo lo expresamos |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| [ISO/IEC 27001 y 27002](/es/compliance/iso-27001-27002) | Controles de información de autenticación, control de acceso y criptografía | *Diseñado para apoyar* los controles 5.15–5.18, 8.5, 8.24 |
| [NIST SP 800-63B](/es/compliance/nist-800-63b) | Buenas prácticas para secretos de autenticación y su almacenamiento | *Alineado con* su guía sobre secretos fuertes, únicos y protegidos |
| [ENS — Esquema Nacional de Seguridad](/es/compliance/ens-ccn-stic) | Marco de seguridad del sector público español (RD 311/2022) | Ayuda a cumplir dimensiones de control de acceso y autenticación; CPSTIC es objetivo a medio plazo |
| FIPS 140-3 / Common Criteria / LINCE | Evaluaciones formales de módulo criptográfico / producto | Fase futura, solo cuando un cliente lo exija y lo financie |
| CE · RoHS · FCC · EMC | Cumplimiento eléctrico y de materiales para vender hardware | Ver [Certificaciones](/es/certifications) |
## Redacción aprobada
Para web, dossier o respuesta a un pliego:
> ZeroKeyUSB está diseñado para apoyar los requisitos de seguridad del sector
> público y empresarial protegiendo la información de autenticación offline, fuera
> del ordenador y fuera de la nube. Su arquitectura está alineada con marcos de
> seguridad reconocidos como ISO/IEC 27001, ISO/IEC 27002, NIST SP 800-63B y los
> principios del Esquema Nacional de Seguridad (ENS).
Más directa:
> ZeroKeyUSB no es un gestor de contraseñas en la nube. Es un dispositivo de
> credenciales offline diseñado para reducir la exposición de contraseñas en
> ordenadores compartidos, infectados o no gestionados, ayudando a las
> organizaciones a reforzar el control de acceso y la gestión de la información de
> autenticación.
## Seguir leyendo
La arquitectura y las propiedades de seguridad en un documento.
Contra qué protege — y, con honestidad, contra qué no.
Declaración de apoyo control a control.
Alineamiento con la guía de secretos de autenticación.
# Mapeo ISO/IEC 27001 y 27002
Source: https://docs.zerokeyusb.com/es/compliance/iso-27001-27002
Cómo ZeroKeyUSB está diseñado para apoyar los controles de ISO/IEC 27002:2022 de información de autenticación, control de acceso y criptografía — una declaración de apoyo, no una certificación.
ISO/IEC 27001 certifica el **sistema de gestión de seguridad de la información**
de una organización, no un dispositivo. ZeroKeyUSB **no** está "certificado ISO
27001". Esta página indica cómo el dispositivo está **diseñado para apoyar**
controles concretos de ISO/IEC 27002:2022, para que pueda formar parte de la
evidencia de *tu* SGSI.
## Control principal: 5.17 Información de autenticación
El **control 5.17** de ISO/IEC 27002:2022 cubre el manejo seguro de la información
de autenticación — contraseñas, PINs y claves.
> ZeroKeyUSB ayuda a las organizaciones a proteger la información de autenticación
> guardando las credenciales offline, cifradas y fuera del ordenador, reduciendo la
> exposición a fugas de contraseñas del navegador, malware, sesiones compartidas y
> compromiso de la nube.
## Apoyo control a control
| Control (27002:2022) | Tema | Cómo lo apoya ZeroKeyUSB |
| -------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **5.15** | Control de acceso | La posesión física + el PIN maestro controlan todo acceso a credenciales; nada es alcanzable sin el dispositivo y el PIN. |
| **5.16** | Gestión de identidad | Cada credencial es una identidad guardada distinta; el propio dispositivo lleva un serial único del elemento seguro. |
| **5.17** | Información de autenticación | Las credenciales están cifradas en reposo (AES-128, clave en el elemento seguro), nunca en poder del host ni de una nube; el PIN se guarda solo como hash con sal. |
| **5.18** | Derechos de acceso | El acceso es todo-o-nada por dispositivo+PIN y se concede por credencial solo ante una acción física deliberada. |
| **8.5** | Autenticación segura | PIN con límite de ritmo (backoff persistente), comparación a tiempo constante y soporte para contraseñas fuertes, únicas y aleatorias y segundos factores TOTP. |
| **8.24** | Uso de criptografía | AES-128 para datos en reposo, ECDSA P-256 para autenticidad del firmware, SHA-256 para el hash del PIN y un TRNG hardware para todo el material de claves. |
## Evidencia que un auditor puede verificar
* **Firmware open-source** — la lógica de cifrado, PIN y arranque es inspeccionable
(ver [Software](/es/firmware/architecture)).
* **Custodia de clave** — la clave AES se genera en y nunca sale del ATECC608A.
* **Sin salida de datos** — no existe interfaz de red; no se transmite nada.
* **Límites documentados** — el [Modelo de amenazas](/es/compliance/threat-model)
indica las fronteras, lo que apoya una evaluación de riesgos honesta.
## Declaración sugerida para un SGSI
> ZeroKeyUSB está diseñado para apoyar controles de ISO/IEC 27001 e ISO/IEC 27002
> de información de autenticación (5.17), control de acceso (5.15/5.18),
> autenticación segura (8.5) y uso de criptografía (8.24). Se usa como control
> técnico dentro del SGSI de la organización; no está certificado en sí mismo.
# Alineamiento con NIST SP 800-63B
Source: https://docs.zerokeyusb.com/es/compliance/nist-800-63b
Cómo ZeroKeyUSB se alinea con la guía de NIST SP 800-63B sobre secretos de autenticación y su protección — una declaración de alineamiento, no una acreditación de nivel de garantía (AAL).
**Alineamiento, no acreditación.** NIST SP 800-63B especifica requisitos para
*autenticadores* en un contexto federal de identidad digital y define niveles de
garantía de autenticación (AAL). ZeroKeyUSB es un almacén de credenciales, no un
autenticador acreditado, y **no hace ninguna afirmación de AAL**. Esta página
muestra cómo ayuda a suscriptores y organizaciones a seguir las buenas prácticas
de 800-63B.
## Dónde ayuda ZeroKeyUSB
| Tema de 800-63B | Guía (parafraseada) | Cómo lo apoya ZeroKeyUSB |
| --------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| **Secretos fuertes** | Fomentar secretos largos y de alta entropía; no imponer reglas de composición que los debiliten | Genera contraseñas de hasta 32 caracteres desde un TRNG hardware; soporta passphrases memorables de varias palabras |
| **Sin reutilización** | Desalentar la reutilización entre servicios | Cada credencial guardada es independiente; una contraseña aleatoria única está a un toque |
| **Almacenamiento del secreto** | Los verificadores deben guardar los secretos protegidos (hash/cifrado), no en claro | Las credenciales están cifradas con AES fuera del host; el PIN maestro se guarda solo como `SHA-256(PIN ‖ serial)` — nunca en claro |
| **Límite de intentos** | Limitar los intentos de autenticación fallidos para resistir el adivinado online | Backoff exponencial persistente en el PIN, reaplicado en cada arranque para que no se reinicie apagando y encendiendo |
| **Resistencia al compromiso del verificador** | Reducir el impacto de un endpoint comprometido | Las credenciales nunca residen en el host y la clave AES nunca sale del elemento seguro |
| **Manejo del secreto memorizado** | Salar y hashear los secretos memorizados; comparar de forma segura | El PIN usa el serial del dispositivo como sal y una comparación a tiempo constante |
## Carencias honestas frente a un autenticador formal 800-63B
* ZeroKeyUSB no es un autenticador criptográfico resistente al phishing (p. ej. una
llave FIDO2); teclea contraseñas, que son un factor tipo "secreto memorizado /
secreto de consulta".
* No realiza prueba de posesión online con una parte confiante; complementa la
autenticación basada en contraseña en lugar de sustituirla.
* El hash del PIN es `SHA-256` de una sola pasada (no un KDF pesado) y es legible
por I²C, así que su resistencia offline depende de la encapsulación física y de
la longitud del PIN — ver el [Modelo de amenazas](/es/compliance/threat-model).
## Declaración sugerida
> El diseño de ZeroKeyUSB se alinea con la guía de NIST SP 800-63B sobre secretos
> de autenticación fuertes y únicos, almacenamiento protegido del secreto y
> verificación con límite de intentos. Es un dispositivo de protección de
> credenciales, no un autenticador acreditado, y no hace ninguna afirmación de
> nivel de garantía de autenticación.
# Whitepaper de seguridad
Source: https://docs.zerokeyusb.com/es/compliance/security-whitepaper
La arquitectura de seguridad de ZeroKeyUSB en un documento: diseño offline, firmware firmado, cifrado respaldado por hardware y protección por PIN — con una declaración honesta de alcance.
Este whitepaper resume cómo ZeroKeyUSB protege las credenciales, para equipos de
seguridad, auditores y compras. Describe qué hace el dispositivo y, de forma
explícita, los límites de lo que protege. Para el detalle técnico profundo, cada
sección enlaza con la documentación de [Software](/es/firmware/architecture), que
es totalmente open-source y verificable.
## Qué es ZeroKeyUSB
Un dispositivo de credenciales **offline** de mano, basado en un microcontrolador
ATSAMD21 y un elemento seguro ATECC608A, encapsulado en resina epoxy. Guarda
inicios de sesión, secretos TOTP y notas cortas, y los teclea a un host por **USB
HID** tras una pulsación física. **No tiene Wi-Fi, Bluetooth, NFC, batería ni
cuenta en la nube.**
## Arquitectura de seguridad
| Capa | Mecanismo |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Arranque seguro** | El bootloader verifica una firma **ECDSA P-256** sobre el firmware de aplicación (vía el ATECC608A) antes de ejecutarlo. La región del bootloader está protegida contra escritura por hardware (`BOOTPROT`). |
| **Cifrado de credenciales** | Las credenciales se guardan cifradas con **AES-128-CBC** en una EEPROM externa. La clave AES la genera el TRNG hardware del ATECC608A y se **guarda dentro del chip** (`IsSecret=1`) — nunca cruza el bus I²C ni llega al software del host. |
| **PIN** | Un PIN maestro de 1–16 dígitos se verifica como `SHA-256(PIN ‖ serial-chip)` con comparación a tiempo constante, y se limita el ritmo con un **backoff exponencial persistente** reaplicado en cada arranque (ver [Verificación del PIN](/es/firmware/security/pin-verification)). |
| **Aleatoriedad** | Todo el material de claves, IVs y contraseñas generadas viene del **TRNG hardware** del ATECC608A. |
| **Físico** | El PCB está **encapsulado en epoxy**; no hay ruta de depuración a la aplicación tras el aprovisionamiento, y las zonas Config/Data del elemento seguro quedan bloqueadas permanentemente. |
| **Airgap** | Las credenciales se introducen/exportan solo por USB, por acción del usuario. No existe pila de red. |
## Propiedades de seguridad
* **Sin nube, sin cuenta, sin telemetría.** No se transmite nada a ningún servidor.
* **Los secretos no tocan el host.** El host recibe pulsaciones de teclado, nunca la bóveda.
* **Custodia de clave resistente a manipulación.** La clave AES y el dispositivo están ligados; el ciphertext de una unidad no lo descifra otra.
* **Firmware verificable.** Solo corren imágenes firmadas con ECDSA; el código está abierto para auditoría.
## Alcance y limitaciones
Una declaración de seguridad creíble nombra sus límites. ZeroKeyUSB **no** afirma
proteger contra:
* **Un compromiso del host de confianza durante el uso.** Cuando tecleas una
credencial, aterriza en el host; si esa máquina ya está comprometida, puede
capturar lo tecleado. El dispositivo reduce la exposición del *almacenamiento*,
no la del *uso*.
* **Backup en claro en un host no confiable.** El backup por USB exporta las
credenciales en texto claro al ordenador conectado; hazlo solo en un host de
confianza y offline.
* **Un atacante físico/de laboratorio determinado.** El hash del PIN es legible
por el bus I²C, así que un atacante que venza la encapsulación de epoxy y alcance
el bus puede intentar un crackeo offline del PIN. La resina y un PIN largo son
las mitigaciones; no hay autoborrado destructivo.
Ver el [Modelo de amenazas](/es/compliance/threat-model) para el análisis completo,
y [Marcos de seguridad](/es/compliance/frameworks) para cómo mapea a controles
ISO, NIST y ENS.
# Modelo de amenazas
Source: https://docs.zerokeyusb.com/es/compliance/threat-model
Contra qué protege ZeroKeyUSB y contra qué no — por activo, adversario y escenario. Escrito para ser defendible en una auditoría, no para vender de más.
Un producto de seguridad es tan fiable como honesto sea sobre sus límites. Este
modelo de amenazas indica qué defiende ZeroKeyUSB, contra quién, y dónde están las
fronteras. Está anclado en el firmware real, que es open-source y verificable.
## Activos
| Activo | Dónde vive |
| ------------------------------------------- | --------------------------------------------- |
| Credenciales (sitio / usuario / contraseña) | Cifradas con AES en EEPROM externa |
| Secretos TOTP y notas | Igual, por credencial |
| Clave maestra AES | Dentro del ATECC608A (nunca sale del chip) |
| PIN maestro | Nunca se guarda; solo `SHA-256(PIN ‖ serial)` |
| Integridad del firmware | Garantizada por arranque seguro firmado ECDSA |
| Semilla Bitcoin (opcional) | Cifrada con AES; se muestra solo en pantalla |
## Adversarios y resultados
| Adversario | Capacidad | Resultado |
| --------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Atacante remoto** | Internet, malware, infraestructura de phishing | **Bloqueado.** Sin interfaz de red; nada que alcanzar. |
| **Host malicioso/no confiable** | Controla el ordenador donde se conecta | **Limitado.** Puede capturar credenciales *mientras se teclean* y leer un *backup en claro* si el usuario lo ejecuta; no puede leer la bóveda en reposo ni extraer la clave AES. |
| **Ladrón oportunista (dispositivo perdido/robado)** | Posesión física, uso normal | **Bloqueado en la práctica.** Necesita el PIN; el backoff persistente hace inviable el adivinado online sin borrar datos. |
| **Atacante físico/de laboratorio** | Decapa la resina, sondea el bus I²C | **Parcialmente mitigado.** Puede leer el hash del PIN e intentar un crackeo **offline**; la fuerza depende entonces de la calidad del epoxy y de la longitud del PIN. |
| **Cadena de suministro / manipulación de firmware** | Intenta ejecutar firmware modificado | **Bloqueado.** El bootloader solo corre imágenes firmadas ECDSA; el bootloader está bloqueado con `BOOTPROT`. |
## Contra qué protege
* **Fugas de contraseñas de navegador y nube** — las credenciales nunca viven en
un navegador ni en una bóveda en la nube.
* **Malware que roba contraseñas guardadas** — la bóveda está fuera del host y
cifrada con una clave que el host nunca ve.
* **Robo de credenciales en reposo** — volcar la EEPROM solo da ciphertext.
* **Clonado** — la clave AES está ligada al dispositivo dentro del elemento seguro.
* **Firmware no firmado/pirata** — rechazado en el arranque.
* **Fuerza bruta online del PIN** — backoff exponencial, reaplicado en cada
arranque para que no se salte apagando y encendiendo; la bóveda nunca se destruye
por PINs incorrectos.
## Contra qué NO protege
* **Un host comprometido durante el uso.** Las pulsaciones tecleadas a un ordenador
infectado se pueden capturar ahí. ZeroKeyUSB reduce la exposición del
almacenamiento, no el riesgo de teclear en una máquina ya controlada por un
atacante.
* **Backup USB en claro en una máquina no confiable.** El export envía las
credenciales en texto claro al host. Haz los backups solo en un ordenador de
confianza y offline.
* **Crackeo offline del PIN tras acceso físico al bus.** El hash del PIN es legible
por I²C (un trade-off conocido del SKU del elemento seguro). Un atacante que
quite el epoxy y alcance el bus puede intentar una búsqueda offline de
`SHA-256(PIN ‖ serial)`. Mitigaciones: la encapsulación de epoxy y usar un PIN
largo. No hay autoborrado destructivo.
* **Coacción / mirar por encima del hombro al PIN.** Aplican las preocupaciones de
seguridad operacional habituales.
## Consecuencias de diseño para los compradores
* Usa ZeroKeyUSB en máquinas **gestionadas o personales** en las que confíes en el
momento de teclear; es más fuerte justo donde los gestores que guardan
contraseñas son más débiles (hosts compartidos, infectados, no gestionados).
* Trata el **fichero de backup** como sensible y genéralo offline.
* Elige un **PIN largo** para despliegues de alto valor; es la última línea contra
un ataque físico de nivel laboratorio.
Este análisis mapea directamente sobre las declaraciones de control de
[ISO 27001/27002](/es/compliance/iso-27001-27002) y [NIST SP 800-63B](/es/compliance/nist-800-63b).
# Arquitectura del firmware
Source: https://docs.zerokeyusb.com/es/firmware/architecture
Entiende cómo está organizado el firmware de ZeroKeyUSB, desde los drivers hardware hasta el gestor seguro de credenciales.
## Modular por diseño
El firmware de ZeroKeyUSB está escrito en C++ para el microcontrolador **Microchip SAMD21E18A** (ARM Cortex-M0+, 48 MHz, 256 KB flash, 32 KB SRAM).\
Sigue una arquitectura en capas que mantiene los drivers de hardware, las primitivas de seguridad y la interfaz de usuario claramente separadas.
```mermaid theme={null}
graph TB
subgraph Application["Capa de aplicación"]
MENU["zerokey-menu.cpp Sistema de menús + asistente"]
IO["zerokey-io.cpp Enrutado de eventos táctiles"]
SETUP["zerokey-setup.cpp Arranque + flag de config"]
end
subgraph Security["Seguridad y servicios"]
SEC["zerokey-security.cpp Encadenamiento CBC + IV (bloques vía AES del ATECC)"]
TOTP["zerokey-totp.cpp Generación de códigos TOTP"]
ATECC["zerokey-atecc.cpp Driver ATECC608A + AES + aprovisionamiento"]
end
subgraph Drivers["Drivers de hardware"]
DISP["zerokey-display.cpp OLED SSD1306 (I²C)"]
EEPROM["zerokey-eeprom.cpp EEPROM M24C64 (I²C)"]
USB["Keyboard.h + SerialUSB Compuesto HID + CDC"]
UTILS["zerokey-utils.cpp Orientación pantalla, tecleo"]
end
subgraph HAL["HAL del SAMD21 / CMSIS"]
WIRE["Wire (I²C)"]
USBHAL["Periférico USB FS"]
end
MENU --> SEC
IO --> MENU
IO --> SEC
SETUP --> SEC
SETUP --> DISP
SEC --> ATECC
SEC --> EEPROM
TOTP --> EEPROM
TOTP --> ATECC
DISP --> WIRE
EEPROM --> WIRE
ATECC --> WIRE
USB --> USBHAL
style Application fill:#dbeafe,stroke:#2563eb
style Security fill:#fef3c7,stroke:#d97706
style Drivers fill:#dcfce7,stroke:#16a34a
style HAL fill:#f3e8ff,stroke:#7c3aed
```
Cada módulo puede evolucionar de forma independiente manteniendo las rutinas críticas de seguridad auditables y fáciles de revisar.
***
## Mapa de ficheros fuente
| Fichero | Líneas | Función |
| ------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `zerokey-security.cpp/.h` | \~750 | Encadenamiento AES-CBC alrededor del comando AES de bloque único del chip, verificación de PIN, borrado, backup/restore |
| `zerokey-atecc.cpp/.h` | \~640 | Driver I²C del ATECC608A: TRNG, Counter, ReadSerial, CheckMac, SHA-256, AES hardware, Lock y la rutina de aprovisionamiento AES de un disparo |
| `zerokey-io.cpp/.h` | \~1561 | Dispatch de eventos táctiles, entrada de fecha/hora TOTP, gestor de comandos serie |
| `zerokey-menu.cpp/.h` | \~1051 | Árbol de menús, asistente de setup (10 páginas), páginas de confirmación/info/actividad |
| `zerokey-display.cpp/.h` | \~750 | Renderizado SSD1306: pantalla principal, pantalla PIN, editor, progreso, scroll |
| `zerokey-eeprom.cpp/.h` | \~257 | Lectura/escritura de página, metadatos TOTP, layout de teclado, persistencia de epoch |
| `zerokey-totp.cpp/.h` | \~500 | HMAC-SHA1/SHA256/SHA512, decodificación Base32, generación de códigos TOTP |
| `zerokey-setup.cpp/.h` | \~159 | Secuencia de arranque, flag de config (`0x42`), init de TS06, probe de hardware |
| `zerokey-utils.cpp/.h` | \~500 | Motor de tecleo, orientación de pantalla, pantalla de error, número de serie |
| `zerokey-globals.h` | \~317 | Constantes, iconos (PROGMEM), externs de variables globales |
| `zerokey-memorymap.h` | \~38 | Cálculo de direcciones EEPROM, constantes del layout de credenciales |
***
## Secuencia de arranque
```mermaid theme={null}
sequenceDiagram
participant PWR as Alimentación USB
participant BL as Bootloader
participant FW as Firmware
participant HW as Hardware
PWR->>BL: Encendido
BL->>BL: Comprobación CRC32 + BLAKE2s MAC
alt Firmware válido
BL->>FW: Salta a la aplicación
else Inválido
BL->>BL: 15 s de penalización
BL->>BL: Entra en modo DFU USB-CDC
end
FW->>HW: Wire.begin() — bus I²C a 100 kHz
FW->>HW: Probe del controlador táctil TS06 (5 reintentos)
FW->>HW: Configura sensibilidad TS06 (reg 0x00–0x02 = 0x3F)
FW->>HW: Init OLED SSD1306 en 0x3C
FW->>HW: Lee layout de teclado desde EEPROM 0x003E
FW->>HW: Lee último epoch TOTP desde EEPROM 0x0040
FW->>HW: Ping ATECC608A + lee estado de lock
FW->>HW: Si es primer arranque — aprovisiona AES + lockea zonas
FW->>HW: Self-test AES (encrypt + decrypt round-trip)
FW->>FW: Lee flag de config desde EEPROM 0x0000
alt Flag ≠ 0x42
FW-->>FW: Lanza el asistente de setup
else Flag = 0x42
FW->>FW: Aplica delay de backoff pendiente
FW-->>FW: Muestra pantalla de PIN
end
```
El flag de configuración en `0x0000` determina si el dispositivo muestra el asistente de setup (`flag ≠ 0x42`) o la pantalla de desbloqueo con PIN (`flag = 0x42`). El asistente escribe `0x42` tras la creación exitosa del PIN.
***
## Bucle principal
El firmware corre un **bucle principal cooperativo** — sin RTOS, sin interrupciones para lógica de aplicación, sin asignación dinámica de memoria:
```mermaid theme={null}
graph LR
A["Poll TS06 estado táctil"] --> B["Debounce umbral 80 ms"]
B --> C["Dispatch del evento a la pantalla actual"]
C --> D["Actualizar pantalla refresco de frame completo"]
D --> E["Comprobar SerialUSB por comandos del host"]
E --> F["Actualizar TOTP epoch basado en millis"]
F --> A
style A fill:#fef3c7,stroke:#d97706,color:#000
style D fill:#dbeafe,stroke:#2563eb,color:#000
```
Cada iteración es determinista. Las operaciones sensibles al tiempo (cuenta atrás TOTP, delays de lockout) usan `millis()` en lugar de delays bloqueantes.
***
## Máquina de estados de pantalla
Cada vista interactiva es un estado identificado por una constante `programPosition`. Los eventos táctiles se despachan en función de este valor:
```mermaid theme={null}
stateDiagram-v2
[*] --> SPLASHSCREEN
SPLASHSCREEN --> SETUP : Primer arranque
SPLASHSCREEN --> PIN_SCREEN : Configurado
SETUP --> PIN_SCREEN : Asistente completado
PIN_SCREEN --> MAIN_INDEX : PIN correcto
PIN_SCREEN --> PIN_SCREEN : PIN incorrecto + delay
state "Vistas principales" as main {
MAIN_INDEX --> MAIN_SITE
MAIN_SITE --> MAIN_USER
MAIN_USER --> MAIN_PASS
MAIN_PASS --> MAIN_2FA
MAIN_2FA --> MAIN_INDEX
}
MAIN_SITE --> EDIT : Pulsación larga Centro
MAIN_USER --> EDIT : Pulsación larga Centro
MAIN_PASS --> EDIT : Pulsación larga Centro
EDIT --> MAIN_INDEX : Pulsación larga Centro (guardar)
main --> MENU : Scroll más allá del último slot
MENU --> main : Derecha o Volver
MAIN_2FA --> TOTP_SHOW_CODE : Tiene secreto + hora sincronizada
MAIN_2FA --> TOTP_DATE_ENTRY : Tiene secreto, sin hora
```
***
## Topología del bus I²C
Todos los periféricos comparten un único bus I²C:
```mermaid theme={null}
graph LR
MCU["SAMD21 PA08/PA09 Maestro I²C"]
MCU -->|"0x3C"| OLED["SSD1306 OLED 128×32"]
MCU -->|"0x50"| EEP["M24C64 EEPROM 8 KB"]
MCU -->|"0x60"| ATECC["ATECC608A Elemento seguro"]
MCU -->|"0x69"| TS06["TS06 Controlador táctil"]
style MCU fill:#dbeafe,stroke:#2563eb,color:#000
style ATECC fill:#fef3c7,stroke:#d97706,color:#000
style EEP fill:#dcfce7,stroke:#16a34a,color:#000
```
Velocidad del bus: **100 kHz** (configurado al arranque, coincide con el bootloader).\
La dirección del TS06 es `0xD2 >> 1 = 0x69`.
***
## Dispositivo USB compuesto
ZeroKeyUSB se enumera como un **dispositivo USB Full-Speed compuesto** con dos interfaces:
| Interfaz | Clase | Propósito |
| --------------- | ----- | --------------------------------------------------------------------------------------- |
| **Teclado HID** | 0x03 | Teclea credenciales al host — aparece como un teclado estándar |
| **Serie CDC** | 0x0A | Protocolo ASCII a 115200 bps para backup/restore, sincronización horaria y diagnósticos |
Ambas interfaces están activas simultáneamente tras el arranque. El canal CDC requiere desbloqueo con PIN antes de aceptar cualquier comando que modifique datos.
***
## Huella de memoria
| Región | Tamaño | Uso |
| ---------- | ---------------------------- | --------------------------------------------------------------------------------- |
| **Flash** | 256 KB total, \~64 KB usados | Código de firmware, fuentes, iconos PROGMEM, mapas de teclado, datos constantes |
| **SRAM** | 32 KB total, \~16 KB usados | Buffers UI, `currentSite/User/Pass[16]`, `pinArray[16]`, workspace TOTP |
| **EEPROM** | 8 KB (M24C64) | Credenciales cifradas (61 slots × 128 B), IV, hash de PIN, config, metadatos TOTP |
No se usa asignación dinámica de memoria (`malloc`/`new`) en ningún sitio. Todos los buffers son asignados en pila o estáticos.
***
## Build y verificación
* Compilado con **ARM GCC** usando el core SAMD de Arduino.
* Proceso de build gestionado por `Makefile` — soporta compilación selectiva y flasheo J-Link vía `Dashboard.bat`.
* El binario del firmware se firma con un **MAC BLAKE2s** y se le añade un footer de seguridad de 28 bytes.
* El bootloader verifica esta firma en cada arranque usando CRC32 + BLAKE2s antes de saltar al código de aplicación.
* El firmware sin firmar o manipulado dispara un **delay de penalización de 15 segundos** y cae en modo DFU USB-CDC.
ZeroKeyUSB corre sobre un stack de firmware mínimo: sin RTOS, sin asignación dinámica de memoria y sin puertas traseras de debug.\
Todas las tareas son cooperativas y deterministas en tiempo — la simplicidad se trata como una característica de seguridad.
# Firmante Bitcoin (técnico / auditoría)
Source: https://docs.zerokeyusb.com/es/firmware/bitcoin-signer
Cómo está implementada la cartera Bitcoin airgapped — fuente de entropía, derivación BIP39/32/84, almacenamiento cifrado de la semilla, firma de PSBT y la frontera de confianza exacta — para que puedas auditarla contra el código fuente.
Esta página documenta la **implementación** del firmante Bitcoin para que pueda
auditarse de forma independiente. Para la guía de usuario paso a paso, ve a
[Cartera Bitcoin](/es/getting-started/bitcoin).
Todo el código Bitcoin está en **`ZerokeyOS/zerokey-bitcoin.cpp`** y en la
librería **uBitcoin** incluida (`ZerokeyOS/libraries/uBitcoin`, "Bitcoin" de
Stepan Snigirev). Todo lo de abajo es verificable en ese código.
**Restricción de diseño.** El **ATECC608A integrado es un chip secp256r1
(NIST P‑256)** — *no puede* producir las firmas **secp256k1** de Bitcoin. Por
eso, toda la operativa Bitcoin (derivación BIP32/39/84, ECDSA secp256k1, PSBT) se
hace **por software** con uBitcoin. El ATECC se usa solo como **TRNG hardware** y,
por separado, para proteger la clave maestra AES que cifra la semilla.
## Frontera de confianza
La propiedad más importante: **la semilla nunca sale del dispositivo por USB.**
Solo cruzan el cable datos públicos y firmas.
| Dato | ¿Sale por USB? | Notas |
| --------------------------------------------- | -------------- | ------------------------------------------------------------- |
| Semilla de 12 palabras / entropía de 16 bytes | **Nunca** | Se muestra solo en el OLED (paginada, 3 palabras/página) |
| Clave pública de cuenta `zpub` | Sí | Pública — seguro de exportar |
| Fingerprint de la clave maestra | Sí | Público — necesario para que el firmante reconozca sus inputs |
| Descriptor de salida `wpkh(...)` | Sí | Público |
| Firmas (`PSBT_IN_PARTIAL_SIG`) | Sí | Solo tras una confirmación mantenida en el dispositivo |
**No existe ningún comando serie** que lea la semilla, la entropía o la clave
privada. La única salida de la semilla es el visor de 12 palabras en pantalla
(`btcDisplaySeed`), que dibuja en el OLED y nada más.
## 1 · Entropía y RNG
El material de clave procede exclusivamente del **TRNG hardware del ATECC608A**
(comando `RANDOM` vía `zerokeyAtecc.random`).
* `btcGenEntropy()` extrae 16 bytes frescos con **hasta 5 reintentos** (el chip
puede rechazar el primer comando tras dormir), **descarta un resultado todo a
cero** y **se niega** (devuelve `false`, pone `g_btcRngHardwareOk = false`) en
lugar de recurrir jamás a una fuente débil. La generación de semilla aborta si
se niega.
* El cripto Trezor de uBitcoin llama a los símbolos débiles
`random32()`/`random_buffer()`. El firmware los **sobrescribe** (`extern "C"`
en `zerokey-bitcoin.cpp`) con versiones respaldadas por el TRNG del ATECC a
través de una caché de 32 bytes.
* Si el TRNG falla a mitad, la sobrescritura pone `g_btcRngHardwareOk = false` y
rellena desde una **mezcla NO fiable de `micros()` *solo*** para que los
caminos que no son de clave no se cuelguen — **nunca** para material de semilla
(ese camino ya se ha negado).
```c theme={null}
// zerokey-bitcoin.cpp — la sobrescritura que reemplaza el PRNG débil de Trezor
extern "C" void random_buffer(uint8_t *buf, size_t len); // -> caché TRNG ATECC
extern "C" uint32_t random32(void); // -> random_buffer()
```
**Comprobación de auditoría:** que el firmware *enlace* correctamente prueba que
la sobrescritura fuerte ganó — un símbolo fuerte duplicado sería un error de
enlazado. Confirma que el dispositivo **se niega a crear una cartera** cuando el
ATECC no está disponible.
## 2 · Semilla y almacenamiento cifrado
Los 16 bytes de entropía se convierten en un **mnemónico BIP39 de 12 palabras**
(`mnemonicFromEntropy(entropy, 16)`). El dispositivo guarda la **entropía**, no
las palabras, en **una página EEPROM cifrada con AES** en `BITCOIN_WALLET_ADDR`.
```text theme={null}
Página en claro (32 bytes), antes de cifrar:
[0..3] magic "ZKBW"
[4] versión = 1
[5] longitud de entropía = 16
[6..21] entropía BIP39 de 16 bytes
[22..31] reservado (cero)
```
* Cifrada con la **misma clave maestra AES‑128‑CBC protegida por el ATECC** que
tus credenciales — así la semilla queda ligada a tu **PIN** (ver
[Cifrado AES‑128](/es/firmware/security/aes-128-encryption)).
* `BITCOIN_WALLET_ADDR` es el último slot de 128 bytes de la región de
credenciales (liberado al bajar `MAX_CREDENTIALS` 62→61 en
`zerokey-memorymap.h`); un `static_assert` lo fija.
* Al leer, `btcReadWallet()` verifica magic/versión/longitud antes de usarla y
pone a cero el búfer en claro después.
## 3 · Derivación de claves (BIP84, mainnet)
```c theme={null}
HDPrivateKey hd(mnemonic, ""); // semilla BIP39, passphrase vacía
HDPrivateKey account = hd.derive("m/84'/0'/0'/");
String zpub = account.xpub(); // clave de cuenta SegWit nativo
String addr0 = account.derive("m/0/0/").address(); // bc1q... primera de recibo
```
* **BIP84 SegWit nativo**, ruta `m/84'/0'/0'`, direcciones `bc1q…`, watch‑only
`zpub`. **Solo mainnet.**
* Sin passphrase (la passphrase BIP39 es vacía por diseño en esta versión).
## 4 · Exportación watch‑only
`bitcoinExportWatchOnly()` imprime datos **públicos** por serie (sin PIN — no es
secreto):
```text theme={null}
fingerprint: <8 hex> # hd.fingerprint()
zpub: # account.xpub()
descriptor: wpkh([/84h/0h/0h]/0/*)
first addr: bc1q...
```
El **origen (fingerprint de la clave maestra)** en el descriptor es obligatorio:
`PSBT::sign` de uBitcoin empareja inputs por
`memcmp(root.fingerprint(), derivation.fingerprint, 4)` **y**
`derivation.pubkey == pubkey derivada`. Sin el origen, una cartera (Sparrow,
BlueWallet, Nunchuk…) construiría PSBTs que el dispositivo no reconoce. La webtool
`bitcoin.html` añade el **checksum** de descriptor de Bitcoin Core en JS.
## 5 · Firma de PSBT
`bitcoinReviewPsbt(base64)` → revisión en pantalla → **mantén Centro** →
`bitcoinConfirmSign()`.
`psbt.parseBase64()`; el OLED muestra dirección de destino, importe enviado y
comisión (`psbt.fee()`), detectando el cambio vía la derivación de cada
salida. Es una revisión **persistente** — el dispositivo nunca firma a ciegas.
Una pulsación larga de Centro arma `bitcoinConfirmSign()`. Cualquier otro
botón cancela (`PSBT CANCEL`).
`psbt.sign(hd)` produce firmas ECDSA **deterministas RFC 6979 low‑s** sobre el
sighash **BIP143** de cada input que pertenezca a esta cartera. Si empareja
**0 inputs** el dispositivo responde `PSBT ERR NO_INPUTS` y no firma nada
(esperado para un PSBT de otra cartera).
El PSBT firmado (base64) se devuelve como `PSBT SIGNED`. Finalizar, extraer la
transacción cruda y difundirla ocurren fuera del dispositivo.
## Modelo de amenaza
* **Host comprometido:** puede mentir en el navegador, pero no puede falsificar
la **revisión del OLED** ni la **confirmación mantenida**. Verifica siempre
dirección/importe/comisión en la pantalla del dispositivo. Una firma se
compromete con las salidas exactas vía el sighash, así que una transacción
manipulada solo puede ser *rechazada* por la red, nunca redirigida.
* **Extracción física de la EEPROM:** solo entrega la página `ZKBW` cifrada con
AES; sin la clave maestra ligada al PIN es ruido.
* **Aleatoriedad débil:** descartada por el camino de entropía exclusivo del TRNG
que se niega ante fallo de hardware.
## Cómo auditarlo tú mismo
Lee las 12 palabras con **Tools → Bitcoin → Show seed**. Introdúcelas en
cualquier herramienta BIP84 independiente (Sparrow, una página Ian Coleman
offline, o un script Python `bip_utils`). Deriva `m/84'/0'/0'` y compara
**fingerprint, `zpub` y primera dirección de recibo** con la exportación
**Watch‑only** del dispositivo. Deben coincidir exactamente.
Construye un PSBT para la cartera, fírmalo en el dispositivo, y comprueba que
el `PSBT_IN_PARTIAL_SIG` devuelto es una firma **ECDSA canónica low‑s** sobre
el sighash **BIP143** para la pubkey de ese input. Cualquier librería PSBT
(`python-bitcoinlib`, `bitcoinjs`, `analyzepsbt` de Bitcoin Core) puede
finalizarla y verificarla.
Observa la línea serie durante **Watch‑only** y durante la firma: solo
aparecen bytes de `zpub`/fingerprint/descriptor/firma — nunca las palabras ni
la entropía. Grepea el firmware: el único escritor de las palabras es
`btcDisplaySeed` (OLED), nunca `SerialUSB`.
**Validación de referencia (2026‑06‑30).** Un verificador BIP84 independiente en
Python puro reprodujo el `zpub` + primera dirección exactos del dispositivo a
partir de las 12 palabras, y una prueba de PSBT sin fondos produjo una firma
**byte a byte igual** a la firma RFC 6979 low‑s predicha de forma independiente
sobre el sighash BIP143, verificando como ECDSA canónica válida.
## Mapa de código
| Aspecto | Dónde |
| ------------------------------------------------------------- | ----------------------------------------------------- |
| Entropía, sobrescritura RNG, almacenamiento, derivación, PSBT | `ZerokeyOS/zerokey-bitcoin.cpp` |
| secp256k1 / BIP32/39 / PSBT | `ZerokeyOS/libraries/uBitcoin` |
| Clave maestra AES, cifrar/descifrar página | `ZerokeyOS/zerokey-security.cpp` |
| E/S de página EEPROM, dirección del slot de cartera | `ZerokeyOS/zerokey-eeprom.cpp`, `zerokey-memorymap.h` |
| Cableado de mantener‑para‑firmar | `ZerokeyOS/zerokey-io.cpp` |
Crear la cartera, exportar watch‑only, firmar un PSBT.
La clave maestra que protege la semilla almacenada.
# Protocolo de flasheo (WebTool)
Source: https://docs.zerokeyusb.com/es/firmware/bootloader/flashing-protocol
La WebTool ahora actúa como un cargador simple que transfiere el binario pre-firmado al dispositivo y gestiona el reinicio.
## WebTool: un cargador seguro
La **WebTool** (el flasher basado en navegador) ya no calcula características de seguridad; solo actúa como una interfaz de transferencia.
### 📥 Ficheros de entrada
La WebTool solo debe recibir **ficheros binarios (`*.bin`) que ya hayan sido pre-firmados** por la herramienta de firma offline.
* El fichero contiene el código de aplicación **MÁS** el footer de seguridad de 28 bytes.
### 🔄 Protocolo de transferencia
El proceso de flasheo sigue el protocolo USB CDC tradicional, tratando el fichero firmado como un único *payload* completo:
1. **Inicio:** La WebTool envía `HELLO` y luego `ERASE APP` para limpiar el espacio de la aplicación.
2. **Escritura:** La WebTool envía el **fichero firmado** entero en *chunks* usando el comando `WRITE addr len crc32` y el *payload* binario.
3. **Finalización:** Se envía el comando `DONE`.
4. **Activación:** El bootloader recibe los datos, los escribe en Flash (incluyendo el footer en su ubicación final) y se reinicia.
### ⏱️ Gestión de timeouts
Para garantizar que el proceso de flasheo no falle prematuramente si se carga firmware no autorizado, la WebTool espera durante un periodo extendido:
* **Retraso incrementado:** El tiempo de espera final tras enviar `DONE` se ha extendido a **20 segundos**.
* **Propósito:** Este tiempo cubre el retraso de 15 segundos impuesto por el bootloader si la comprobación de autenticidad falla, garantizando que la conexión USB no se corta antes de que el dispositivo pueda reiniciarse o entrar en modo de espera.
***
# Herramienta de firma offline
Source: https://docs.zerokeyusb.com/es/firmware/bootloader/signer-tool
Detalles sobre cómo se firma el firmware de forma segura antes de su distribución para garantizar autenticidad.
## Arquitectura de firma segura
Para proteger la **clave secreta BLAKE2s** (`ZK_SECRET_KEY`) y mantener la WebTool pública, ZeroKeyUSB usa un proceso de **firma offline**.
### 🔑 El secreto: clave de firma
* **Residencia:** La clave secreta de 32 bytes existe solo dentro de la **herramienta de firma offline privada** y en el propio bootloader del dispositivo.
* **Función:** La clave se usa para calcular el **MAC BLAKE2s** del firmware.
* **Seguridad:** Como la WebTool es pública, este enfoque garantiza que **ningún usuario o atacante puede extraer la clave de firma** para crear su propio firmware oficial.
### ✍️ Proceso de firma (offline)
1. **Entrada:** El binario del firmware (`firmware.bin`) listo para release.
2. **Cálculo:** La herramienta calcula el **CRC32** y el **MAC BLAKE2s** (16 bytes) del fichero.
3. **Creación del footer:** Ensambla la estructura del **footer de seguridad** con el Magic Number, la longitud del código, el CRC32 y el MAC.
4. **Concatenación:** El footer se **concatena** al final del binario del firmware.
5. **Salida:** Se produce un único **fichero binario pre-firmado** (`firmware_signed_footer.bin`), listo para ser subido por la WebTool pública.
### 📦 Reproducibilidad y transparencia
Aunque la clave de firma es secreta, el firmware sigue siendo **open source y auditable**. El proceso garantiza que:
* Solo el equipo de desarrollo puede crear un binario que el bootloader acepta como oficial (saltándose el retraso de 15 segundos).
* Se mantiene el principio de que **no hay mecanismos de firma o actualización remota**.
***
# Verificación de integridad y autenticidad
Source: https://docs.zerokeyusb.com/es/firmware/bootloader/verification
Proceso de verificación de la aplicación usando CRC32 hardware y BLAKE2s MAC, incluyendo la lógica de penalización.
## Cadena de confianza al arrancar
El Bootloader de ZeroKeyUSB ejecuta un proceso de verificación criptográfico rápido antes de ceder el control al firmware de aplicación. Este proceso garantiza que el firmware **no ha sido alterado (integridad)** y que **proviene de una fuente oficial (autenticidad)**.
### ⚡ Comprobación rápida de integridad (CRC32 hardware)
La verificación se realiza usando el hardware **DSU (Data Scrambling Unit)** del microcontrolador SAMD21 para calcular CRC32. Esto permite escanear toda la memoria Flash a la máxima velocidad del bus:
* **CRC32 acumulativo:** El CRC32 se calcula eficientemente chunk por chunk.
* **Velocidad:** Minimiza el tiempo de arranque, asegurando que la verificación completa tarde solo unos pocos milisegundos.
### 🔐 Autenticación criptográfica (BLAKE2s MAC)
Para garantizar que el firmware fue firmado con la clave secreta, se usa el algoritmo **BLAKE2s-128 MAC (Message Authentication Code)**.
1. **MAC en el footer:** El firmware de aplicación final termina con un **footer de seguridad** de 28 bytes que contiene el CRC32 final y el MAC BLAKE2s precalculado.
2. **Recálculo:** El bootloader recalcula el MAC sobre todo el código de aplicación usando la **clave secreta** embebida (`ZK_SECRET_KEY`).
3. **Aprobación:** Si el MAC calculado coincide con el MAC del footer, la autenticación es exitosa.
### 🛡️ Comprobaciones de cordura y rango
Antes de la verificación criptográfica, se ejecutan comprobaciones de punteros para prevenir ataques de redirección:
* **Stack Pointer (SP):** Se verifica que la dirección inicial del Stack Pointer esté dentro del rango válido de **SRAM**.
* **Reset Handler:** Se comprueba que la dirección de la función de inicio de la aplicación esté dentro de la región **Flash** reservada para el firmware.
### 🚨 Penalización para software no oficial (15 segundos)
En los casos en los que el firmware ha sido alterado o proviene de una fuente sin firmar, el bootloader impone una política de penalización estricta:
* **Fallo de verificación:** Si el CRC32 o el MAC BLAKE2s no coinciden, se aplica un **retraso de 15 000 milisegundos** (`PENALTY_DELAY_MS`) usando el *SysTick Timer*.
* **Efecto:** Este retraso desincentiva el uso de firmware no autorizado y evita bucles de reinicio rápidos, ofreciendo al usuario una ventana de tiempo para entrar en el modo de flasheo del bootloader.
***
# Enlace de navegador
Source: https://docs.zerokeyusb.com/es/firmware/browser-link
Cómo los comandos serie del host FIND / ZK PING / TIME y la extensión de Chrome gobiernan la búsqueda del dispositivo — solo navegación, nunca teclea.
El **enlace de navegador** permite a un host —la
[extensión](/es/getting-started/browser-extension) de Chrome/Edge— gobernar la
búsqueda alfabética del dispositivo por el puerto serie USB CDC. Es **solo
navegación**: el host puede sugerir *dónde mirar*, pero una credencial siempre la
teclea el dispositivo por USB HID tras una pulsación física.
## Comandos serie
Parseados por `handleIncomingHostRequests()` en `zerokey-io.cpp`. Tabla completa
del protocolo en [Utilidades USB](/es/firmware/usb-utilities).
| Comando | Respuesta | Propósito |
| -------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `ZK PING` | `ZK PONG ON` / `OFF` | Sonda de identidad + estado del link (deja que la extensión ponga su icono en gris). Siempre responde. |
| `FIND ` | `OK FIND ` / `ERR OFF` / `ERR BUSY` / `ERR EMPTY` | Salta la búsqueda alfabética del dispositivo a ``. |
| `TIME ` | `OK TIME` / `ERR OFF` | Ajusta el reloj del dispositivo (segundos Unix UTC) para que los TOTP sean exactos. |
## El toggle de Herramientas
`Menú → Herramientas → Chrome: On/Off` controla el link. El flag se persiste en
EEPROM (`0x000D`) y se lee al arrancar en `chromeLinkEnabled`; viene **On** por
defecto (EEPROM en blanco se lee como habilitado). En off, `FIND` y `TIME` se
ignoran con `ERR OFF` y no corre nada más allá del ping de identidad — superficie
de ataque mínima para quien no use la extensión.
## Modelo de seguridad
* **Solo navegación.** Deliberadamente no hay ningún comando serie que teclee o
revele una credencial. Teclear siempre requiere una pulsación física.
* `FIND` solo se acepta con la bóveda **desbloqueada y navegando** la lista de
credenciales (`MAIN_SITE/USER/PASS/2FA` o la búsqueda) — nunca durante el PIN,
una edición, el menú o TOTP, así que no puede pisar entrada en curso.
* Lo peor que puede hacer una página o host malicioso es mover el cursor de
búsqueda del dispositivo.
* `TIME` solo ajusta el reloj (afecta a TOTP), que no es secreto; aun así va
detrás del toggle.
## Arquitectura de la extensión
* **Web Serial** (`navigator.serial`) — la extensión abre el puerto CDC tras un
permiso de usuario único y **por origen** (en una pestaña normal, no en el
popup, que el selector de puertos cerraría). Identifica el dispositivo con
`ZK PING`, así que no depende de un VID/PID USB concreto.
* **Letra del dominio principal** — usa la primera letra del dominio registrable,
ignorando subdominios (`ss.revolut.com` → `R`), con una pequeña lista de
sufijos de dos niveles (`co.uk`, `com.br`, …).
* **Sync de reloj** — envía `TIME ` en cada uso para que los TOTP sean
exactos sin abrir la [herramienta de sincronización](/es/firmware/totp/web-time-sync-tool).
* **Enfoque del campo** vía `chrome.scripting`: heurísticas (`autocomplete=username`,
`type=email`, name/id que contenga user/email/login, o el input de texto de un
formulario con campo de contraseña), inyectadas en todos los frames y diferidas
para que el cursor caiga tras cerrarse el popup y recuperar el foco la página.
## Limitaciones
* La detección de campo falla en algunas SPA, shadow DOM e iframes de otro origen.
* Los flujos que separan usuario y contraseña (algunos logins de Google/Microsoft)
no encajan con la salida *usuario → TAB → contraseña* del dispositivo.
* El greyado del icono en vivo sin abrir el popup necesitaría un documento
offscreen (MV3) manteniendo la conexión serie; el build actual actualiza el
badge en cada apertura del popup.
# Sistema de pantalla
Source: https://docs.zerokeyusb.com/es/firmware/display
Cómo se renderiza la UI del OLED, cómo se anima y cómo se mantiene clara mientras navegas tus credenciales.
## 128×32 píxeles con propósito
ZeroKeyUSB usa un **panel OLED blanco** (controlador SSD1306, I²C en `0x3C`) con una resolución de 128×32 píxeles.\
El firmware mantiene la interfaz intencionadamente minimalista: tipografía grande, layout claro y transiciones suaves que siguen siendo legibles incluso con poca luz.
***
## Jerarquía de pantallas
```mermaid theme={null}
graph TD
SPLASH["Pantalla de bienvenida Logo ZeroKeyUSB"]
SETUP["Asistente de setup 10 páginas con scroll"]
PIN["Entrada de PIN selector de dígito + puntos"]
MAIN["Pantalla principal Sitio / Usuario / Pass / 2FA"]
EDIT["Editor 3 páginas de teclado"]
MENU["Menú lista con scroll"]
TOTP["Vista TOTP código de 6 dígitos + cuenta atrás"]
TEXT["Página de texto confirmaciones, info"]
SPLASH --> SETUP
SPLASH --> PIN
SETUP --> PIN
PIN --> MAIN
MAIN --> EDIT
MAIN --> MENU
MAIN --> TOTP
MENU --> TEXT
style PIN fill:#fef3c7,stroke:#d97706,color:#000
style MAIN fill:#dbeafe,stroke:#2563eb,color:#000
style EDIT fill:#dcfce7,stroke:#16a34a,color:#000
```
***
## Pipeline de renderizado
```mermaid theme={null}
flowchart LR
A["Componer frame en buffer RAM (512 bytes)"] --> B["Transferencia I²C de frame completo a SSD1306"]
B --> C["Pantalla actualizada ~30 fps"]
style A fill:#dbeafe,stroke:#2563eb,color:#000
style B fill:#fef3c7,stroke:#d97706,color:#000
```
1. **Construcción del frame buffer** — la aplicación compone toda la pantalla en un buffer RAM de 512 bytes usando funciones de dibujo de `Adafruit_SSD1306`: `setCursor()`, `print()`, `drawRect()`, `fillRect()`, `drawBitmap()`.
2. **Transferencia de frame completo** — `display.display()` envía los 512 bytes al OLED por I²C en una sola ráfaga.
3. **Cadencia de refresco** — el bucle principal cooperativo redibuja solo cuando cambia el estado de la pantalla, evitando tráfico I²C innecesario.
***
## Tipos de pantalla
### Pantalla de entrada de PIN
* Muestra el selector de dígito actual (0–9) con navegación Arriba/Abajo.
* Los dígitos introducidos se muestran como puntos rellenos (●) por seguridad.
* La longitud del PIN se muestra como indicador de cuenta.
### Pantalla principal de credenciales
* **Cuatro líneas** mostrando el slot actual:
* Línea 0: Indicador de índice de slot
* Línea 1: Nombre del sitio (scroll si > \~20 caracteres)
* Línea 2: Usuario
* Línea 3: Contraseña (oculta por defecto)
* Un indicador de contexto (`SITE`, `USER`, `PASS`, `2FA`) aparece arriba.
### Pantalla de editor
* **Tres páginas de teclado** seleccionables con Arriba/Abajo:
* Página 1 (`EDIT_KB1`): `A-Z`, corchetes, símbolos
* Página 2 (`EDIT_KB2`): `a-z`, puntuación
* Página 3 (`EDIT_KB3`): `0-9`, espacio, caracteres especiales
* Controles Izquierda/Derecha: posición del cursor (◀ / ▶), carácter aleatorio, retroceso
* Carácter seleccionado resaltado con colores invertidos
### Pantalla de menú
* Lista con scroll y resaltado invertido sobre el ítem seleccionado.
* Cuando los ítems superan las 4 filas, aparece una **barra de scroll con thumb** en el borde derecho (3 px de ancho).
* La posición del thumb se actualiza proporcionalmente a la posición de scroll.
### Páginas del asistente de setup
* 10 páginas de texto con scroll y navegación Arriba/Abajo.
* Aparecen en pantalla un indicador de paso (`1/9`, `2/9`, etc.) y pistas en el footer.
* Las páginas con > 4 líneas muestran un thumb de scroll.
### Pantalla de código TOTP
* **Texto a doble tamaño** para el código de 6 dígitos.
* Cuenta atrás en segundos: `"Expires in: XXs"`.
* Se refresca automáticamente cada segundo.
* Tocar cualquier pad vuelve a las credenciales.
***
## Tipografía y assets
| Asset | Formato | Tamaño | Uso |
| ------------------------ | --------------------- | -------- | -------------------------------------------------- |
| **Fuente por defecto** | Adafruit GFX incluida | 6×8 px | Menús, etiquetas, texto de info |
| **Tamaño de texto 2** | escalado 2× | 12×16 px | Códigos TOTP, prompts grandes |
| **Iconos** | bitmaps PROGMEM | 16×16 px | Ítems del menú (backup, settings, danger, info) |
| **SVGs del dispositivo** | Vector en `/images/` | Varios | Ilustraciones de pads táctiles en la documentación |
Todas las fuentes e iconos se almacenan en **Flash (PROGMEM)** — sin carga en runtime desde EEPROM.
***
## Scroll
Hay dos tipos de scroll implementados:
### Auto-scroll (nombres de credenciales)
* Los nombres más largos que el ancho de pantalla (\~20 caracteres) hacen scroll **horizontal** a ritmo constante.
* Gestionado por `refreshMainScrollIfNeeded()` en el bucle principal.
* El scroll se pausa brevemente en cada extremo antes de invertirse.
### Scroll vertical (menús y asistente)
* Los ítems de menú y páginas del asistente hacen scroll vertical con Arriba/Abajo.
* `menuScrollTop` rastrea la primera fila visible.
* `ensureMenuSelectedVisible()` mantiene el ítem resaltado a la vista.
* Aparece un thumb de scroll relleno proporcional a la longitud del contenido en el borde derecho.
***
## Feedback visual
| Tipo de feedback | Implementación |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Resaltado de selección** | Colores invertidos (texto negro sobre fondo blanco) |
| **Progreso de pulsación larga** | Dos plumas blancas se llenan por el borde de la pantalla vía `drawLongPressProgress()`, con un cabezal negro por delante de cada una para seguir el avance con claridad |
| **Spinner de actividad** | Animación basada en frames vía `renderActivityScreen()` |
| **Barra de progreso** | `renderProgress()` con título, subtítulo, hecho/total |
| **Indicador de tecleo** | `renderTypingActivity()` muestra caracteres tecleados vs. total |
| **Indicador de contexto** | La barra superior muestra el contexto actual (`MENU`, `SITE`, `TOTP`, `SETUP`) |
***
## Apagado por inactividad
Tras **1 minuto sin ninguna pulsación táctil**, el firmware apaga el OLED
(`SSD1306_DISPLAYOFF`) desde `handleButtonChecker()`. Es un apagado **solo de la
pantalla**:
* La bóveda **no** se bloquea y `programPosition` no cambia — al despertar
aparece exactamente la misma pantalla.
* **Cualquier toque** enciende el panel (`SSD1306_DISPLAYON`); ese primer toque
se consume, así que solo despierta y no navega además.
* Un `FIND` del host desde la [extensión de navegador](/es/getting-started/browser-extension)
también enciende el panel para que el salto sea visible.
El MCU sigue funcionando mientras está apagado (temporizadores, pasos TOTP,
serie). Los redibujados periódicos automáticos (p. ej. la cuenta atrás del TOTP)
**no** cuentan como actividad — solo el táctil y los comandos del host.
***
## Seguridad del display
* Los campos sensibles (contraseñas, códigos TOTP) se **muestran brevemente y se borran** — el frame buffer se sobrescribe en la siguiente transición.
* El display **se apaga tras 1 minuto de inactividad pero no se bloquea** (ver arriba); bloquear la bóveda sigue requiriendo quitar la alimentación (desconexión USB).
* Mientras se teclea al host, `renderTypingActivity()` muestra progreso sin mostrar el contenido de la credencial.
* Ninguna credencial descifrada se guarda en la GDDRAM del controlador OLED más allá del frame actual.
El OLED queda detrás del encapsulado epoxy sellado, proporcionando excelente contraste y resistencia a arañazos, polvo y humedad.
# Gestión de EEPROM
Source: https://docs.zerokeyusb.com/es/firmware/eeprom-management
Mapa de memoria, layout de página y operaciones de lectura/escritura para el almacenamiento de credenciales M24C64-WMN6TP.
ZeroKeyUSB guarda todos los secretos dentro de una EEPROM externa **ST M24C64-WMN6TP** (64 Kbit = 8 KB). El firmware gestiona lecturas y escrituras con cuidado, respetando los límites de página y manteniendo todos los datos de credenciales cifrados en reposo.
***
## Mapa de memoria
La EEPROM está organizada en una **zona de configuración** (primeros \~220 bytes) y una **zona de credenciales** (resto):
```mermaid theme={null}
block-beta
columns 1
block:config["Zona de configuración (0x0000–0x00FF)"]
A["0x0000: Flag de config (1B)"]
B["0x0001: Modo de pantalla (1B)"]
C["0x0002: Intentos fallidos (1B)"]
D["0x0010–0x001F: IV AES (16B)"]
E["0x0020–0x0023: legacy / reservado (4B)"]
F["0x0024: Flag de aprovisionamiento (1B)"]
G["0x0028–0x0037: legacy / reservado (16B)"]
I["0x003E: Layout de teclado (1B)"]
J["0x0040–0x0047: Último epoch TOTP (8B)"]
H["0x0048–0x0067: Hash de PIN (32B)"]
K["0x0068–0x00E3: Meta TOTP (124B)"]
end
block:creds["Zona de credenciales (0x0100–0x1FFF)"]
L["61 slots de credenciales × 128 bytes cada uno"]
end
style config fill:#dbeafe,stroke:#2563eb
style creds fill:#dcfce7,stroke:#16a34a
```
| Dirección | Tamaño | Contenido | Fuente en código |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `0x0000` | 1 B | Flag del asistente de config (`0x42` = hecho) | `zerokey-setup.cpp` |
| `0x0001` | 1 B | Orientación de pantalla (0 = normal, 1 = invertida) | `EEPROM_SCREEN_MODE_ADDR` |
| `0x0002` | 1 B | Contador de intentos fallidos (backoff persistente, reaplicado al arrancar) | `FAILED_ATTEMPTS_ADDR` |
| `0x0010–0x001F` | 16 B | Vector de inicialización AES-CBC | `EEPROM_IV_ADDR` |
| `0x0020–0x0023` | 4 B | *Reservado.* Umbral de intentos legacy del lockout por Counter0 ya eliminado; no se lee ni escribe. | — |
| `0x0024` | 1 B | Flag de aprovisionamiento (`0xA5` = aprovisionado) | `EEPROM_PROVISION_FLAG` |
| `0x0028–0x0037` | 16 B | *Reservado.* Contenía la clave maestra AES en firmware anteriores; la clave ahora vive en el slot 8 del ATECC y nunca toca la EEPROM. | — |
| `0x003E` | 1 B | Selector de layout de teclado (0–8) | `EEPROM_LAYOUT_ADDR` |
| `0x0040–0x0047` | 8 B | Último epoch TOTP (big-endian) | `EEPROM_LAST_TOTP_EPOCH_ADDR` |
| `0x0048–0x0067` | 32 B | Hash de PIN: SHA-256(PIN ∥ serial) | `EEPROM_PIN_HASH` |
| `0x0068–0x00E3` | 124 B | Metadatos TOTP: 2 B × 61 slots (algoritmo + secret\_len) | `CONFIG_TOTP_META_START` |
| `0x0100–0x1F7F` | 7 808 B | 61 slots de credenciales (128 B cada uno = 4 páginas × 32 B) | `EEPROM_CREDENTIAL_BASE` |
| `0x1F80–0x1FFF` | 128 B | Wallet Bitcoin — página de semilla cifrada con AES ([auditoría](/es/firmware/bitcoin-signer)) | `BITCOIN_WALLET_ADDR` |
***
## Estructura del slot de credencial
Cada uno de los **61 slots de credenciales** ocupa **4 páginas consecutivas de 32 bytes en EEPROM** (128 bytes en total):
```mermaid theme={null}
graph LR
subgraph Slot["Slot de credencial N (128 bytes)"]
P0["Página 0 Nombre del sitio 32 B ciphertext"]
P1["Página 1 Usuario 32 B ciphertext"]
P2["Página 2 Contraseña 32 B ciphertext"]
P3["Página 3 Secreto TOTP 32 B ciphertext"]
end
P0 --- P1 --- P2 --- P3
style P0 fill:#dbeafe,stroke:#2563eb,color:#000
style P1 fill:#dbeafe,stroke:#2563eb,color:#000
style P2 fill:#dbeafe,stroke:#2563eb,color:#000
style P3 fill:#fef3c7,stroke:#d97706,color:#000
```
Cada página de 32 bytes contiene:
* **16 bytes de texto plano** (rellenados con `0xFF`) cifrados como **dos bloques AES-128 CBC**
* El cifrado usa el IV global del dispositivo más la clave maestra AES que vive dentro del slot 8 del ATECC — las rondas del cifrador corren en el chip, nunca en el MCU.
No hay un byte `status` o CRC separado por página. Los slots vacíos se escriben como blancos `0xFF` cifrados durante `silentEraseAll()`.
***
## Cálculo de dirección de página
```
credentialPageAddress(slotIndex, pageIndex) =
(FIRST_CREDENTIAL_PAGE + slotIndex × 4 + pageIndex) × 32
```
Donde:
* `FIRST_CREDENTIAL_PAGE` se calcula a partir de `CONFIG_TOTP_META_END` redondeado al siguiente límite de 32 bytes.
* `slotIndex` va de 0 a 61.
* `pageIndex` va de 0 (sitio) a 3 (TOTP).
***
## Secuencia de escritura
La función `writeEepromPage()` escribe 32 bytes en una dirección alineada a página:
1. Comprueba la presencia de EEPROM vía `Wire.beginTransmission()` + test de ACK.
2. Envía la dirección de 2 bytes (MSB primero) seguida de 32 bytes de datos.
3. Espera 10 ms para el ciclo de escritura interno de la EEPROM.
4. Devuelve `true` si `Wire.endTransmission()` no reportó error.
Para escrituras relacionadas con seguridad que cruzan límites de página (p. ej. IV de 16 bytes, hash de PIN de 32 bytes), `eepromWriteRaw()` en `zerokey-security.cpp` divide los datos en los límites de 32 bytes para evitar el comportamiento de wrap-around de direcciones del M24C64.
***
## Secuencia de lectura
`readEepromPage()` lee exactamente 32 bytes:
1. Envía la dirección de 2 bytes vía escritura I²C.
2. Emite `Wire.requestFrom(eepromAddress, 32)`.
3. Lee todos los bytes disponibles al buffer de salida.
4. Rellena con ceros los bytes no recibidos (lectura parcial = error).
***
## Metadatos TOTP
Cada slot de credencial tiene una entrada de metadatos TOTP de 2 bytes en la zona de configuración:
| Byte | Contenido |
| ---- | ----------------------------------------------------------------------------- |
| 0 | Código de algoritmo: `0` = ninguno, `1` = SHA-1, `2` = SHA-256, `3` = SHA-512 |
| 1 | Longitud del secreto en bytes crudos (antes de codificar en Base32) |
Los metadatos se leen/escriben independientemente de las páginas de credenciales para permitir detección rápida de TOTP sin descifrar el slot completo.
***
## Características de desgaste
* La M24C64-WMN6TP soporta **>1 millón de ciclos de escritura por página** (garantía del datasheet).
* Las páginas de credenciales solo se reescriben cuando el usuario edita un campo o importa datos.
* Las páginas de la zona de configuración (IV, hash de PIN, umbral) se escriben durante el aprovisionamiento y cambios de PIN — eventos infrecuentes.
* El epoch TOTP en `0x0040` se actualiza cada vez que el usuario sincroniza la hora o genera un código — es la ubicación más escrita.
* Como las credenciales suelen ser estáticas, la vida útil esperada de la EEPROM supera las décadas de uso normal.
***
## Resolución de problemas
| Síntoma | Causa | Solución |
| --------------------------------------- | ----------------------------------- | ------------------------------------------------------------ |
| `EEPROM not found` | Conexión I²C rota | Comprueba las soldaduras; verifica los pull-ups en SDA/SCL |
| `EEPROM write 0xNNN` | Escritura fallida en la dirección | Reintenta; si persiste, la EEPROM puede estar dañada |
| Credenciales corruptas | IV o clave AES cambió sin re-cifrar | Reset de fábrica + re-aprovisionar; restaurar desde backup |
| El slot aparece en blanco tras importar | Parsing del secreto TOTP falló | Verifica codificación Base32; verifica soporte del algoritmo |
Ninguna credencial descifrada toca memoria persistente sin acción explícita del usuario. El texto plano solo existe en SRAM durante la sesión activa.
# Manejo de entrada táctil
Source: https://docs.zerokeyusb.com/es/firmware/io-touch-handling
Cómo se escanean las teclas capacitivas, se aplica debounce y se mapean a navegación de menú.
ZeroKeyUSB reemplaza los botones mecánicos con **cinco pads táctiles de cobre** conectados a un **controlador capacitivo TS06** dedicado. El firmware sondea este controlador por I²C y traduce los toques en eventos de navegación.
***
## Vista general del hardware
| Componente | Detalle |
| ---------------------- | ------------------------------------------------------------------------------- |
| **Controlador** | TS06 — IC táctil capacitivo de 6 canales |
| **Dirección I²C** | `0xD2 >> 1 = 0x69` |
| **Pads usados** | 5 de 6 canales: Izquierda, Derecha, Arriba, Abajo, Centro |
| **Registro de estado** | `0x25` — bitmask de canales actualmente tocados |
| **Sensibilidad** | Puesta a `0x3F` (mínima) en canales 0–2 al arranque para evitar disparos falsos |
El controlador gestiona la calibración de baseline internamente y reporta qué canales están activos vía el registro de estado.
***
## Inicialización táctil
Al arranque (`zerokey-setup.cpp`), el firmware:
1. Prueba el TS06 en `0x69` con hasta **5 reintentos** (separados 20 ms).
2. Si lo detecta, escribe la sensibilidad mínima (`0x3F`) en los registros `0x00–0x02`.
3. Configura el modo de operación vía registros `0x05–0x06`.
4. Pone `ts06_ok = true` — si no se encuentra el controlador, el táctil queda deshabilitado y el log serie muestra `"TS06 not found on I2C"`.
***
## Ciclo de polling
```mermaid theme={null}
flowchart TD
A["Leer registro de estado 0x25"] --> B{"¿Algún canal activo?"}
B -->|No| C["Limpiar timers de pulsación"]
B -->|Sí| D{"¿Mismo canal que el lockeado?"}
D -->|No| E["Ignorar — lockout activo"]
D -->|Sí / Ninguno lockeado| F{"¿Duración de pulsación?"}
F -->|"< 80 ms"| G["Aún en debounce"]
F -->|"80–800 ms"| H["Evento de pulsación corta"]
F -->|"> 800 ms"| I["Evento de pulsación larga"]
H --> J["Dispatch a la pantalla actual"]
I --> J
C --> A
G --> A
style A fill:#dbeafe,stroke:#2563eb,color:#000
style H fill:#bbf7d0,stroke:#16a34a,color:#000
style I fill:#fef3c7,stroke:#d97706,color:#000
```
El método `handleButtonChecker()` corre en el bucle principal:
1. Lee `STATUS_REGISTER` (0x25) vía `readRegister()` por I²C.
2. Para cada bit de canal:
* Registra `pressStartTime` cuando un canal se vuelve activo por primera vez.
* Aplica un **debounce de 80 ms** (`DEBOUNCE_MS`) — las liberaciones más cortas que esto se ignoran.
* Aplica un **lockout de canal de 150 ms** (`CHANNEL_LOCKOUT_MS`) — tocar un pad distinto mientras hay uno activo se ignora.
3. Al soltar:
* Si se mantuvo > `LONG_PRESS_THRESHOLD` (800 ms) → dispatcha el handler de pulsación larga.
* En otro caso → dispatcha el handler de pulsación corta.
***
## Mapeo de gestos
| Gesto | Disparador | Pantalla principal | Editor | Menú |
| -------------- | ---------- | ------------------------- | ---------------------------------------- | ---------------------------------------- |
| Tap Izquierda | \< 800 ms | Slot anterior | Mover cursor izquierda | Volver / Salir de submenú |
| Tap Derecha | \< 800 ms | Slot siguiente | Mover cursor derecha / Entrar al teclado | Entrar al submenú / Salir a credenciales |
| Tap Arriba | \< 800 ms | Ciclar a vista Sitio | Cambiar carácter | Navegar arriba |
| Tap Abajo | \< 800 ms | Ciclar a vista 2FA | Cambiar página de teclado | Navegar abajo |
| Tap Centro | \< 800 ms | Tipear credencial al host | Insertar carácter | Seleccionar / Confirmar |
| Hold Izquierda | ≥ 800 ms | Saltar 10 slots atrás | — | — |
| Hold Derecha | ≥ 800 ms | Saltar 10 slots adelante | — | — |
| Hold Centro | ≥ 800 ms | Entrar en modo edición | Guardar y salir del editor | Autorizar import/export |
***
## Feedback visual de pulsación larga
Cuando una pulsación larga está en progreso, `drawLongPressProgress()` renderiza una barra de progreso que se va llenando en el OLED. Esto da al usuario confirmación visual de que debe seguir manteniendo. Soltar antes de los 800 ms cancela la acción.
***
## Controles en la pantalla de PIN
En la pantalla de entrada de PIN, los controles cambian:
| Pad | Acción |
| -------------------------- | ------------------------------------------------ |
| **Arriba / Abajo** | Cambia el dígito actual (0–9) |
| **Derecha** | Añade el dígito actual al PIN (hasta 16 dígitos) |
| **Izquierda** | Borra el último dígito introducido |
| **Centro** | Envía el PIN para verificación |
| **Pulsación larga Centro** | Teclea el número de serie del dispositivo |
***
## Manejo de errores
* Si el TS06 no se detecta al arranque, `ts06_ok` se pone a `false` y la lectura del registro de estado devuelve `0x00` — efectivamente deshabilitando la entrada táctil.
* El firmware no muestra una pantalla de error táctil; en su lugar, el log serie reporta el problema para debug.
* El táctil se deshabilita silenciosamente durante los delays de lockout (`waitFromEeprom()`) y durante operaciones largas como el borrado de credenciales.
El TS06 opera independientemente de las operaciones de EEPROM o ATECC608A. Como todos comparten el mismo bus I²C a 100 kHz, el polling táctil se entrelaza con otro tráfico I²C en el bucle principal cooperativo.
# Sistema de menú
Source: https://docs.zerokeyusb.com/es/firmware/menu
Explora la estructura del menú de ZeroKeyUSB, el asistente de setup y cómo usar cada función de forma segura.
## Control simple, funciones potentes
ZeroKeyUSB no tiene botones, ni apps, ni menús ocultos — solo **cinco puntos táctiles dorados** que lo controlan todo.\
El menú es accesible **tras introducir tu PIN maestro** haciendo scroll más allá del último slot de credenciales.
***
## Estructura del menú
```mermaid theme={null}
graph TD
ROOT["Menú principal"]
ROOT --> TOOLS["Tools"]
ROOT --> SETTINGS["Settings"]
ROOT --> DANGER["Danger Zone"]
ROOT --> INFO["Info"]
TOOLS --> IMP["Import"]
TOOLS --> EXP["Export"]
TOOLS --> BTC["Bitcoin"]
TOOLS --> CHR["Chrome: On/Off"]
SETTINGS --> ROT["Rotate Screen"]
SETTINGS --> KB["Keyboard: XX-XX"]
SETTINGS --> UILANG["UI Language"]
SETTINGS --> READER["Reader: On/Off"]
SETTINGS --> PWD["Pwd: XX"]
DANGER --> FACTORY["Factory Reset"]
DANGER --> BOOT["Bootloader Mode"]
INFO --> SW["SW: x.x.x"]
INFO --> SN["SN: XXXXXXXX"]
style ROOT fill:#dbeafe,stroke:#2563eb,color:#000
style DANGER fill:#fee2e2,stroke:#dc2626,color:#000
```
***
## Navegación del menú
| Gesto | Acción |
| --------------- | ------------------------------------------------ |
| **Arriba ↑** | Mueve la selección arriba (envuelve al final) |
| **Abajo ↓** | Mueve la selección abajo (envuelve al principio) |
| **Centro ●** | Selecciona / Ejecuta el ítem resaltado |
| **Izquierda ←** | Vuelve al menú padre o sale a credenciales |
| **Derecha →** | Sale del menú, salta al slot 0 de credenciales |
Cuando un menú tiene más ítems de los que caben en las 4 filas, aparece una **barra de scroll con thumb** en el borde derecho. La selección permanece visible al hacer scroll.
***
### 🧰 Tools
Este submenú se llamaba **Backup** en firmware anteriores; se renombró a **Tools** al añadir la cartera Bitcoin.
| Ítem | Acción |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Import** | Recibe credenciales desde el host vía USB serie (CDC). El dispositivo muestra "Waiting for data from the web app". |
| **Export** | Envía los 61 slots de credenciales como CSV en texto plano por USB serie. Requiere autorización con pulsación larga Centro. |
| **Bitcoin** | Cartera Bitcoin airgapped: crear cartera, mostrar la semilla de 12 palabras (solo pantalla) y exportar un `zpub` watch-only. Ver [Firmante Bitcoin](/es/firmware/bitcoin-signer). |
| **Chrome: On/Off** | Habilita el comando host `FIND` que usa la [extensión de navegador](/es/getting-started/browser-extension) para saltar la búsqueda del dispositivo. Por defecto **On**; en Off el firmware ignora el comando. Guardado en EEPROM (`0x000D`). |
El flujo de export/import muestra un prompt de autorización antes de transferir cualquier dato:
```mermaid theme={null}
sequenceDiagram
participant Host
participant Device as Dispositivo
Host->>Device: "EXPORT" o "IMPORT"
Device-->>Host: "AWAIT_AUTH EXPORT"
Device->>Device: Muestra "Hold center to authorize"
alt El usuario mantiene Centro
Device->>Host: Stream de datos CSV (export) o recepción de datos CSV (import)
Device-->>Device: "Export/Import complete"
else El usuario suelta
Device-->>Device: Cancela, vuelve al menú
end
```
Export envía **credenciales en texto plano** por USB serie. Solo realiza esto en un ordenador de confianza.
***
### ⚙️ Settings
| Ítem | Acción |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Rotate Screen** | Voltea la pantalla 180° para uso a izquierda/derecha. También invierte los controles táctiles. Guardado en EEPROM. |
| **Keyboard: XX-XX** | Cicla por los 9 layouts de teclado (EN-US → DA-DK → DE-DE → ES-ES → FR-FR → HU-HU → IT-IT → PT-PT → SV-SE → EN-US). Guardado en EEPROM. |
| **UI Language** | Cicla el idioma de la interfaz en pantalla (Inglés ↔ Español). Guardado en EEPROM. |
| **Reader: On/Off** | Alterna el [modo lector de pantalla](/es/firmware/screen-reader) HID persistente — teclea la pantalla por USB para que una unidad con el display muerto siga siendo usable. Guardado en EEPROM. |
| **Pwd: XX** | Cicla el formato que usa `Rand` al generar una contraseña: `Symbols` → `Numeric` → `a-z 0-9` → `Aa-z 0-9` → `Words` → `Words+Num`. Todos los formatos salen del TRNG del ATECC608A y topan en 16 caracteres. Guardado en EEPROM (`0x0004`). Ver [Editar una credencial](/es/getting-started/edit-credential). |
***
### ⏱️ TOTP
El TOTP se accede desde la vista de credenciales, no desde el menú principal. Al ver una credencial, haz scroll **Abajo más allá de Password** hasta el campo **2FA**:
* Si no existe secreto TOTP para ese slot → muestra "No TOTP secret" durante 2 segundos.
* Si la hora no está sincronizada → muestra "Time not set — Request host time" y envía `REQTIME` por serie.
* Si está listo → muestra un **código de 6 dígitos** con cuenta atrás de 30 segundos. Se refresca automáticamente cada periodo. Toca cualquier pad para volver.
***
### ⚠️ Danger Zone
Toda acción en esta sección muestra una **página de confirmación** que requiere pulsar Centro para proceder o Izquierda para cancelar:
| Ítem | Efecto | ¿Reversible? |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------- |
| **Factory Reset** | Ejecuta `eraseAll()` (cuenta atrás de 3 segundos, blancos cifrados en los 61 slots + limpia metadatos TOTP) y resetea el flag de aprovisionamiento a `0x00`, así el próximo arranque inicia el asistente de setup. | ❌ No |
| **Bootloader Mode** | Pone la palabra mágica de doble reset (`0xF01669EF` en `0x20007FFC`) y luego ejecuta `NVIC_SystemReset()`. El dispositivo se reinicia en el bootloader USB DFU para flashear firmware. | ✅ Sí (reflasheo) |
***
### ℹ️ Info
Submenú de solo lectura mostrando:
* **SW: x.x.x** — versión del firmware desde `zerokeyInfo::getSoftwareVersion()`
* **SN: XXXXXXXX** — serial hardware desde los registros de ID único del SAMD21
***
## Asistente de setup
El asistente de setup corre en el primer arranque (o tras reset de fábrica). Consta de **10 páginas internas** repartidas en **9 pasos visibles**:
```mermaid theme={null}
graph LR
W1["1. Bienvenida"] --> W2["2. Navegación"]
W2 --> W3["3. Rotar pantalla"]
W3 --> W4["4. Layout de teclado"]
W4 --> W5["5. Crear PIN"]
W5 --> W6["6. Confirmar PIN"]
W6 --> W7["7. Info desbloqueo"]
W7 --> W8["8. Info cuentas"]
W8 --> W9["9. Generar IV"]
W9 --> W10["10. ¡Listo!"]
style W5 fill:#fef3c7,stroke:#d97706,color:#000
style W6 fill:#fef3c7,stroke:#d97706,color:#000
style W9 fill:#fee2e2,stroke:#dc2626,color:#000
```
Las páginas con más de 4 líneas de texto son **scrollables verticalmente** usando Arriba/Abajo. Aparece un thumb de scroll en el borde derecho.
Cada página del asistente soporta:
* **Derecha** → avanza a la siguiente página
* **Izquierda** → vuelve a la página anterior
* **Centro** → acción (alternar orientación, cambiar layout, iniciar entrada de PIN)
* **Arriba/Abajo** → scroll del contenido
***
## Filosofía de diseño
El sistema de menú es intencionadamente minimalista:
* Sin submenús profundos — cada opción está a **dos toques** del menú principal.
* Todas las acciones destructivas requieren confirmación explícita en una página dedicada.
* El layout y los gestos se mantienen consistentes entre versiones del firmware.
* Los ítems del menú actualizan dinámicamente sus etiquetas (p. ej., layout de teclado muestra la selección actual).
ZeroKeyUSB no requiere drivers ni instalación de software.\
Se reconoce como un teclado USB estándar en cualquier sistema operativo.
# Modo lector / sin pantalla (técnico / auditoría)
Source: https://docs.zerokeyusb.com/es/firmware/screen-reader
Cómo está implementado el modo lector por HID ("pantalla rota") — el gesto de activación, la mecánica de la línea-eco, qué emite exactamente y qué no emite nunca — para que puedas auditar su exposición de datos.
Esta página documenta la **implementación** del lector por HID para que su
exposición de datos pueda auditarse. Para la guía de recuperación de usuario, ve
a [Modo sin pantalla](/es/getting-started/recovery-no-screen).
El modo hace que el dispositivo **teclee el estado actual de la pantalla por USB
HID (teclado)**, línea a línea, para que una unidad con el OLED muerto pueda
igualmente desbloquearse y operarse a ciegas. La implementación está en
**`ZerokeyOS/zerokey-utils.cpp`** (emisor), **`zerokey-io.cpp`** (gesto) y
**`zerokey-menu.cpp`** (conmutador persistente).
## Estado y activación
* **Flag en tiempo de ejecución** `screenReaderMode` (`zerokey-globals.cpp`, por
defecto `false`).
* **Flag persistente** en EEPROM en `EEPROM_SCREEN_READER_ADDR = 0x0003`
(`1` = arrancar directamente en modo lector). `initScreenReaderMode()` lo lee y
aplica en el arranque; **Ajustes → "Reader: On/Off"**
(`setScreenReaderPersistent`) lo escribe y voltea el flag de ejecución para que
el cambio surta efecto al instante.
* **Gesto de sesión:** mantén **Centro 10 s** (`SCREEN_READER_HOLD_MS = 10000`)
**con la pantalla de PIN visible** (`PIN_SCREEN`/`EDITPIN`). Una animación de
borde se rellena durante los 10 s completos y voltea `screenReaderMode`
justo al completarse.
El gesto se restringe deliberadamente a la **pantalla de PIN** para que sea
accesible **antes de desbloquear** — el objetivo es rescatar un dispositivo cuya
pantalla murió. Soltar antes de los 10 s no hace nada.
## Qué emite (mecánica de la línea-eco)
El dispositivo mantiene **una línea lógica** en el host. `announceCurrentScreen()`:
* cachea el texto en `g_lastEcho` y la longitud en `g_hidEchoLen`;
* ante un cambio de estado, **borra con retroceso** el eco anterior y teclea el
nuevo (los estados sin cambios **no** se reteclean → sin parpadeo);
* pone `g_suppressNextAnnounce` para saltarse el anuncio automático que si no se
duplicaría justo después de teclear una credencial real.
La salida HID usa una cadencia fija (`hidTapKey`): pulsar, `12 ms`, `releaseAll`,
`18 ms`; `\n`→`KEY_RETURN`, `\t`→`KEY_TAB`.
| Pantalla | Línea emitida |
| ---------------------- | -------------------------------------------------------------------------------------------- |
| Entrada de PIN | `PIN 125 >7` — dígitos introducidos, luego el dígito seleccionado (`>OK` = tic de confirmar) |
| Credencial (sitio) | `3: google.com` |
| Campos de credencial | `3: user`, `3: password`, `3: 2FA` |
| Menú | `Menu: ` |
| Página de confirmación | ` Hold=OK Left=No` |
**Entrada de PIN a ciegas:** Arriba/Abajo cambian el dígito seleccionado (mira
`>n`), Derecha lo añade, Izquierda borra, luego selecciona `>OK` y Derecha/Centro
para desbloquear — la línea tecleada refleja lo que mostraría el OLED.
**Revelar un valor:** pulsar **Centro** en una credencial borra el eco
(`typeCredential` retrocede `g_hidEchoLen`) y teclea el **valor real**
(usuario/contraseña) exactamente como lo haría el tecleo HID normal — así puedes
leer una contraseña a ciegas en un campo de texto enfocado.
## Frontera de exposición de datos (lo relevante para auditar)
El lector teclea **dígitos del PIN** y, con un Centro explícito, **contraseñas**
en el campo que esté enfocado. Úsalo solo en un campo de texto **privado** que
controles, y bórralo después.
Dos propiedades importan para una auditoría:
1. **Sin canal de salida nuevo.** El lector solo emite (a) la única línea de
estado que mostraría el OLED, o (b) — con un Centro explícito — el *mismo*
valor que `typeCredential` ya teclea en uso normal. No expone nada que un
usuario con vista no pudiera obtener ya por HID.
2. **La semilla Bitcoin nunca se teclea.** El visor de semilla (`btcDisplaySeed`)
dibuja solo en el OLED y **no tiene ruta HID**; el lector no tiene ninguna
rama que emita palabras de semilla ni entropía. Una unidad con la pantalla
rota por tanto **sigue sin poder** filtrar la semilla Bitcoin por USB. (Ver
[Firmante Bitcoin](/es/firmware/bitcoin-signer).)
## Cómo auditarlo tú mismo
Grepea `announceCurrentScreen` y `hidTypeText`/`hidTapKey` en
`zerokey-utils.cpp`. Confirma que cada punto de llamada corresponde a una
pantalla que el OLED ya muestra, y que ninguno lee la página de cartera
`ZKBW` ni las palabras de semilla.
En `zerokey-io.cpp`, verifica que el conmutador de 10 s está condicionado a
`programPosition == PIN_SCREEN || EDITPIN` y `SCREEN_READER_HOLD_MS`.
Verifica que el flag persistente es un único byte en `EEPROM_SCREEN_READER_ADDR
(0x0003)` y que solo alterna el mismo comportamiento de ejecución.
## Mapa de código
| Aspecto | Dónde |
| ------------------------------------------------- | ----------------------------------- |
| Emisor, línea-eco, revelado de credencial | `ZerokeyOS/zerokey-utils.cpp` |
| Gesto de activación de 10 s | `ZerokeyOS/zerokey-io.cpp` |
| Conmutador persistente "Reader: On/Off" | `ZerokeyOS/zerokey-menu.cpp` |
| Flag de ejecución y persistente, dirección EEPROM | `ZerokeyOS/zerokey-globals.{h,cpp}` |
Activa y usa el modo con la pantalla muerta.
Por qué la semilla nunca se expone, ni siquiera aquí.
# Cifrado AES-128
Source: https://docs.zerokeyusb.com/es/firmware/security/aes-128-encryption
Cómo ZeroKeyUSB encadena el motor AES hardware del ATECC608A en modo CBC para proteger cada credencial almacenada en el dispositivo.
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.
| Propiedad | Detalle |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Origen** | TRNG hardware del ATECC608A (comando `random()`, modo 0x00 — refresca la semilla DRBG antes de la salida) |
| **Tamaño** | 16 bytes (128 bits) |
| **Almacenamiento** | Slot 8 del ATECC608A (`IsSecret=1`, `KeyType=6`/AES, `WriteConfig=Never`) |
| **Visibilidad** | El chip nunca expone los contenidos del slot vía I²C una vez configurado así |
| **Momento de generación** | Único disparo, durante el primer arranque del firmware nuevo en un chip virgen, dentro de `provisionAesAndLock()` |
| **Mutabilidad** | Ninguna tras bloquear la zona de datos. La clave persiste durante la vida del dispositivo. |
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:
```mermaid theme={null}
flowchart LR
subgraph Cifrado
IV["IV (16B) EEPROM 0x0010"] --> XOR0["⊕"]
P0["Bloque plano 0"] --> XOR0
XOR0 --> AES0["ATECC608A AES ECB encrypt clave slot 8"]
AES0 --> C0["Bloque cifrado 0"]
C0 --> XOR1["⊕"]
P1["Bloque plano 1"] --> XOR1
XOR1 --> AES1["ATECC608A AES ECB encrypt clave slot 8"]
AES1 --> C1["Bloque cifrado 1"]
end
style IV fill:#fef3c7,stroke:#d97706,color:#000
style AES0 fill:#fef3c7,stroke:#d97706,color:#000
style AES1 fill:#fef3c7,stroke:#d97706,color:#000
style C0 fill:#dcfce7,stroke:#16a34a,color:#000
style C1 fill:#dcfce7,stroke:#16a34a,color:#000
```
### Cifrado
```
prev = IV
para cada bloque de 16 bytes b (0, 1):
x = plain[b] XOR prev
cipher[b] = ATECC608A.aesEncryptBlock(slot=8, mode=0x00, in=x)
prev = cipher[b]
```
### Descifrado
```
prev = IV
para cada bloque de 16 bytes b (0, 1):
dec = ATECC608A.aesDecryptBlock(slot=8, mode=0x01, in=cipher[b])
plain[b] = dec XOR prev
prev = cipher[b]
```
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:
| Campo | Valor | Propósito |
| -------------- | -------------------------------------------------- | ---------------------------------------------- |
| Token de wake | bajar SDA 60 µs | Sacar al chip de sleep |
| Opcode | `0x51` (`AES`) | Identificador de comando |
| Param1 (Mode) | `0x00` = encrypt block 0, `0x01` = decrypt block 0 | `bit 0` operación, bits 6–7 índice de sub-key |
| Param2 (KeyID) | `0x0008` | Slot 8 |
| Datos | 16 bytes | Plaintext (encrypt) o ciphertext (decrypt) |
| CRC | 2 bytes | CRC-16 personalizado (poli `0x8005`, init `0`) |
| Respuesta | 16 bytes + status | El bloque cifrado o descifrado |
| Token de sleep | `0x01` | Devolver el chip a estado de bajo consumo |
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 RC SS` más una segunda línea `LC= LV= KT=` que muestra el estado de lock y key-type del chip en el momento del fallo:
| Código | Significado |
| -------- | ------------------------------------------------------------------------------------------ |
| `AES E1` | `silentEraseAll()` no pudo cifrar un slot en blanco |
| `AES E2` | `eraseAll()` no pudo cifrar un slot en blanco |
| `AES E3` | `lock()` no pudo cifrar un campo de credencial (`f0`/`f1`/`f2` = sitio/usuario/contraseña) |
| `AES E4` | `unlock()` no pudo descifrar un campo de credencial |
`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
| Consideración | Estado |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Entropía de clave** | 128 bits del TRNG hardware del chip — no es susceptible a fuerza bruta |
| **PIN ≠ clave** | Cambiar u olvidar el PIN no afecta la clave AES ni el ciphertext existente |
| **Clave en reposo** | Vive dentro del slot 8 del ATECC608A con `IsSecret=1`. El slot no es legible vía comando `Read` tras bloquear la zona de datos. |
| **Clave en tránsito** | Nunca cruza el bus I²C. El MCU envía bloques de plaintext / ciphertext; el chip usa su copia interna de la clave. |
| **Ataque físico vía I²C** | Un atacante que exponga I²C puede repetir llamadas AES pero no puede extraer la clave. Aun así podría observar los bloques de plaintext que el MCU envía al chip — el encapsulado físico sigue siendo esencial. |
| **Reset de fábrica** | `eraseAll()` sobrescribe todas las páginas de credenciales con blancos cifrados. La propia clave AES es permanente (slot 8 con lock Never). |
| **Sin custodia de clave** | No hay copia de seguridad de la clave maestra AES en ningún sitio. Fallo del chip = pérdida permanente de todas las credenciales. Mantén una copia de seguridad exportada. |
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.
# Sistema de seguridad
Source: https://docs.zerokeyusb.com/es/firmware/security/index
Cómo ZeroKeyUSB protege tus credenciales a través de un elemento seguro hardware, cifrado AES-128 CBC (clave en chip, bloques ECB en hardware) y operación exclusivamente offline.
## Offline por diseño
ZeroKeyUSB no depende de Internet, almacenamiento en la nube ni apps acompañantes.
Todo — desde la generación de números aleatorios hasta la verificación del PIN — ocurre **dentro del dispositivo**, alimentado directamente por USB.
Tus contraseñas **nunca salen del hardware** y **no se pueden acceder remotamente**, ni siquiera por el fabricante.
***
## Dos chips cooperando
La seguridad está dividida entre dos piezas de silicio para que ninguna por sí sola pueda filtrar la bóveda:
```mermaid theme={null}
graph LR
subgraph MCU["SAMD21E18A (MCU)"]
CBC["Encadenamiento CBC\n(XOR + dispatch de bloque)"]
SHA["SHA-256\nhash del PIN"]
USB["USB HID + CDC"]
end
subgraph SE["ATECC608A (Elemento seguro)"]
TRNG["TRNG hardware\ngeneración de clave + IV"]
AESHW["Motor AES-128 ECB\nhardware\n(clave en slot 8)"]
KEY["Clave AES (16B)\nslot 8, IsSecret=1"]
SER["Serial del chip\nsalt único de 9 bytes"]
end
subgraph MEM["EEPROM M24C64"]
IV["IV (16B)\n@ 0x0010"]
HASH["Hash del PIN (32B)\n@ 0x0048"]
CRED["61 slots de credenciales\ncifrados"]
end
TRNG -->|"genera al aprovisionar"| KEY
KEY -->|"interno, nunca sale del chip"| AESHW
TRNG -->|"genera"| IV
SER -->|"salt"| SHA
SHA -->|"guarda"| HASH
IV -->|"cargado a RAM"| CBC
CBC -->|"AES por bloque vía I²C"| AESHW
CBC -->|"lee/escribe"| CRED
style MCU fill:#dbeafe,stroke:#2563eb
style SE fill:#fef3c7,stroke:#d97706
style MEM fill:#dcfce7,stroke:#16a34a
```
| Chip | Función |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **SAMD21E18A (MCU)** | Ejecuta el firmware de aplicación, gestiona la lógica de encadenamiento CBC, drivea el OLED, USB HID, táctil y el bus I²C. El AES de bloque único se delega al elemento seguro. |
| **ATECC608A-MAHDA-T (elemento seguro)** | Genera números aleatorios verdaderos (TRNG), guarda un serial único de 9 bytes usado como salt del PIN, y — una vez aprovisionado — ejecuta cada bloque AES-128 ECB en hardware dedicado usando una clave guardada en el slot 8 que **nunca sale del chip**. |
El MCU y el ATECC608A comparten un bus I²C en la dirección `0x60`. El MCU no tiene una copia usable de la clave AES: envía bloques de plaintext de 16 bytes al chip y recibe 16 bytes de ciphertext de vuelta. El chip generó la clave por sí mismo al aprovisionarse usando su TRNG; la secuencia de bytes nunca cruzó el bus I²C.
> **Modelo criptográfico — Camino A (clave AES + motor AES en chip).**
> El firmware habilita el comando AES hardware, configura el slot 8 como contenedor de clave AES (`IsSecret=1`, `KeyType=6`), escribe una clave de 16 bytes generada por el TRNG, y bloquea tanto la zona Config como Data. A partir de ahí, el cifrado y descifrado son llamadas ECB de bloque único al chip, encadenadas en el MCU en CBC.
***
## Arquitectura de cifrado
Todos los datos sensibles se almacenan en la **EEPROM externa M24C64-WMN6TP**, cifrados con **AES-128 en modo CBC**. Cada bloque ECB lo computa el motor AES hardware del ATECC608A; el MCU solo gestiona el encadenamiento XOR del CBC.
| Elemento | Origen / ubicación |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cifrador** | AES-128 CBC — ECB de bloque único delegado al comando `AES` del ATECC608A (opcode `0x51`). El encadenamiento XOR por bloque se hace en el MCU alrededor de las llamadas al chip para que la clave nunca tenga que cargarse en SRAM del MCU. |
| **Clave maestra AES** | 16 bytes aleatorios producidos por el TRNG del ATECC608A al primer arranque y escritos al **slot 8** del chip. `IsSecret=1` significa que nunca se puede leer vía el bus I²C. |
| **IV (Vector de Inicialización)** | 16 bytes aleatorios producidos por el TRNG del ATECC608A al aprovisionar. Guardados en EEPROM en `0x0010–0x001F`. |
| **Vinculación con el PIN** | La clave maestra AES **no** se deriva del PIN. El PIN se verifica por separado mediante un hash guardado en EEPROM que gobierna el acceso a la rutina de unlock. |
| **Layout de credenciales** | Cada slot contiene hasta cuatro páginas EEPROM cifradas de 32 bytes: sitio (p0), usuario (p1), contraseña (p2), secreto TOTP / nota (p3). Cada página guarda **hasta 32 bytes** de plaintext (sitio/usuario/contraseña hasta 32 chars); cualquier cola sin usar se rellena con `0xFF`. |
### Detalle del encadenamiento CBC
`cbcEncrypt32` / `cbcDecrypt32` procesan cada credencial de 32 bytes en dos bloques de 16 bytes. Para cada bloque:
1. El bloque de plaintext se XOR con el ciphertext anterior (o con el IV del dispositivo para el primer bloque).
2. El resultado del XOR se envía al ATECC608A vía el comando AES (`mode=0x00` para encrypt, `0x01` para decrypt, clave del slot 8, key block 0). El chip devuelve los 16 bytes de ciphertext.
3. El ciphertext resultante se convierte en `prev` para el siguiente bloque.
El descifrado es simétrico: el chip devuelve plaintext, el MCU lo XOR con el ciphertext anterior para recuperar el bloque original.
**¿Por qué dividir el trabajo así?** El comando `AES` del ATECC608A solo expone ECB de bloque único. CBC es la *política de encadenamiento* añadida encima — implementarlo en el MCU mantiene cada byte de la clave dentro del elemento seguro mientras nos da los beneficios de difusión de CBC en los datos de credenciales.
***
## Mapa de seguridad de la EEPROM
| Dirección | Tamaño | Contenido |
| --------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `0x0000` | 1 B | Flag del asistente de configuración (`0x42` = hecho) |
| `0x0001` | 1 B | Modo de pantalla / orientación |
| `0x0002` | 1 B | Contador soft de intentos fallidos (backoff UX) |
| `0x0010–0x001F` | 16 B | Vector de inicialización AES-CBC (generado por TRNG) |
| `0x0020–0x0023` | 4 B | *Reservado* — umbral de intentos legacy del lockout por Counter0 ya eliminado. No se lee ni escribe. |
| `0x0024` | 1 B | Flag de aprovisionamiento (`0xA5` = aprovisionado) |
| `0x0028–0x0037` | 16 B | *Reservado* — slot legacy de la clave maestra AES del build con AES software. No se usa desde que la clave se movió al slot 8 del ATECC. |
| `0x003E` | 1 B | Selector de layout de teclado |
| `0x0040–0x0047` | 8 B | Último epoch TOTP (persistente entre apagados) |
| `0x0048–0x0067` | 32 B | Hash del PIN = SHA-256(pinArray\[16] ∥ chip\_serial\[9]) |
| `0x0068+` | 124 B | Metadatos TOTP (algoritmo + longitud del secreto, 2 B × 61 slots) |
| `0x0100+` | — | Páginas de credenciales (4 × 32 B × 61 slots = 7808 B máx.) |
> **Nota sobre `0x0028`.** Unidades antiguas (compiladas antes del cambio de AES) usaban esta región para guardar la clave maestra AES de 16 bytes en plaintext. Las unidades nuevas no la tocan; los bytes permanecen con lo que tuviera la EEPROM. Trata la dirección como reservada.
***
## El PIN maestro
El PIN autoriza un ciclo de unlock; **nunca se usa directamente como clave de cifrado**.
```mermaid theme={null}
flowchart TD
A["Usuario introduce PIN\n4-16 dígitos"] --> E["SHA-256(PIN ∥ serial)"]
E --> F{"¿Hash coincide\nEEPROM @ 0x0048?"}
F -->|"Sí (tiempo constante)"| G["✅ Unlock\nLimpia contador de fallos"]
F -->|"No"| H["❌ Denegar\nIncrementar contador de fallos\nDelay exponencial (persiste)"]
style G fill:#bbf7d0,stroke:#16a34a,color:#000
style H fill:#fef3c7,stroke:#d97706,color:#000
```
El flujo de verificación:
1. Al arrancar, antes de que la pantalla de PIN acepte entrada, el firmware reaplica el backoff acumulado para el contador de fallos guardado (`waitFromEeprom()`).
2. El usuario introduce hasta **16 dígitos** en los pads capacitivos.
3. `derivePinKey()` computa `SHA-256(pinArray[16] ∥ chip_serial[9])` para producir el hash de 32 bytes.
4. Lee el hash guardado de 32 bytes desde EEPROM (`0x0048`) y ejecuta una **comparación a tiempo constante** (`diff |= stored[i] ^ derived[i]`).
5. **En coincidencia:** el contador de fallos se limpia y el unlock procede.
6. **En no coincidencia:** el contador se incrementa y el dispositivo aplica un delay exponencial antes del siguiente intento — y de nuevo en el siguiente arranque.
El contador de backoff vive en EEPROM (`0x0002`) y se reaplica en cada encendido, así que un atacante no puede saltarse el retardo cortando la corriente. La bóveda **nunca se borra** por PINs incorrectos. Ten en cuenta que esto solo protege los intentos hechos *a través del dispositivo*: un atacante que lea el hash del PIN por el bus I²C puede crackearlo offline sin ningún retardo, por eso importan la encapsulación en resina (que bloquea el acceso al bus) y un PIN largo.
### Backoff exponencial — tabla de retardos
Guardado en EEPROM `0x0002`; reaplicado al arrancar; se resetea con un PIN correcto.
| Intentos fallidos | Tiempo de espera |
| ----------------- | ---------------------------------- |
| 1 | 5 s |
| 2 | 10 s |
| 3 | 20 s |
| 4 | 40 s |
| 5 | 80 s |
| … | dobla hasta **2 560 s (≈ 43 min)** |
Fórmula: `wait = BASE_SECONDS (5) × 2^(min(intentos,10)−1)`, con tope en `MAX_WAIT_SECONDS` (2 560).
### Sin bloqueo destructivo
Un diseño anterior usaba el `Counter0` monotónico del ATECC608A para borrar la bóveda tras 50 PINs incorrectos. Ese mecanismo se **eliminó**: `verifySignature()` ya no incrementa Counter0, ni lee ningún umbral, ni llama a `eraseAll()` en intentos fallidos. El backoff persistente de arriba es la única defensa automática contra fuerza bruta; `eraseAll()` solo corre en un reset de fábrica iniciado por el usuario.
***
## Mapa de slots del ATECC608A
Establecido por el propio dispositivo la primera vez que arranca, luego **bloqueado permanentemente** (las zonas Config y Data ambas cerradas irreversiblemente):
| Slot | Tamaño usado | Contenido | SlotConfig / KeyConfig |
| ----- | ------------ | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| **8** | 16 B | Clave maestra AES-128 — generada en el chip por el TRNG al primer arranque, usada por cada encrypt/decrypt de credencial | `IsSecret=1` / `WriteConfig=Never` / `KeyType=6 (AES)`. La clave no se puede leer por I²C, ni reescribir una vez bloqueada la zona de datos. |
| **9** | 32 B | `SHA-256(PIN_padded ∥ device_serial)` — la clave del PIN | `IsSecret=0` / `WriteConfig=Always` para que la app pueda reescribir el slot cuando el usuario cambie su PIN. Legible por I²C. |
### Secuencia de aprovisionamiento (solo primer arranque)
`zerokeyAtecc.provisionAesAndLock()` corre una vez, antes del asistente de setup:
1. **Lee** los bloques 0, 1 y 3 de la Config Zone para conocer los valores de fábrica actuales del chip.
2. **Establece el bit AES\_Enable** (byte 13, bit 0) usando una escritura de bloque de 32 bytes que preserva cada bit de fábrica que no pretendíamos cambiar. Re-lee y verifica que el bit tuvo efecto; aborta sin bloquear si no.
3. **Establece SlotConfig\[8]** — `IsSecret=1` (bit 7 del byte 36) y `WriteConfig=Never` (nibble alto del byte 37 = `0x4`), preservando el resto del byte. Re-lee y verifica.
4. **Establece KeyConfig\[8].KeyType = 6 (AES)** — bits 2..4 del byte 112, preservando cada otro bit. Re-lee y verifica.
5. **Bloquea la zona Config.** Irreversible.
6. **Genera** una clave aleatoria de 16 bytes vía el TRNG del chip y la escribe en el slot 8 en claro (aún permitido mientras la zona de datos esté abierta).
7. **Bloquea la zona Data.** Irreversible.
Cada escritura va seguida de una re-lectura. Si algún verify falla, la función devuelve un error numerado `PROV E` con el byte de status crudo del chip adjunto y **no procede a bloquear la zona**, de modo que un chip que se porte mal no pueda brickearse silenciosamente.
> **¿Por qué escrituras a nivel de bit?** Los chips MAHDA-T se entregan con varios bits "reservados" en el byte 13 (`AES_Enable`) configurados de fábrica. Una escritura ingenua que los borra es rechazada por el chip con un parse error (`SS=0x03`). El código de aprovisionamiento lee cada byte primero, hace OR-mask solo de los bits que necesita cambiar, y escribe el bloque de 32 bytes entero de vuelta.
> **Trade-off conocido (Slot 9 legible):** El slot 9 no está bloqueado como secreto porque el SKU MAHDA-T rechaza escrituras en claro a slots IsSecret 0–7. El hash del PIN vive ahí con `IsSecret=0`, así que un atacante con acceso físico I²C puede leer el hash de 32 bytes e intentar fuerza bruta offline SHA-256(PIN∥serial). El backoff persistente limita los intentos online pero no hace nada contra el cracking offline — ahí lo que se interpone es la encapsulación en resina (que bloquea el acceso al bus). Los PINs cortos son vulnerables a este ataque — usa los 16 dígitos máximos.
***
## Vector de inicialización
El IV se genera **una vez** durante el aprovisionamiento por el TRNG del ATECC608A y se guarda en EEPROM en `0x0010`. Dos comprobaciones lo protegen:
* Una lectura que devuelva todo `0x00` o todo `0xFF` se trata como sin inicializar y dispara regeneración desde el TRNG.
* Si la lectura de EEPROM falla en el momento del unlock, el firmware intenta regenerar desde el TRNG y re-guarda el IV.
**IV único por dispositivo:** todas las páginas de credenciales se encadenan contra el mismo IV. Esto mantiene el layout simple y auditable. El modelo de amenaza se apoya en la calidad del TRNG y en el secreto de la clave AES, no en nonces por registro.
**Consecuencia de la regeneración:** si el IV se pierde o se regenera sin re-cifrar las credenciales, los slots existentes descifrarán a basura (el ciphertext se produjo bajo el IV antiguo). La rutina de auto-curación del firmware (`silentEraseAll`) se llama automáticamente en el primer unlock si el slot 0 página 0 sigue en `0xFF` (por defecto de EEPROM), y se puede llamar de nuevo manualmente vía `generateAndStoreIV()`.
***
## Inicialización con auto-curación
En el primer unlock tras el aprovisionamiento, `ZerokeySecurity::unlock()` comprueba si el slot 0 página 0 de credenciales sigue en el valor por defecto de fábrica de EEPROM (`0xFF` en los 32 bytes). Si es así, llama a `silentEraseAll()`:
1. Carga el IV del dispositivo desde EEPROM.
2. Para cada uno de los 61 slots × 4 páginas: cifra un blanco de 32 bytes `0xFF` bajo AES-128 CBC y lo escribe en EEPROM.
3. Limpia los metadatos TOTP de cada slot.
Esto garantiza que las unidades nuevas siempre tengan entradas en blanco consistentes y correctamente cifradas antes de que se escriba ninguna credencial.
***
## Segmentación de datos
Cada slot de credencial ocupa 4 páginas EEPROM consecutivas (128 bytes en total):
| Página | Contenido (plaintext, hasta 32 B; cola sin usar `0xFF`) | Bytes EEPROM |
| ------ | ------------------------------------------------------- | --------------- |
| 0 | Sitio / dominio | 32 B ciphertext |
| 1 | Usuario | 32 B ciphertext |
| 2 | Contraseña | 32 B ciphertext |
| 3 | Secreto TOTP | 32 B ciphertext |
Dividir los campos mantiene patrones reconocibles de plaintext fuera del stream de ciphertext y limita el radio de explosión de una página EEPROM corrupta. Los bytes de padding son `0xFF`; los espacios finales `0x20` se reemplazan con `0xFF` antes del cifrado para evitar fugas de patrones.
***
## Protección contra manipulación
* El PCB está **encapsulado en resina epoxy**; abrir el dispositivo destruye la placa y las conexiones de los chips.
* **Sin interfaces wireless** (sin Wi-Fi, sin Bluetooth, sin NFC).
* La **región del bootloader está BOOTPROT-locked** en fuses hardware (`BOOTPROT = 7`, protegiendo los primeros 16 KB) — el firmware de aplicación no puede reescribir ni reubicar el bootloader.
* El bootloader solo saltará a **firmware firmado con ECDSA P-256**; una imagen sin firmar o manipulada cae en modo de recuperación USB-CDC en vez de ejecutarse.
* Todas las credenciales descifradas viven en **buffers temporales de RAM** (`currentSite`, `currentUser`, `currentPass`) que se rellenan al unlock y se sobrescriben en el siguiente lock o ciclo de alimentación.
* El **pin de Write Protect** (`EEPROM_WP_PIN = PA01`) puede ponerse a alto por firmware para bloquear escrituras EEPROM por hardware.
***
## Backup y restore
Las credenciales se pueden exportar e importar por la interfaz serie USB CDC:
* **Export (`backupAllCredentials`):** descifra los 61 slots dentro del dispositivo y los envía como líneas plaintext separadas por comas por SerialUSB. El host recibe las credenciales en claro — **asegúrate de que la conexión USB es de confianza**.
* **Import (`loadAllbackupCredentials`):** recibe registros desde el host, los re-cifra bajo el IV/clave maestra actual del dispositivo y los escribe en EEPROM. Los secretos TOTP se parsean y guardan en la página 3 de cada slot.
> **Nota de seguridad:** el backup transmite credenciales descifradas en plaintext por USB. Solo realiza backup/restore en un host de confianza, air-gapped.
***
## Limitaciones conocidas y compromisos
| Limitación | Impacto | Mitigación |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| La clave AES no se puede regenerar tras el aprovisionamiento | Si el chip alguna vez falla, las credenciales cifradas con esa clave son irrecuperables | `WriteConfig=Never` es el precio de `IsSecret=1` más la zona de datos bloqueada; elige el compromiso de máxima seguridad y acéptalo. Mantén una copia de seguridad exportada. |
| Hash del PIN del slot 9 legible por I²C | Fuerza bruta offline SHA-256 si I²C es accesible | Los PINs cortos (\< 6 dígitos) son vulnerables; usa PINs de máxima longitud |
| IV único por dispositivo | Mismo IV para todos los slots; sin nonces por registro | Entropía del IV desde el TRNG del ATECC; perder el IV requiere re-inicializar |
| Comparación de PIN en software (no CheckMac); hash legible por I²C | Fuerza bruta offline SHA-256 si se alcanza el bus I²C; timing side-channel | Comparación a tiempo constante; backoff persistente en el dispositivo; la encapsulación en resina bloquea el acceso al bus; usa un PIN largo |
| El backup es plaintext por USB | Un host físico puede capturar credenciales | Documentado claramente; usar solo en máquinas de confianza |
| Cada bloque AES es un round-trip al chip por I²C | Más lento que AES software — las credenciales descifran en \~30 ms en vez de \~3 ms | Aceptable para un gestor de contraseñas de mano; el chip es el límite de seguridad |
***
## Transparencia, no dependencia
El firmware de ZeroKeyUSB es totalmente open source y está disponible para **auditoría y verificación** públicas. Cualquiera puede revisar:
* Cómo se drivea el ATECC608A, incluyendo la rutina de aprovisionamiento bit-level que habilita el motor AES del chip y bloquea ambas zonas (`zerokey-atecc.cpp`).
* Cómo el encadenamiento CBC envuelve el comando AES de bloque único del chip (`zerokey-security.cpp`).
* Cómo se genera, valida y refresca el IV (`zerokey-security.cpp::generateAndStoreIV`).
* Cómo el bootloader hashea y verifica la aplicación (`bootloader/src/main.c`).
**No hay mecanismos de actualización remota**: reflashear requiere acceso físico vía pogo pins SWD o el bootloader local USB-CDC, y cualquier nueva imagen debe estar firmada por la clave ECDSA offline.
***
## Profundizar
Cómo el motor AES hardware del ATECC608A cifra cada bloque de credencial, con encadenamiento CBC envolviéndolo en el MCU.
El backoff persistente, el hash SHA-256 guardado en EEPROM y la comparación a tiempo constante que gobierna el unlock.
Cómo el TRNG del ATECC608A siembra el IV global del dispositivo y cómo se gestiona la regeneración.
ZeroKeyUSB combina un MCU con un elemento seguro endurecido. El chip proporciona la *entropía* (TRNG), la *identidad* (serial del chip usado como salt del PIN), **y** el *cifrador* en sí — cada bloque AES se computa dentro del elemento seguro usando una clave que el MCU nunca ha visto. El papel del MCU es encadenar esos bloques en CBC, manejar la UI y transportar plaintext / ciphertext hacia y desde la EEPROM.
# Generación del vector de inicialización
Source: https://docs.zerokeyusb.com/es/firmware/security/iv-generation
Cómo ZeroKeyUSB crea el IV de AES-CBC usando el TRNG del ATECC608A, lo valida y gestiona la regeneración.
El vector de inicialización (IV) garantiza que bloques de plaintext idénticos produzcan ciphertext distinto bajo AES-128 CBC. ZeroKeyUSB genera el IV **una vez al aprovisionar** usando el TRNG hardware del ATECC608A, lo guarda en EEPROM y lo valida en cada unlock.
***
## Procedimiento de generación
Llamado como parte de `storeSignature()` (la rutina de configuración del PIN):
1. `generateAndStoreIV()` llama a `fillIVFromAtecc(iv)`.
2. `fillIVFromAtecc()` llama a `zerokeyAtecc.random(buf)` — el comando `RANDOM` del ATECC608A con modo `0x00` (actualiza la semilla DRBG interna antes de generar).
3. Los primeros **16 bytes** de la salida TRNG de 32 bytes se copian a `iv[16]`.
4. `ivIsValid()` comprueba el resultado: rechaza todo `0x00` o todo `0xFF` (estadísticamente imposible con un TRNG en funcionamiento, pero protege contra fallos del chip).
5. El IV se escribe en EEPROM en `0x0010–0x001F` vía `eepromWriteRaw()`.
6. Tras guardar el IV, se llama a `eraseAll()` para resetear todas las páginas de credenciales a blancos cifrados bajo el nuevo IV.
El proceso entero corre en el dispositivo sin intervención del host.
***
## Layout de EEPROM
| Dirección | Contenido |
| --------- | -------------- |
| `0x0010` | Byte 0 del IV |
| `0x0011` | Byte 1 del IV |
| … | … |
| `0x001F` | Byte 15 del IV |
***
## Validación al unlock
`loadIVfromEEPROM()` se llama antes de cada operación de cifrado o descifrado:
1. Lee 16 bytes desde `0x0010`.
2. Si la lectura I²C falla → intenta regenerar desde TRNG y reescribir en EEPROM.
3. Si `ivIsValid()` devuelve false (todo-cero o todo-FF) → regenera desde el TRNG del ATECC y guarda.
4. Si la regeneración también falla → devuelve `false`; el llamador muestra una pantalla de error.
***
## Disparadores de regeneración
El IV se regenera automáticamente cuando:
* La lectura de EEPROM falla (error I²C).
* El valor guardado es todo `0x00` o todo `0xFF` (EEPROM en blanco/corrupta).
* El usuario genera un PIN nuevo (el flujo completo de `storeSignature()` regenera el IV).
**Consecuencia de la regeneración:** todas las páginas de credenciales fueron cifradas bajo el IV anterior. Regenerar el IV sin también reescribir las páginas de ciphertext hace ilegibles las credenciales existentes. La función `generateAndStoreIV()` por lo tanto llama a `eraseAll()` inmediatamente después de guardar el nuevo IV para llevar la EEPROM a un estado consistente (blancos cifrados).
***
## ¿Por qué un único IV global por dispositivo?
* Mantiene el layout simple, determinista y totalmente auditable.
* El modo CBC con un IV fijo a través de todos los slots no debilita la confidencialidad mientras la clave AES sea secreta y el IV en sí se haya generado aleatoriamente — la clave varía por dispositivo.
* IVs por registro requerirían 16 bytes adicionales por slot de credencial y complicarían significativamente el layout de EEPROM.
* La garantía primaria de confidencialidad viene de la clave maestra AES de 128 bits generada aleatoriamente, no de la unicidad del IV entre registros.
El IV nunca sale del dispositivo. El ciphertext extraído de un ZeroKeyUSB no se puede descifrar con otra unidad incluso si se conocen el PIN y el serial del dispositivo, porque la clave maestra AES es única por dispositivo y vive dentro del ATECC608A — no hay forma de copiarla y usarla en otro sitio.
***
## Detección de manipulación
* `ivIsValid()` rechaza valores obviamente corruptos (todo-cero, todo-FF).
* Si el IV se voltea bit a bit en la EEPROM, el siguiente descifrado AES-CBC producirá basura para el primer bloque de cada slot (el IV solo afecta la entrada XOR para el bloque 0; los bloques posteriores son auto-sincronizantes en el descifrado CBC).
* No hay CRC ni MAC guardado junto con el IV en la implementación actual. Un atacante que pueda escribir bytes arbitrarios en la dirección EEPROM `0x0010` puede forzar regeneración del IV (y por lo tanto pérdida de datos) corrompiendo esos bytes.
***
## Propiedades del TRNG del ATECC608A
* El comando `RANDOM` con modo `0x00` actualiza la semilla DRBG interna del chip desde entropía hardware antes de devolver 32 bytes aleatorios.
* El DRBG está diseñado a requisitos FIPS 140-2 con una vida máxima de semilla antes de re-sembrar forzosamente.
* La salida se valida localmente (`ivIsValid`) para capturar fallos patológicos.
# Verificación del PIN
Source: https://docs.zerokeyusb.com/es/firmware/security/pin-verification
Flujo de unlock, comparación de hash SHA-256 y el backoff exponencial persistente que limita el ritmo del PIN maestro.
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 **backoff exponencial persistente** que se vuelve a aplicar en cada arranque, haciendo la fuerza bruta impracticable sin destruir jamás los datos guardados.
***
## Cómo se guarda el PIN
El PIN **nunca se guarda en texto plano**. Al configurar el PIN (`storeSignature()`):
```mermaid theme={null}
flowchart LR
PIN["Dígitos del PIN pinArray[16]"] --> CONCAT["Concatenar"]
SERIAL["Serial del chip 9 bytes del ATECC"] --> CONCAT
CONCAT --> SHA["SHA-256"]
SHA --> HASH["Hash de 32 bytes"]
HASH --> EEP["EEPROM @ 0x0048"]
HASH --> SLOT["Slot 9 ATECC (para futuro CheckMac)"]
style SHA fill:#dbeafe,stroke:#2563eb,color:#000
style EEP fill:#dcfce7,stroke:#16a34a,color:#000
style SLOT fill:#fef3c7,stroke:#d97706,color:#000
```
1. Los dígitos del PIN del usuario se leen desde `pinArray[16]` (cada byte contiene un valor de dígito 0–9).
2. `derivePinKey()` computa: `SHA-256(pinArray[16] ∥ chip_serial[9])`.
* `chip_serial` es el serial único de 9 bytes leído desde la Config Zone del ATECC608A.
3. El hash resultante de 32 bytes se escribe en EEPROM en `0x0048–0x0067`.
4. El mismo hash de 32 bytes también se escribe en el **slot 9 del ATECC** (para uso potencial futuro con CheckMac).
El serial del chip actúa como salt hardware: el mismo PIN numérico en otro dispositivo produce un hash de 32 bytes completamente distinto.
***
## Secuencia de unlock
Cada intento de unlock ejecuta los siguientes pasos en `verifySignature()`:
```
Al arrancar, antes de que la pantalla de PIN acepte ninguna entrada:
waitFromEeprom() // reaplica el backoff acumulado para el contador de fallos guardado
Luego cada intento de unlock ejecuta verifySignature():
1. derivePinKey(pinArray, derived):
serial = ATECC608A.readSerial()
derived = SHA-256(pinArray[16] || serial[9])
2. Leer hash guardado desde EEPROM [0x0048] → stored[32]
3. diff = 0; for i in 0..31: diff |= stored[i] ^ derived[i] // tiempo constante
4. Si diff == 0:
writeFailedAttemptsCounter(0) // limpia el backoff
→ ACCESO CONCEDIDO
5. Si no:
incrementFailedAttemptsCounter()
waitFromEeprom() // backoff exponencial
→ ACCESO DENEGADO
```
En esta ruta **no hay incremento de `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.
| Evento | Contador de fallos (EEPROM `0x0002`) |
| --------------------- | -------------------------------------------------------- |
| PIN incorrecto | +1, y luego aplica el retardo de backoff |
| PIN correcto | se resetea a 0 |
| Ciclo de alimentación | el retardo del contador guardado se reaplica al arrancar |
`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 (EEPROM `0x0048` y slot 9 del ATECC con `IsSecret=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 EEPROM `0x0002` y reaplicado al arrancar; se resetea solo con 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) |
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()`:
1. Cuenta atrás de 3 segundos en pantalla (permite aborto seguro).
2. `derivePinKey(pinArray, derived)` computa el nuevo hash.
3. El nuevo hash de 32 bytes se escribe en el slot 9 del ATECC.
4. El nuevo hash de 32 bytes se escribe en EEPROM `0x0048`.
5. Se limpia el contador de intentos fallidos (EEPROM `0x0002`).
6. 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.
7. El IV se carga o se genera.
8. Se escribe el flag de config (`0x42`).
9. 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:
1. Muestra una cuenta atrás de 3 segundos.
2. Carga el IV del dispositivo.
3. Sobrescribe los 61 slots de credenciales × 4 páginas con blancos cifrados.
4. Limpia los metadatos TOTP.
Tras el reset el dispositivo se para con un error "LOCKED — reflash". Hay que usar el bootloader para flashear firmware nuevo y re-aprovisionar el dispositivo desde cero.
Las credenciales previamente almacenadas son irrecuperables a menos que tengas un backup en texto plano exportado antes del reset.
Elige un PIN que puedas recordar pero que otros no puedan adivinar. Un PIN de 4 dígitos o menos es vulnerable a ataques de diccionario SHA-256 offline si un adversario obtiene acceso I²C al dispositivo.
# Sincronización del epoch
Source: https://docs.zerokeyusb.com/es/firmware/totp/epoch-synchronization
Mantén el reloj interno de ZeroKeyUSB alineado para que los códigos TOTP sigan siendo válidos.
ZeroKeyUSB no contiene un reloj de tiempo real. En su lugar, rastrea el tiempo usando el contador de milisegundos del SAMD21 más un epoch Unix guardado. Para mantener la precisión, el dispositivo necesita ocasionalmente que el host le envíe la hora actual.
***
## Cuándo se requiere sincronización
* Primer arranque o tras un reset de fábrica
* Cuando el OLED muestra `REQTIME`
* Si los servicios de login reportan "código inválido" a pesar de introducirlo inmediatamente
* Tras periodos largos sin alimentación (varias semanas)
El firmware dispara una petición de sincronización una vez que la deriva supera ±90 segundos.
***
## Flujo de sincronización
1. Desbloquea ZeroKeyUSB.
2. Conéctate a la interfaz serie vía el web manager o la CLI.
3. El dispositivo envía `REQTIME` para señalar que necesita el epoch actual.
4. El host responde con `SETTIME `, por ejemplo `SETTIME 1706227200`.
5. ZeroKeyUSB guarda el valor en EEPROM (64 bits little-endian) y resetea sus contadores internos.
El intercambio entero es local; no se requiere conexión de red.
***
## Comprobar la deriva manualmente
Ejecuta el comando de status de la CLI:
```bash theme={null}
zerokeyusb-cli status
```
Busca una línea como `Clock drift: +18s`. Si el valor se acerca a ±60s, realiza una sincronización nueva.
***
## Resolución de problemas
| Síntoma | Solución |
| ----------------------------------------- | ------------------------------------------------------------------------------------------- |
| `REQTIME` persiste tras enviar `SETTIME` | Asegúrate de que el epoch está en segundos (no en milisegundos). |
| Los códigos están siempre desfasados 30 s | El reloj del host probablemente mal configurado; verifica la sincronización horaria del SO. |
| La CLI no puede abrir el puerto | Cierra otros programas serie (p. ej. Arduino IDE) que puedan estar conectados. |
La alineación correcta de tiempo garantiza que tus códigos TOTP coincidan con las expectativas del servidor.
# Módulo TOTP
Source: https://docs.zerokeyusb.com/es/firmware/totp/index
Genera códigos 2FA offline junto a tus contraseñas almacenadas.
TOTP significa **Time-based One-Time Password** (contraseña de un solo uso basada en tiempo) — el mismo estándar que usa Google Authenticator o Authy. ZeroKeyUSB calcula cada código de 6 dígitos **offline**, usando el secreto cifrado guardado en EEPROM y un valor de tiempo Unix mantenido localmente.
***
## Cómo funciona
* Los secretos se importan como **cadenas Base32** y se cifran con AES-128 antes de escribirse en EEPROM.
* El firmware mantiene un contador de epoch Unix de 8 bytes en texto plano (por simplicidad) y lo incrementa usando el temporizador de milisegundos del SAMD21.
* Cada 30 segundos el dispositivo computa `Truncate(HMAC-SHA1(secreto, epoch / 30))` y muestra el resultado en el OLED.
Como el algoritmo sigue el RFC 6238, los códigos coinciden con cualquier app autenticadora estándar manteniéndose aislados de Internet.
***
## Añadir un secreto TOTP
1. Desbloquea el dispositivo y abre la credencial que quieras proteger.
2. Usa el **web manager local** o la CLI para pegar la URI `otpauth://` proporcionada por el servicio.
3. La herramienta extrae el parámetro `secret=` y lo envía una vez por el canal serie seguro.
4. ZeroKeyUSB cifra el secreto, lo almacena en la página TOTP y marca el slot como 2FA-habilitado.
Los secretos nunca se muestran en texto plano una vez almacenados.
***
## Ver códigos
* Las credenciales con secreto TOTP muestran un prompt `2FA → Toca para ver` debajo de la contraseña.
* Tocando el pad central se revela el código de 6 dígitos actual y un anillo de cuenta atrás que se refresca cada segundo.
* La pantalla se auto-oculta tras 15 segundos de inactividad para mantener los códigos privados.
Si el dispositivo necesita el epoch actual, muestra `REQTIME` y espera a que el host envíe la hora una vez.
***
## Mantenlo preciso
Entiende cómo ZeroKeyUSB rastrea el tiempo Unix y cómo re-sincronizar cuando hay deriva.
Guía paso a paso para usar la utilidad basada en navegador para mantener el reloj TOTP alineado.
***
## Algoritmos soportados
| Algoritmo | Estado | Uso típico |
| ----------- | -------------- | -------------------------------------------------------------- |
| **SHA-1** | ✅ Implementado | La mayoría de servicios de consumo (Google, Microsoft, GitHub) |
| **SHA-256** | ⏳ Planificado | Despliegues de alta seguridad |
| **SHA-512** | ⏳ Planificado | Suites autenticadoras empresariales |
Futuras versiones del firmware pueden ampliar las opciones de hash sin cambiar hardware.
***
## Nota de texto en vez de un código
El mismo campo por credencial puede guardar una **nota de texto** —un código de
recuperación, una pista, cualquier cosa que quieras apuntar— en lugar de un
secreto TOTP. Se elige por credencial en el webtool: un selector marca el campo
como **2FA code** o **Note**.
* Una nota está **oculta si está vacía**, igual que el campo 2FA.
* Si existe, **se muestra en el dispositivo** (con scroll si es larga) con un
icono de documento, para que puedas leerla.
* **Nunca se teclea por USB ni se edita desde el dispositivo** — las notas solo
se crean desde el webtool.
* Límite: **32 caracteres** (la capacidad del campo).
El tipo del campo vive en su byte de metadatos (un marcador `NOTE`), así que el
dato no necesita ningún marcador en el dispositivo; los backups conservan el tipo
mediante un prefijo `note:`, de modo que importar no requiere re-marcar nada.
***
## Buenas prácticas
* Re-sincroniza la hora tras periodos largos de almacenamiento o viajes a través de zonas horarias.
* Mantén un backup offline de tus credenciales antes de realizar un reset de fábrica.
* Trata los secretos TOTP impresos o exportados como material altamente sensible.
Con TOTP gestionado directamente por la llave hardware, tu contraseña y segundo factor se mantienen juntos pero siguen offline.
# Herramienta web de sincronización horaria
Source: https://docs.zerokeyusb.com/es/firmware/totp/web-time-sync-tool
Usa el helper basado en navegador para enviar tiempo Unix preciso a ZeroKeyUSB.
El repositorio del firmware de ZeroKeyUSB incluye una aplicación web ligera que corre localmente en tu navegador. Se conecta al dispositivo por WebUSB y envía el epoch actual para que los códigos TOTP se mantengan sincronizados.
***
## Requisitos
* Navegador basado en Chromium (Chrome, Edge, Brave) con WebUSB habilitado
* ZeroKeyUSB desbloqueado y conectado vía USB-C
* Copia local del directorio **`tools/web-time-sync`** servida vía `npm run dev`
No es necesario acceso a Internet una vez cargada la página.
***
## Lanzar la herramienta
```bash theme={null}
cd tools/web-time-sync
npm install
npm run dev
```
Abre la URL local impresa (típicamente `http://localhost:5173`) en tu navegador. Deberías ver el logo de ZeroKeyUSB y un botón "Connect".
***
## Enviar la hora
1. Haz clic en **Connect** y selecciona tu ZeroKeyUSB en la lista de dispositivos (`ZeroKeyUSB CDC`).
2. La página muestra el epoch Unix actual y una cuenta atrás hasta el siguiente límite de 30 segundos.
3. Pulsa **Sync now** cuando el dispositivo pida la hora (muestra `REQTIME`).
4. La herramienta envía `SETTIME ` automáticamente y confirma el éxito con una notificación toast.
Si el dispositivo ya estaba sincronizado, responde con `OK` y no se hacen cambios.
***
## Características de seguridad
* La herramienta solo se comunica con dispositivos USB cuyos IDs de vendor/producto coincidan con ZeroKeyUSB.
* Todos los comandos son visibles en la consola en pantalla para auditabilidad.
* Ningún dato sale de la pestaña del navegador; la telemetría y analíticas están deshabilitadas.
Cierra la pestaña cuando termines. Dejar conexiones WebUSB abiertas puede impedir que otras aplicaciones accedan al puerto serie.
***
## Resolución de problemas
| Problema | Resolución |
| ---------------------------------------- | ------------------------------------------------------------------------------------- |
| El navegador no puede acceder a USB | Asegúrate de usar un navegador Chromium y haber concedido permisos al dispositivo. |
| `Failed to send epoch` | El dispositivo puede estar bloqueado; desbloquéalo e inténtalo de nuevo. |
| La herramienta se cierra inesperadamente | Comprueba la terminal que ejecuta `npm run dev` por errores y reinicia el dev server. |
La herramienta web de sincronización ofrece una forma amigable para el usuario de mantener tu autenticador hardware alineado sin instalar software pesado.
# Utilidades USB
Source: https://docs.zerokeyusb.com/es/firmware/usb-utilities
Emulación de teclado, comandos serie y backup/restore por la interfaz USB compuesta.
## Personalidad dual USB
ZeroKeyUSB opera como un **dispositivo USB Full-Speed compuesto** exponiendo dos interfaces simultáneamente:
```mermaid theme={null}
graph LR
USB["Conector USB-C"] --> COMP["Dispositivo USB compuesto"]
COMP --> HID["Teclado HID Clase 0x03"]
COMP --> CDC["Serie CDC 115200 bps"]
HID --> TYPE["Teclea credenciales al host"]
CDC --> CMD["Backup, restore, sincronización horaria"]
style USB fill:#dbeafe,stroke:#2563eb,color:#000
style HID fill:#bbf7d0,stroke:#16a34a,color:#000
style CDC fill:#fef3c7,stroke:#d97706,color:#000
```
Ambas interfaces permanecen activas tras el arranque, pero los comandos CDC que modifican datos requieren desbloqueo con PIN + autorización en el dispositivo.
***
## Motor de salida de teclado
El firmware soporta **9 layouts de teclado** almacenados como mapas de teclado compilados:
| Código | Layout |
| ------- | -------------------------------------- |
| `EN-US` | QWERTY de Estados Unidos (por defecto) |
| `DA-DK` | Danés |
| `DE-DE` | Alemán |
| `ES-ES` | Español |
| `FR-FR` | Francés |
| `HU-HU` | Húngaro |
| `IT-IT` | Italiano |
| `PT-PT` | Portugués |
| `SV-SE` | Sueco |
El layout activo se guarda en EEPROM en `0x003E` y se puede cambiar desde **Settings → Keyboard** o durante el asistente de setup.
### Secuencia de tecleo
Cuando tocas **Centro** en la pantalla principal de credenciales, ZeroKeyUSB teclea:
```mermaid theme={null}
sequenceDiagram
participant Usuario
participant Device as Dispositivo
participant Host as Ordenador host
Usuario->>Device: Toca Centro en la credencial
Device->>Host: Teclea usuario (carácter por carácter)
Device->>Host: Envía tecla TAB
Device->>Host: Teclea contraseña (carácter por carácter)
Note over Device,Host: Cada carácter se envía como keypress HID USB + release
```
El motor de tecleo en `zerokey-utils.cpp` convierte cada carácter ASCII al keycode HID apropiado usando la librería del layout de teclado seleccionado.
***
## Protocolo de comandos serie
El canal CDC se comunica a **115200 bps** usando líneas ASCII simples. Los comandos se procesan por `handleIncomingHostRequests()` en `zerokey-io.cpp`.
| Comando | Dirección | Precondición | Descripción |
| -------------- | ------------------ | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EXPORT` o `R` | Host → Dispositivo | PIN desbloqueado | Inicia la exportación de credenciales. El dispositivo muestra prompt de autorización. |
| `IMPORT` | Host → Dispositivo | PIN desbloqueado | Inicia la importación de credenciales. El dispositivo muestra prompt de autorización. |
| `` | Host → Dispositivo | Dispositivo mostrando `REQTIME` | Envía timestamp Unix epoch para sincronización TOTP. |
| `ZK PING` | Host → Dispositivo | — (siempre) | Sonda de identidad. Responde `ZK PONG ON` / `ZK PONG OFF`. La usa la [extensión de navegador](/es/getting-started/browser-extension) para detectar el dispositivo y leer el estado del link. |
| `FIND ` | Host → Dispositivo | `Chrome: On`, desbloqueado, navegando | Salta la búsqueda alfabética a ``. Responde `OK FIND `, o `ERR OFF` / `ERR BUSY` / `ERR EMPTY`. Solo navega — nunca teclea ni revela una credencial. |
| `TIME ` | Host → Dispositivo | `Chrome: On` | Ajusta el reloj del dispositivo desde el host (segundos Unix UTC) — el mismo valor que envía la [herramienta de sincronización](/es/firmware/totp/web-time-sync-tool) vía `REQTIME`, pero empujado en vez de pedido. Responde `OK TIME`. La [extensión de navegador](/es/getting-started/browser-extension) lo envía en cada uso para que los TOTP sean exactos. |
`FIND` solo *navega* la búsqueda del dispositivo. Deliberadamente no hay ningún
comando serie que teclee o revele una credencial; teclear siempre requiere una
pulsación física. `FIND` se ignora salvo que **Herramientas → Chrome** esté en on
y la bóveda esté desbloqueada y en la lista de credenciales.
### Formato de datos de exportación
Cada credencial se envía como línea CSV:
```
slotIndex,siteName,userName,password[,totpSecret]
```
* El campo TOTP es opcional y solo se incluye si el slot tiene secreto 2FA.
* La primera línea enviada es el número total de slots (`61`).
* Ejemplo: `0,github.com,alice,MyP@ss123,JBSWY3DPEHPK3PXP`
### Formato de datos de importación
Mismo formato CSV. El host envía:
1. El número total de registros (entero).
2. Una línea por registro: `slotIndex,site,user,pass[,totpSecret]`.
El dispositivo cifra cada campo con AES-128 CBC y lo escribe en el slot correspondiente de EEPROM.
***
## Sincronización horaria
Los códigos TOTP requieren hora precisa. Como ZeroKeyUSB **no tiene RTC hardware**, la hora se rastrea usando la deriva de `millis()` desde un epoch sincronizado.
```mermaid theme={null}
sequenceDiagram
participant Device as Dispositivo
participant Host
Device->>Host: "REQTIME" (vía SerialUSB)
Note over Device: Muestra "Time not set" en OLED
Host->>Device: "1714328400" (Unix epoch)
Device->>Device: syncTotpEpoch(epoch)
Device->>Device: Guarda en EEPROM 0x0040
Note over Device: Códigos TOTP ahora disponibles
```
* El valor de epoch debe estar entre `946684800` (2000-01-01) y `4102444800` (2099-12-31).
* El epoch guardado persiste entre apagados en EEPROM en `0x0040–0x0047`.
* En cada arranque, el último epoch guardado se carga y el seguimiento con `millis()` se reanuda desde ese punto.
* La deriva se acumula con el tiempo; sesiones largas o desenchufes frecuentes pueden requerir re-sincronización.
***
## Entrada al bootloader
Desde **Menú → Danger Zone → Bootloader Mode**, el firmware:
1. Escribe `0xF01669EF` en la dirección SRAM `0x20007FFC` (la palabra mágica de doble reset).
2. Llama a `NVIC_SystemReset()`.
3. El bootloader ve la palabra mágica y permanece en modo DFU USB-CDC para flashear firmware.
Esto permite actualizaciones de firmware sin acceso físico a los pogo pins SWD.
***
## Modelo de seguridad serie
* **Antes del desbloqueo con PIN:** los comandos `EXPORT` e `IMPORT` se rechazan con `ERR LOCKED`.
* **Tras el desbloqueo con PIN:** los comandos se aceptan pero requieren **autorización en el dispositivo** (pulsación larga Centro) antes de que comience cualquier transferencia de datos.
* **Durante la transferencia:** el dispositivo muestra una pantalla de progreso y rechaza nuevos comandos con `ERR BUSY`.
* **Sin comandos ocultos:** el protocolo serie no tiene comandos de debug, dump ni diagnóstico en el firmware de producción.
Toda la comunicación serie es texto plano. No hay capa de cifrado en el canal CDC. Trata la conexión USB como un cable directo a las tripas del dispositivo.
# Cartera Bitcoin
Source: https://docs.zerokeyusb.com/es/getting-started/bitcoin
Crea una cartera Bitcoin airgapped en el dispositivo, exporta una clave watch-only a tu móvil y firma transacciones (PSBT) — la clave privada nunca sale del dispositivo.
ZeroKeyUSB puede funcionar como **cartera Bitcoin airgapped**. Crea una cartera
Bitcoin estándar de 12 palabras **en el dispositivo**; la semilla se muestra solo
en la pantalla y **nunca sale por USB**. Ves saldos y construyes pagos en una app
de cartera normal (móvil o escritorio), y en ZeroKeyUSB solo ocurre la **firma**.
El menú Bitcoin está en **Tools → Bitcoin**. Solo mainnet.
| Item del menú | Qué hace |
| ----------------- | ----------------------------------------------------------------------------- |
| **Show seed** | Vuelve a mostrar las 12 palabras en la OLED (solo pantalla) |
| **Watch-only** | Envía tu clave de cuenta **pública** a una app de cartera para que vea saldos |
| **Create wallet** | Genera una nueva seed de 12 palabras (sobrescribe la existente) |
***
## 1 · Crear la cartera
Si ya existe una cartera, el dispositivo te pide **mantener pulsado Centro** para confirmar la sobrescritura (esto **destruye** la seed anterior). En un dispositivo nuevo empieza directamente.
El dispositivo muestra las palabras en páginas de tres. Avanza tocando. **Apúntalas solo en papel** — nunca las fotografíes ni las teclees en un ordenador.
Cualquiera con esas 12 palabras controla los fondos. El dispositivo guarda la seed cifrada; el papel es tu única copia.
**Create wallet** genera una seed nueva aleatoria y sobrescribe la anterior. Las monedas de la cartera vieja se pierden salvo que hayas apuntado sus 12 palabras.
Puedes volver a comprobar las palabras cuando quieras con **Show seed**.
***
## 2 · Configurar una cartera watch-only (móvil/escritorio)
Para ver saldos y construir pagos importas la clave de cuenta **pública** en una cartera normal (BlueWallet, Nunchuk, Sparrow, Bitcoin Core…). Esta clave solo puede *ver* tu cartera — nunca puede gastar.
Abre `bitcoin.html` en Chrome o Edge (Web Serial), pulsa **Connect device** y desbloquea el dispositivo con tu PIN.
Pulsa **Get watch-only**. La página muestra un **código QR** con tu clave de cuenta pública.
Escanea el **QR** en tu cartera móvil. Ya puede mostrar saldos y crear pagos, pero no puede gastar — no tiene clave privada.
También puedes lanzar el export desde el propio dispositivo: **Tools → Bitcoin → Watch-only** imprime los mismos datos por la conexión serie.
***
## 3 · Firmar una transacción (PSBT)
El flujo es el estándar airgapped de PSBT: construye el gasto en tu cartera watch-only, fírmalo en ZeroKeyUSB y transmítelo desde la cartera.
En tu cartera watch-only, crea la transacción y **exporta el PSBT sin firmar** (base64).
En `bitcoin.html`, pega el PSBT y pulsa **Send to device**.
La OLED muestra el destino, el importe que se envía y la comisión. **Verifica esto en la pantalla del dispositivo**, no en el navegador.
Mantén **Centro** para firmar (un borde de progreso se completa alrededor de la pantalla). Toca cualquier otro botón para cancelar. El dispositivo devuelve el **PSBT firmado** al webtool.
Copia el PSBT firmado de vuelta a tu cartera, finaliza y transmite.
Confirma siempre la **dirección y el importe en la propia pantalla del dispositivo** antes de mantener pulsado para firmar. Un ordenador comprometido puede mostrarte una cosa en el navegador y enviar otra — la pantalla del dispositivo es el display de confianza.
Si el PSBT no tiene entradas que pertenezcan a esta cartera, el dispositivo responde **"No inputs for us"** y no firma nada — es lo esperado cuando el PSBT es de otra cartera.
***
## Tus claves están a salvo
* La semilla de 12 palabras **nunca sale del dispositivo** — se muestra solo en la pantalla y se guarda cifrada con tu PIN.
* Por USB solo viajan datos **públicos** y **firmas** — nunca la semilla.
* Cada firma requiere un **hold de Centro** deliberado tras comprobar la dirección y el importe **en la propia pantalla del dispositivo**. Nunca firma a ciegas.
¿Quieres el detalle técnico —cómo se deriva la cartera, de dónde sale la aleatoriedad y cómo verificarlo tú mismo? Ver **[Firmante Bitcoin](/es/firmware/bitcoin-signer)** en la sección de Software.
Entropía, derivación, almacenamiento y firma — verificable contra el código.
Dónde encontrar Tools → Bitcoin.
Recupera el acceso si el display falla.
# Extensión de navegador
Source: https://docs.zerokeyusb.com/es/getting-started/browser-extension
Instala el ZeroKeyUSB Web Link y rellena inicios de sesión con un clic.
La extensión opcional **ZeroKeyUSB Web Link** para Chrome/Edge agiliza los
inicios de sesión: pulsa el icono de la barra en una página de login y el
dispositivo salta al sitio correspondiente y enfoca el campo de login — solo
eliges el sitio en el dispositivo y él teclea la credencial.
La extensión nunca ve tu contraseña. Solo le dice al dispositivo qué letra
buscar y enfoca un campo; la credencial la teclea el dispositivo después de que
tú la elijas físicamente. Cómo funciona por dentro: [Enlace de navegador](/es/firmware/browser-link).
## Instalación
Abre `chrome://extensions`, activa **Modo desarrollador**, elige **Cargar
descomprimida** y selecciona la carpeta `chrome-extension/`.
Pulsa el icono → **Connect ZeroKeyUSB…**, y elige el puerto del dispositivo en
la página que se abre. Se recuerda después.
El icono de la barra muestra un **punto verde** cuando el dispositivo está
enlazado y un **punto gris** cuando no lo está.
## Cómo usarla
Ve a una web donde tengas cuenta, y asegúrate de que el ZeroKeyUSB está conectado
y **desbloqueado**.
El dispositivo salta a la primera letra del sitio y el campo de login de la
página queda enfocado.
Elige el sitio correspondiente en el dispositivo y pulsa — teclea tu usuario y
contraseña.
## Desactivarla
¿No la quieres? En el dispositivo, pon **Menú → Herramientas → `Chrome: Off`** e
ignorará la extensión por completo. Viene **On** de fábrica.
Funciona en la mayoría de páginas de login. Los sitios que separan usuario y
contraseña en pantallas distintas (algunos flujos de Google/Microsoft) pueden no
rellenarse correctamente.
# Hacer una copia de seguridad
Source: https://docs.zerokeyusb.com/es/getting-started/creating-backups
Exporta todas tus credenciales como CSV por USB serie. Pantalla a pantalla, desde el menú hasta el fichero guardado.
Una copia de seguridad te protege contra pérdida o daño del dispositivo. ZeroKeyUSB hace que el proceso sea **deliberado**: ninguna credencial sale del dispositivo sin que pulses el botón de autorización físicamente.
La copia de seguridad sale en **texto plano** por USB serie. Cífrala inmediatamente después de guardarla (GPG, age, 7z con contraseña…) y guárdala offline.
***
## Antes de empezar
| Necesitas | Para qué |
| ---------------------------------------- | -------------------------------------------------------------- |
| El dispositivo desbloqueado | Solo se puede exportar con el PIN ya introducido |
| Una herramienta serie | Web manager, `screen`, `minicom`, PuTTY o similar a 115200 bps |
| Un sitio seguro donde guardar el fichero | Disco cifrado, tarjeta SD bajo llave, etc. |
***
## Paso 1 — Abrir el menú
Desde la primera credencial, pulsa **Izquierda**. (También puedes llegar pulsando **Derecha** desde la última credencial → "Anadir Nuevo" → Derecha.) Ver [Navegar el menú](/es/getting-started/menu-navigation) si tienes dudas.
*Pulsa **Izquierda** estando en la credencial 1.*
***
## Paso 2 — Seleccionar "Tools"
En el firmware actual este menú se llama **Tools** (se llamaba **Backup** cuando se hicieron estas capturas, y ahora contiene también la cartera Bitcoin). La posición y los pasos son idénticos — Export/Import están dentro de él.
En el menú raíz, **Tools** ya está seleccionado por defecto. Pulsa **Centro** para entrar al submenú.
*Pulsa **Centro**.*
***
## Paso 3 — Seleccionar "Exportar"
Dentro de Backup tienes `Importar`, `Exportar` y `< Volver`. Pulsa **Abajo** una vez para resaltar `Exportar` y luego **Centro**.
*Pulsa **Abajo** para llegar a `Exportar`, después **Centro** para activar.*
***
## Paso 4 — Autorización física
Tras pulsar Centro aparece la pantalla de autorización. Es una **medida de seguridad**: el dispositivo no exporta hasta que mantengas Centro pulsado más de 800 ms (verás el halo grande del botón).
*Antes de continuar, abre tu terminal serie en el host y conéctate al dispositivo (115200 bps), o abre el [web manager](https://zerokeyusb.com/manager). Cuando esté listo para recibir, **mantén Centro \~1 segundo**.*
Si te lo piensas mejor, **suelta antes de los 800 ms** o pulsa **Izquierda** — la operación se cancela y vuelves al menú.
***
## Paso 5 — Progreso del envío
Tras autorizar, el dispositivo descifra cada credencial (una por una) y la envía como una línea CSV por USB. La pantalla muestra el progreso.
*No tienes que pulsar nada. El dispositivo recorre los 61 huecos y al terminar muestra "Exportacion completa" durante 1 segundo antes de volver al menú.*
***
## Paso 6 — Capturar el CSV en el host
Mientras el dispositivo envía, en tu terminal serie verás aparecer línea a línea:
```csv theme={null}
61
0,Google,user1,contrasena1,JBSWY3DPEHPK3PXP
1,Apple,user2,password,
2,Netflix,user3,Atalaya,
3,github,user4,no me acuerdo,JBSWY3DPEHPK3PXP;algo=SHA256
4,Amazon,comprador99,123456,
...
```
| Campo | Significado |
| ------------ | ------------------------------------------------------- |
| Línea 0 | Número total de huecos (siempre `61`) |
| `slotIndex` | 0–61 |
| `site` | Sitio (≤32 chars) |
| `username` | Usuario (≤32 chars) |
| `password` | Contraseña |
| `totpSecret` | Opcional — Base32 (con `;algo=SHA256` opcional) o vacío |
Guarda el output completo en un fichero (`zerokeyusb-2026-05-27.csv`, por ejemplo). En el web manager hay un botón "Guardar" que hace esto por ti.
***
## Paso 7 — Cifrar inmediatamente
El CSV recién guardado contiene **todas** tus contraseñas en claro. Cífralo nada más capturarlo y borra la versión sin cifrar.
```bash theme={null}
# Opción A: GPG (simétrico, AES-256)
gpg -c --cipher-algo AES256 zerokeyusb-2026-05-27.csv
shred -u zerokeyusb-2026-05-27.csv
# Opción B: age con contraseña
age -p -o zerokeyusb-2026-05-27.csv.age zerokeyusb-2026-05-27.csv
shred -u zerokeyusb-2026-05-27.csv
# Opción C: 7z con contraseña fuerte (Windows)
7z a -p -mhe=on zerokeyusb-2026-05-27.7z zerokeyusb-2026-05-27.csv
del zerokeyusb-2026-05-27.csv
```
***
## ¿Cuándo hacer copia?
| Cuándo | Por qué |
| --------------------------------------------------- | ------------------------------------ |
| Tras añadir o importar varias credenciales | Para no perder el trabajo nuevo |
| Antes de hacer Reset de fábrica o flashear firmware | El reset es irreversible |
| Antes de cualquier acción del submenú **Peligro** | Idem |
| Periódicamente | Como parte de tu rutina de seguridad |
***
## Buenas prácticas
| Práctica | Por qué |
| --------------------------------------- | ------------------------------------------------------ |
| Cifra al instante | El CSV en plano es un riesgo enorme |
| Guarda en ≥ 2 lugares físicos distintos | Protege contra fallo de un disco/nube |
| Prueba la restauración alguna vez | Garantiza que el fichero es válido cuando lo necesites |
| Borra copias antiguas con `shred` | Reduce la exposición |
| Nombra con fecha (`YYYY-MM-DD`) | Para saber cuál es la más reciente |
***
## Próximos pasos
Restaurar desde una copia o migrar de otro gestor.
Conoce el resto de opciones del menú.
# Editar una credencial
Source: https://docs.zerokeyusb.com/es/getting-started/edit-credential
Cómo entrar al editor, mover el cursor, usar las tres páginas de teclado, generar contraseñas aleatorias y guardar.
Esta guía detalla todas las operaciones del editor on-device. Si solo quieres crear tu primera credencial, empieza por la guía [Crear tu primera credencial](/es/getting-started/first-credential).
***
## Paso 1 — Selecciona la credencial
Desde la pantalla principal, navega hasta la credencial que quieras editar usando **Izquierda/Derecha**. Si tienes muchas, mantén **Izquierda/Derecha** para saltar 10 huecos de golpe.
*Cuando la credencial correcta esté en pantalla (verás su número arriba a la izquierda — aquí `4`), pasa al siguiente paso.*
***
## Paso 2 — Selecciona el campo y entra al editor
El campo activo se ve invertido en blanco a la izquierda (mundo = sitio, silueta = usuario, candado = clave, llave = 2FA). Cambia entre campos con **Abajo/Arriba**:
* **Sitio** ↓ **Usuario** ↓ **Clave** ↓ **2FA** (si la credencial tiene TOTP)
Cuando estés sobre el campo correcto, **mantén Centro \~1 segundo** para entrar al editor.
*Una pulsación **corta** en Centro tecleará el campo al ordenador (no es lo que quieres). Una pulsación **larga** (\~800 ms hasta ver el halo grande) abre el editor.*
***
## Paso 3 — Anatomía del editor
El editor se ve así:
| Elemento | Función |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Fila 1 | Muestra el contenido actual del campo (**hasta 32 caracteres**, con scroll lateral a partir de 16) y un cursor invertido en la posición de inserción |
| Fila 2 — `<` `>` | Mueven el cursor del **campo** (la posición donde se inserta) |
| Fila 2 — KB1 | Teclado de mayúsculas: `A B C D E F G H I J K L M N O P` |
| Fila 3 — `Rand` | Rellena el campo con caracteres aleatorios fuertes del TRNG hardware (formato según Ajustes) |
| Fila 3 — KB2 | Teclado de minúsculas: `a b c d e f g h i j k l m n o p` |
| Fila 4 — `Save` | Guarda los cambios y vuelve a la pantalla principal |
| Fila 4 — KB3 | Números/símbolos: `0 1 2 3 4 5 6 7 8 9 - + ! @ #` |
***
## Paso 4 — Mover el cursor por el teclado
Cuando entras al editor, el "foco" empieza en KB1 (fila 2). **Izquierda/Derecha** mueven el cursor del teclado dentro de la fila actual.
*Pulsa **Derecha** para avanzar el cursor del teclado, **Izquierda** para retroceder. Al llegar al final/inicio, salta al control de la fila adyacente (`Rand`, `Save`, `<`, `>`).*
***
## Paso 5 — Insertar un carácter
Con el carácter deseado bajo el cursor del teclado, pulsa **Centro**. Se inserta en la posición actual del cursor del campo, que avanza automáticamente.
*La `L` se inserta en la posición 5 del campo y el cursor del campo avanza a la posición 6. Si querías reemplazar en vez de insertar, lee el siguiente paso.*
***
## Paso 6 — Mover el cursor por el **campo** (no por el teclado)
A veces quieres editar caracteres en medio del campo, no añadir al final. Para eso usa los símbolos `<` y `>` de la fila 2.
*Llega hasta el símbolo `<` o `>` con **Izquierda** (desde KB1) y pulsa **Centro**. El cursor del **campo** se mueve una posición a la izquierda o a la derecha. Cuando esté en la posición que quieras editar, vuelve a KB1/KB2/KB3 y pulsa **Centro** sobre la nueva letra — sobreescribe la que había.*
Las inserciones del teclado **sobreescriben** el carácter de la posición actual, no insertan. Así "borrar" un carácter es simplemente moverte sobre él y meter un espacio (KB3, primer carácter).
***
## Paso 7 — Generar contraseña aleatoria
Para campos de contraseña, en lugar de teclear letra a letra puedes usar **Rand**. Genera una contraseña aleatoria fuerte (**hasta 32 caracteres**).
El **formato** sigue lo que elijas en **Ajustes → `Pwd:`** (ver [Menú](/es/firmware/menu)). Hay seis formatos disponibles:
| Etiqueta en Ajustes | Formato | Ejemplo |
| ----------------------- | --------------------------------------------------------------------------- | ---------------------------------- |
| `Symbols` (por defecto) | 32 chars del set imprimible completo (letras, dígitos, símbolos) | `t7#Kp!2r$Qm9^Za5Wd8&Bv3Ln6@Xj1Qz` |
| `Numeric` | 32 dígitos | `48201937560428173905641820937584` |
| `a-z 0-9` | 32 letras minúsculas + dígitos | `k3m9xq1z7r4p2n8wj5c6vb0hd1ft9gs2` |
| `Aa-z 0-9` | 32 letras mayúsculas/minúsculas + dígitos | `Kp3Mx9Qz1Rt4Nb8yWd2Fc7Hj5Lv0Gs6R` |
| `Words` | Palabras BIP39 capitalizadas unidas por un separador aleatorio (`-_.!@#*+`) | `Ocean-Cargo-Mint-River-Sun` |
| `Words+Num` | Igual, más un grupo de 2 dígitos tras la primera palabra | `River_28_Mango_Ocean_Tiger` |
Los formatos de palabras usan una lista de palabras incorporada y meten tantas palabras como caben bajo 32 caracteres. `Rand` en los campos **site** o **user** siempre usa el set `Symbols` completo, independientemente de este ajuste.
*Navega a `Rand` con **Abajo** desde la fila 2. Pulsa **Centro**: el campo se rellena. Si no te gusta, pulsa **Centro** otra vez para regenerar.*
***
## Paso 8 — Guardar
Cuando termines, navega a `Save` (esquina inferior izquierda) y pulsa **Centro**. Los cambios se cifran con AES-128 CBC y se escriben a EEPROM.
*El editor se cierra y vuelves a la vista principal. La credencial ya tiene los cambios.*
Salir del editor sin pulsar **Save** **descarta** los cambios. No hay confirmación: en el momento que cambies de pantalla por cualquier ruta que no sea `Save`, lo escrito se pierde.
***
## Tabla de atajos
| Acción | Cómo |
| ---------------------------- | -------------------------------------------------------------------- |
| Entrar al editor | Pulsación larga **Centro** sobre un campo |
| Mover cursor del teclado | **Izquierda/Derecha** |
| Cambiar de fila/teclado | **Arriba/Abajo** |
| Insertar carácter | **Centro** sobre la letra |
| Mover cursor del **campo** | **Centro** sobre `<` o `>` (fila 2) |
| "Borrar" un carácter | Mover el cursor a esa posición y meter espacio (KB3 primer carácter) |
| Generar contraseña aleatoria | **Centro** sobre `Rand` |
| Guardar y salir | **Centro** sobre `Save` |
| Descartar cambios | Desconectar el USB antes de pulsar Save |
***
## Próximos pasos
El campo TOTP solo se importa por USB-CDC, no se edita on-device.
Guarda tu estado actual antes de editar muchas credenciales.
# Crear tu primera credencial
Source: https://docs.zerokeyusb.com/es/getting-started/first-credential
Desde desbloquear el dispositivo con el PIN hasta guardar tu primera contraseña usando 'Anadir Nuevo' y el editor on-device.
Esta guía asume que ya has completado el [tutorial inicial](/es/getting-started/setup-wizard). Si no, hazlo antes.
***
## Paso 1 — Desbloquear con el PIN
Al enchufar el dispositivo aparece el numpad para introducir el PIN. Para cada dígito: **Arriba/Abajo** hasta el valor correcto, **Derecha** para confirmar.
*Cuando termines el último dígito, el cursor salta al tick (✓). Pulsa **Centro** sobre el tick para validar.*
***
## Paso 2 — "Anadir Nuevo" aparece automáticamente
Si es la primera vez (no hay credenciales aún) o has navegado hasta el final de tu lista, aparece la pantalla **Anadir Nuevo** — un botón centrado.
*Pulsa **Centro** para crear una credencial vacía en el primer hueco libre.*
Si ya tienes credenciales, también puedes llegar aquí desde la pantalla principal pulsando **Derecha** repetidamente hasta pasar la última.
***
## Paso 3 — Credencial recién creada
El dispositivo crea un hueco con valores placeholder (`nuevo`/`usuario`/`clave`) y te lleva directamente a su vista principal. En la izquierda verás el número del hueco (`1`).
*Para empezar a rellenar el sitio, **mantén pulsado Centro \~1 segundo** (verás aparecer el halo grande). Al soltar entrarás al editor.*
***
## Paso 4 — Editor: la primera letra
El editor ocupa toda la pantalla. Tiene 4 filas:
1. **Arriba:** el campo en edición (16 huecos).
2. **Fila 2:** controles `< >` para mover el cursor + teclado **KB1** (mayúsculas `A-P`).
3. **Fila 3:** `Rand` (relleno aleatorio) + **KB2** (minúsculas `a-p`).
4. **Fila 4:** `Save` + **KB3** (números y símbolos).
Empezamos por escribir la primera letra. Estamos en `KB1` con el cursor sobre `G`.
*Usa **Derecha/Izquierda** para mover el cursor sobre el teclado y **Centro** para insertar el carácter seleccionado en el campo. El cursor del campo (la posición donde se inserta) avanza automáticamente.*
***
## Paso 5 — Cambiar de página de teclado
Para acceder a minúsculas (KB2) o números/símbolos (KB3), usa **Abajo** desde KB1.
*Pulsa **Abajo** para saltar de KB1 a KB2 (y otra vez para llegar a KB3). **Arriba** sube. Sigue insertando letras con **Centro**.*
Para mover el cursor **dentro del campo de texto** (no del teclado), usa los símbolos `<` y `>` de la segunda fila — accesibles cuando estás en KB1 con el cursor del teclado en su primera posición y pulsas **Izquierda** una vez más.
***
## Paso 6 — Guardar la credencial
Cuando hayas terminado de escribir, navega a **Save** (fila 4, columna izquierda) usando **Abajo** desde `Rand` o **Izquierda** desde KB3, y pulsa **Centro**.
*Con `Save` resaltado en blanco, pulsa **Centro** para guardar y volver a la vista principal de la credencial. Lo escrito se cifra y se guarda antes de salir.*
***
## Paso 7 — Pasar al campo Usuario
De vuelta en la vista principal verás el sitio (`github`) y los demás campos aún vacíos. Usa **Abajo** para cambiar del campo **Sitio** al campo **Usuario**.
*Pulsa **Abajo**. El icono pasa de sitio (mundo) a usuario (silueta), confirmando que el siguiente edit aplicará al campo usuario. Después **mantén Centro** para volver al editor.*
***
## Paso 8 — Generar contraseña aleatoria con Rand
Repite los pasos para el campo Usuario. Después navega a **Pass** (otra pulsación de Abajo) y entra al editor. Pero esta vez, en lugar de teclearla a mano, usa la opción **Rand** para que el dispositivo genere una contraseña fuerte de 12 caracteres.
*Con `Rand` resaltado, pulsa **Centro**. El campo `Pass` se rellena al instante con una contraseña aleatoria fuerte. Luego baja a `Save` y pulsa **Centro** para guardar.*
La contraseña generada se queda fija en el campo. Si no te gusta, pulsa **Centro** de nuevo sobre `Rand` para regenerarla.
***
## Paso 9 — Listo
Vuelves a la vista principal. La credencial ya tiene su sitio, usuario y contraseña guardados de forma segura. Si ahora pulsas **Centro** sobre el campo Sitio, ZeroKeyUSB tecleará al ordenador `usuario` + `TAB` + `contraseña` + `ENTER` como si fueras tú con un teclado normal.
*Cuando estés en la página de login de la web (con el cursor en el campo de usuario), pulsa **Centro** corto. El dispositivo escribe usuario + TAB + contraseña + ENTER automáticamente.*
***
## Próximos pasos
Aprende a modificar campos existentes y a usar atajos del editor.
Añade códigos TOTP a tus credenciales para login con segundo factor.
Exporta tus credenciales recién creadas a un fichero seguro.
Descubre Tools, Ajustes, Peligro e Info desde la lista de credenciales.
# Importar credenciales
Source: https://docs.zerokeyusb.com/es/getting-started/importing-credentials
Restaurar desde una copia de seguridad o migrar desde otro gestor. Pantalla a pantalla, desde el menú hasta los datos importados.
Importar carga credenciales (sitio, usuario, contraseña y opcionalmente un secreto TOTP) desde un fichero CSV vía USB serie. Se hace **después de desbloquear** el dispositivo y requiere **autorización física** con el botón Centro.
La importación **sobreescribe** los huecos de destino sin preguntar. Si ya tienes credenciales, **haz una copia de seguridad** ([guía](/es/getting-started/creating-backups)) antes de continuar.
***
## Antes de empezar
| Necesitas | Cómo conseguirlo |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| El dispositivo desbloqueado con PIN | Conéctalo y mete tu PIN |
| Fichero CSV con tus credenciales | Una exportación previa, o un export desde 1Password / Bitwarden / Keepass adaptado al formato (ver más abajo) |
| Una herramienta serie | [Web manager](https://zerokeyusb.com/manager) (recomendado), `screen`, `minicom`, PuTTY a 115200 bps |
***
## Paso 1 — Abrir el menú
Estando en la primera credencial, pulsa **Izquierda**.
*Pulsa **Izquierda** estando en la credencial 1.*
***
## Paso 2 — Entrar al submenú Tools
En el firmware actual este menú se llama **Tools** (se llamaba **Backup** cuando se hicieron estas capturas, y ahora contiene también la cartera Bitcoin). La posición y los pasos son idénticos — Export/Import están dentro de él.
En el menú raíz, **Tools** está seleccionado por defecto. Pulsa **Centro**.
*Pulsa **Centro**.*
***
## Paso 3 — Seleccionar "Importar"
Dentro de Backup, `Importar` está arriba. Si no está resaltado, pulsa **Arriba** hasta llegar. Luego **Centro**.
*Con `Importar` resaltado, pulsa **Centro**.*
***
## Paso 4 — Autorización física
Igual que en la exportación, una pantalla de autorización pide pulsación larga en Centro antes de habilitar el canal de importación.
*Antes de continuar, prepara tu fichero CSV en el host. Cuando estés listo, **mantén Centro \~1 segundo**. El dispositivo envía `REQUEST_SAVE` por USB serie esperando los datos.*
**No pulses Centro corto por accidente** — la pulsación corta no autoriza nada (solo pasa a la siguiente pantalla del menú si la hubiera).
***
## Paso 5 — Enviar el CSV desde el host
Con el dispositivo en modo "esperando datos", envía por USB serie:
1. Una línea con el **número total de registros** a importar (ejemplo: `5`).
2. Una línea CSV por cada credencial siguiendo este formato:
```csv theme={null}
slotIndex,sitio,usuario,clave[,secretoTotp]
```
Ejemplo completo:
```csv theme={null}
5
0,github.com,alice,MyP@ss123,JBSWY3DPEHPK3PXP
1,gmail.com,bob@gmail.com,correct horse battery staple
2,bank.com,12345678X,s3cur3P@ss,JBSWY3DPEHPK3PXP;algo=SHA256
3,aws-prod,admin,A!7zQ#mYpL2v
4,banca,12345678X,Pin-only2FA
```
Cada línea se procesa así:
```
Host → dispositivo: "0,github.com,alice,MyP@ss123,JBSWY3DPEHPK3PXP"
Dispositivo: lo cifra y lo guarda en el hueco 0
Dispositivo → host: "Record 1 stored correctly."
```
El [web manager](https://zerokeyusb.com/manager) tiene un selector de fichero que envía las líneas en el orden correcto y enseña el progreso. Si no quieres pelearte con la terminal, úsalo.
***
## Paso 6 — Progreso
Durante la importación verás en pantalla qué slot se está escribiendo y el progreso global.
*Cuando termina, el dispositivo muestra "Importacion completa" durante 1 segundo y vuelve al menú. Pulsa **Izquierda** para salir hacia la lista de credenciales y verificar que se han añadido.*
***
## Paso 7 — Verificar
Pulsa **Izquierda** repetidamente para salir del menú a la lista de credenciales. Navega con **Izquierda/Derecha** y comprueba que los datos importados aparecen.
*Usa **Derecha** para recorrer las credenciales recién importadas. Si todo aparece bien, considera hacer una [nueva copia de seguridad](/es/getting-started/creating-backups) ahora.*
***
## Formato del CSV
| Campo | Descripción | Límite |
| ------------ | ----------------------- | -------------------------- |
| `slotIndex` | Hueco de destino | 0–61 |
| `site` | Sitio o servicio | 32 caracteres |
| `username` | Usuario | 32 caracteres |
| `password` | Contraseña | Cualquier ASCII imprimible |
| `totpSecret` | (opcional) Secreto TOTP | Ver abajo |
### Formatos de secreto TOTP aceptados
| Formato | Ejemplo |
| ------------------------- | ---------------------------------------------------------------------- |
| Base32 a secas | `JBSWY3DPEHPK3PXP` |
| Base32 + algoritmo | `JBSWY3DPEHPK3PXP;algo=SHA256` |
| URI `otpauth://` completa | `otpauth://totp/GitHub:alice?secret=JBSWY3DPEHPK3PXP&algorithm=SHA256` |
Algoritmo por defecto si no se especifica: **SHA-1**.
***
## Validación y errores
| Caso | Comportamiento |
| --------------------------- | ----------------------------------------- |
| `slotIndex` fuera de 0–61 | Rechaza la línea: `"Index out of range"` |
| Base32 inválida | Rechaza el secreto TOTP: `"TOTP invalid"` |
| Línea con menos de 3 campos | Se salta con un log de error |
| Línea vacía | Termina la importación temprano |
***
## Migración desde otros gestores
Para migrar de 1Password, Bitwarden, Keepass u otros:
1. Exporta a CSV en el gestor de origen.
2. Adapta el CSV al formato ZeroKeyUSB (script Python sencillo — recorta cada campo a 32 chars y reordena columnas).
3. Importa siguiendo esta guía.
Hay plantillas de scripts de conversión en el [repositorio de tools](https://github.com/Depbit-lab/zerokeyusb-tools) — `tools/convert_.py`.
***
## Próximos pasos
Exporta los datos recién importados antes de seguir trabajando.
Si has importado secretos TOTP, mira cómo verlos y teclearlos.
# Navegar el menú
Source: https://docs.zerokeyusb.com/es/getting-started/menu-navigation
Cómo acceder al menú principal desde la lista de credenciales y moverte por Tools, Ajustes, Peligro e Info.
El menú principal contiene las acciones que **no** son credenciales: herramientas (copias de seguridad y la cartera Bitcoin), ajustes de pantalla y layout, opciones peligrosas (reset de fábrica) e información del firmware. Esta guía te enseña a llegar allí y a moverte dentro.
***
## Ruta A — Desde la primera credencial
Estando en la credencial 1, **Izquierda** abre el menú directamente.
*Pulsa **Izquierda** estando en la credencial número 1.*
***
## Ruta B — Desde la última credencial (vía "Anadir Nuevo")
Si estás cerca del final de tu lista, otra forma de llegar es **Derecha** repetidamente: tras la última credencial aparece "Anadir Nuevo", y otra **Derecha** salta al menú.
*Desde "Anadir Nuevo", pulsa **Derecha** para llegar al menú (sin crear nada). Si en lugar de pasar quieres crear una credencial, pulsa **Centro**.*
***
## Saltar de 10 en 10
Mantén pulsado **Izquierda** o **Derecha** (pulsación larga) mientras navegas las credenciales para saltar **10 huecos usados** de golpe en esa dirección, en lugar de uno. Un toque corto sigue moviendo un hueco. Útil cuando tienes muchas credenciales.
***
## Pantalla del menú
Una vez dentro, ves cuatro opciones en el menú raíz. La barra `MENU` invertida ocupa los primeros 10 píxeles a la izquierda como marca visual.
| Item | Qué hace |
| ----------- | ---------------------------------------------------------------------------------------------------- |
| **Tools** | `Exportar` / `Importar` credenciales por USB, más la cartera **Bitcoin** |
| **Ajustes** | Rotar pantalla, idioma de la UI, layout del teclado, modo lector de pantalla y re-lanzar el tutorial |
| **Peligro** | Reset de fábrica (borra **todo**) y modo bootloader (para reflashear) |
| **Info** | Versión del firmware, número de serie y estado |
*Navega entre items con **Arriba/Abajo**. Para entrar en un submenú, pulsa **Centro**. Para volver al menú padre, pulsa **Izquierda**.*
***
## Entrar a un submenú
Por ejemplo, dentro de **Ajustes**:
| Item | Acción |
| -------------- | --------------------------------------------------------------------------------------------------------------- |
| Rotar pantalla | Gira la pantalla 180° (también gira los controles) |
| Idioma: XX | Cicla el idioma de la UI (Español ↔ Inglés) |
| Teclado: XX-XX | Cicla el layout del teclado USB |
| Reader: On/Off | Hace persistente el [modo lector de pantalla](/es/getting-started/recovery-no-screen) entre reinicios |
| Pwd: XX | Elige el formato de contraseña aleatoria que usa `Rand` (Symbols, Numeric, a-z 0-9, Aa-z 0-9, Words, Words+Num) |
| Tutorial | Relanza el wizard de primer encendido |
| `< Volver` | Vuelve al menú padre (equivalente a pulsar Izquierda) |
*Selecciona un item con **Arriba/Abajo** y pulsa **Centro** para ejecutarlo. **Izquierda** vuelve al menú raíz.*
***
## Salir del menú
Desde el menú raíz, pulsar **Izquierda** te lleva a la pantalla "Anadir Nuevo" (saltando una credencial atrás), y otra **Izquierda** a la última credencial. Pulsar **Derecha** te lleva directamente a la primera credencial.
| Acción en el menú raíz | Resultado |
| ---------------------- | --------------------------------------------- |
| **Izquierda** | "Anadir Nuevo" → otra IZQ → última credencial |
| **Derecha** | Primera credencial |
| **Arriba/Abajo** | Navegar items |
| **Centro** | Entrar al submenú/ejecutar item |
La navegación es **circular**: credenciales → "Anadir Nuevo" → menú → primera credencial → … No hay esquinas sin salida.
***
## Submenús — referencia rápida
### Tools
| Item | Acción |
| -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Exportar | Envía todas las credenciales por USB-CDC (ver [guía](/es/getting-started/creating-backups)) |
| Importar | Recibe credenciales por USB-CDC (ver [guía](/es/getting-started/importing-credentials)) |
| Bitcoin | Cartera Bitcoin en el dispositivo: crear, ver seed, export watch-only y firma de PSBT (ver [guía](/es/getting-started/bitcoin)) |
### Peligro
Las acciones del submenú **Peligro** son irreversibles. El reset de fábrica, el bootloader y el Importar/Exportar de credenciales ahora requieren **mantener pulsado Centro** (\~1,5 s) para confirmar — un toque no las activa, y **Izquierda** cancela. Un borde de progreso se va completando alrededor de la pantalla mientras mantienes pulsado.
| Item | Acción |
| ------------- | --------------------------------------------------------------------------------------------- |
| Reset fabrica | Borra **todas** las credenciales y resetea el PIN. El dispositivo vuelve al tutorial inicial. |
| Bootloader | Reinicia el dispositivo en modo flash para subir un firmware nuevo. |
### Info
Muestra la versión del firmware (`v1.0`), el número de serie del dispositivo y un check de integridad. Solo informativo — los items son no-interactivos excepto `< Volver`.
***
## Próximos pasos
Crear una cartera, exportar watch-only y firmar transacciones.
Lee la pantalla por USB si el display falla.
El primer uso del submenú Tools → Exportar.
Recuperar de una copia o migrar de otro gestor.
# Modo pantalla rota / sin pantalla
Source: https://docs.zerokeyusb.com/es/getting-started/recovery-no-screen
Si la OLED falla, ZeroKeyUSB puede teclear por USB lo que habría en pantalla para que aún puedas meter el PIN y usar el dispositivo.
Si la pantalla se rompe, el dispositivo no queda inservible. Un **modo lector de pantalla** hace que ZeroKeyUSB teclee el contenido de la pantalla actual por **USB HID (teclado)**, línea a línea, en un campo de texto enfocado de tu ordenador o móvil. Puedes seguir metiendo el PIN, navegar las credenciales y teclear contraseñas — a ciegas.
El host teclea en el campo de texto que tengas enfocado. Abre una app de notas / caja de texto vacía **antes** de activarlo, para que la salida vaya a un sitio inofensivo.
***
## Activarlo
Conecta el dispositivo y llega a la pantalla del PIN (la pantalla normal de arranque).
Mantén pulsado el botón **Centro \~10 s**. Un borde se va completando alrededor de la pantalla durante los 10 s; al completarse, el modo se alterna. Si sueltas antes de los 10 s no pasa nada.
El dispositivo empieza a teclear el estado actual en tu campo de texto enfocado, p. ej. `ZK reader ON` y luego la línea del PIN.
Mantén Centro 10 s de nuevo para desactivarlo.
Para hacerlo **permanente** (útil cuando la pantalla está realmente muerta), actívalo en **Ajustes → Reader: On/Off**. Eso guarda la elección en EEPROM y el dispositivo arranca directamente en modo lector. El gesto de 10 s solo lo alterna para la sesión actual.
***
## Qué teclea
El dispositivo mantiene **una línea** en pantalla, borrándola y reescribiéndola según navegas:
| Pantalla | Teclea |
| ------------------------------ | ------------------------------------------------------------------------------------------------- |
| Entrada de PIN | `PIN 125 >7` — los dígitos metidos, y luego el dígito seleccionado (`>OK` = el tick de confirmar) |
| Credencial (sitio) | `3: google.com` |
| Credencial (user / pass / 2FA) | `3: user`, `3: password`, `3: 2FA` |
| Menú | `Menu: ` |
| Página de confirmación | ` Hold=OK Left=No` |
Meter el PIN a ciegas: usa **Arriba/Abajo** para cambiar el dígito seleccionado (mira cómo cambia `>n`), **Derecha** para añadirlo, **Izquierda** para borrar, y selecciona el tick (`>OK`) y luego **Derecha/Centro** para desbloquear — igual que en pantalla, pero leyendo la línea tecleada en vez de la OLED.
Cuando pulsas **Centro** en una credencial, la línea de anuncio se borra y se teclea el **valor real** (user / contraseña) — así, con la pantalla muerta, aún puedes leer una contraseña pulsando Centro en una caja de texto.
***
## Notas y seguridad
Esto teclea el contenido de tu pantalla — **incluidos los dígitos del PIN y, al pulsar Centro, las contraseñas** — en el campo enfocado. Úsalo para recuperación, en un campo de texto privado que controles, y borra ese campo después.
* Funciona en la pantalla de PIN incluso antes de desbloquear, así que un dispositivo con la pantalla rota aún se puede desbloquear.
* La **seed** de Bitcoin nunca se teclea con este modo — es solo-pantalla por diseño.
* El tecleo va a un ritmo deliberado, así que cada línea tarda un momento en aparecer.
Qué emite exactamente por USB — y qué no hace nunca.
Dónde está el ajuste Reader.
Otras opciones de recuperación.
# Tutorial inicial
Source: https://docs.zerokeyusb.com/es/getting-started/setup-wizard
Recorrido pantalla a pantalla del asistente que aparece la primera vez que enchufas tu ZeroKeyUSB. Orientación, layout del teclado y PIN maestro.
La primera vez que enchufas tu ZeroKeyUSB en un puerto USB-C, arranca automáticamente un asistente de configuración en español de 9 páginas. Esta guía te lleva paso a paso desde la pantalla de bienvenida hasta tener el dispositivo listo para guardar credenciales.
***
## Paso 1 — Pantalla de bienvenida
Al conectar el cable, la pantalla se enciende mostrando el logo durante unos segundos. Es la confirmación de que el dispositivo ha arrancado correctamente y ha pasado su comprobación de seguridad del firmware.
*Espera unos 2 segundos. El asistente aparece automáticamente — no tienes que pulsar nada.*
***
## Paso 2 — Página 1: Bienvenida
Primera página del asistente. La barra superior invertida muestra el título y el indicador de página `<1/9>`. El cuerpo describe brevemente qué es el dispositivo y qué hacer.
*Pulsa **Derecha** para avanzar. Si el cuerpo tiene más texto del que cabe en pantalla, usa **Arriba/Abajo** para hacer scroll antes de continuar.*
***
## Paso 3 — Página 2: Navegación
Explica qué hace cada uno de los 5 pads. Es la única página que verás que describe los controles explícitamente.
*Lee la página y pulsa **Derecha** para avanzar a la siguiente.*
***
## Paso 4 — Página 3: Girar pantalla
Si los puntos dorados quedan a tu izquierda en lugar de a la derecha, dale la vuelta al dispositivo físicamente — o gira la pantalla por software con **Centro**. La orientación se guarda en el dispositivo y persiste entre apagados.
*Pulsa **Centro** para alternar la orientación (verás `NORMAL ↔ ROTADA` en pantalla). Cuando los controles te queden bien, pulsa **Derecha** para continuar.*
***
## Paso 5 — Página 4: Distribución del teclado
ZeroKeyUSB emula un teclado USB. Para que las contraseñas con símbolos especiales (`@`, `!`, `#`, etc.) se tecleen correctamente en tu ordenador, tiene que conocer **tu** layout de teclado.
*Pulsa **Centro** para ciclar entre EN-US, ES-ES, FR-FR, DE-DE, IT-IT, PT-PT, DA-DK, SV-SE, HU-HU. Cuando aparezca el tuyo, pulsa **Derecha**.*
Puedes cambiarlo más adelante desde **Menú → Ajustes → Teclado**.
***
## Paso 6 — Página 5: PIN maestro (explicación)
Esta página explica las reglas del PIN antes de pedirlo. Elige entre 4 y 16 dígitos (0–9). Es lo único que separa al atacante de tus credenciales.
*Pulsa **Centro** para pasar a la pantalla de introducción del PIN.*
**No hay recuperación de PIN.** Si lo olvidas, la única opción es un reset de fábrica que borra **todas** tus credenciales. Memoriza el PIN — o apúntalo en un lugar físico seguro.
***
## Paso 7 — Introducir el PIN
Pantalla del numpad. Cada dígito se introduce con **Arriba/Abajo** (para elegir 0–9) y **Derecha** (para confirmar y avanzar al siguiente dígito). Las flechas pequeñas a los lados del dígito activo te recuerdan los movimientos disponibles.
*Por cada dígito de tu PIN: **Arriba/Abajo** hasta el número correcto, luego **Derecha** para confirmarlo. Cuando termines el último dígito, el cursor saltará al símbolo de tick (✓). Pulsa **Centro** sobre el tick para guardar el PIN.*
Para borrar el último dígito si te equivocas, pulsa **Izquierda**.
***
## Paso 8 — Página 6: PIN guardado
Tras introducir el PIN, el asistente te lo muestra **una sola vez** para que lo memorices. Esta es tu última oportunidad de verlo en claro.
*Apunta el PIN o memorízalo. Cuando lo tengas, pulsa **Derecha**.*
***
## Paso 9 — Página 7: Desbloqueo
Te explica que a partir de ahora cada vez que conectes el USB tendrás que introducir el PIN. Y que los fallos repetidos activan una espera exponencial.
*Lee la página y pulsa **Derecha** para continuar.*
**Backoff exponencial:** primer fallo = 5 s de espera, segundo = 10 s, tercero = 20 s… hasta un máximo de \~43 min. El contador se resetea al meter el PIN correcto. Nada se borra automáticamente.
***
## Paso 10 — Página 8: Cuentas
Vista previa de la pantalla principal — cómo se navegan las credenciales una vez que estés dentro.
*Pulsa **Derecha**.*
***
## Paso 11 — Página 9: Todo listo
Última página. Te recuerda que puedes acceder al menú principal con **Izquierda** estando en la primera credencial (o **Derecha** estando en la última, pasando antes por "Anadir Nuevo").
*Pulsa **Centro** para salir del asistente y llegar a la pantalla de PIN. Ya está — el dispositivo está configurado.*
***
## Paso 12 — Listo para usar
Tras pulsar Centro, el dispositivo te lleva a la pantalla del numpad para introducir el PIN que acabas de crear (la **misma** que verás cada vez que enchufes el dispositivo desde ahora). Cuando metas el PIN correctamente, llegarás a la pantalla principal — pero como no hay credenciales aún, aparecerá directamente la pantalla **Anadir Nuevo**.
*Pulsa **Centro** para crear tu primera credencial — o sigue la guía dedicada en [Crear tu primera credencial](/es/getting-started/first-credential).*
***
## Próximos pasos
Guarda tu primera contraseña paso a paso con el editor del dispositivo.
Conoce las opciones de Tools, Ajustes, Peligro e Info.
# Códigos 2FA (TOTP)
Source: https://docs.zerokeyusb.com/es/getting-started/totp-codes
Cómo introducir la fecha/hora la primera vez, ver el código de 6 dígitos con cuenta atrás y teclearlo automáticamente al ordenador.
ZeroKeyUSB puede generar **códigos TOTP (RFC 6238)** completamente offline — los mismos que verías en Google Authenticator, pero sin móvil. Esta guía cubre el flujo de uso. Para añadir un secreto TOTP a una credencial, ver [Importar credenciales](/es/getting-started/importing-credentials).
**Antes de empezar:** la credencial tiene que tener un secreto TOTP cargado (en el campo `2FA`). Si verás `2FA --` en pantalla, no lo tiene. Carga el secreto desde el web manager o por USB-CDC primero.
***
## Paso 1 — Navegar al campo 2FA
Desde la pantalla principal de la credencial, pulsa **Abajo** hasta llegar al campo 2FA. El icono pasa de candado (Pass) a llave (2FA). Si la credencial tiene secreto cargado, verás `2FA (OK)`. Si no, `2FA --`.
*Con el campo 2FA seleccionado (icono llave invertido), pulsa **Centro** corto para empezar a calcular el código.*
***
## Paso 2 — Introducir la fecha (primera vez de la sesión)
El ATECC608A no tiene RTC. Necesita la fecha y hora **una sola vez** por sesión (entre apagados). El dispositivo te pide primero la fecha en formato `DD/MM/AA`.
| Botón | Acción |
| --------------------- | ------------------------------------------------ |
| **Arriba/Abajo** | Subir/bajar el dígito actual (0–9) |
| **Izquierda/Derecha** | Mover el cursor entre dígitos (saltando los `/`) |
| **Centro** | Confirmar la fecha y pasar a la hora |
*Pon la fecha de hoy y pulsa **Centro**.*
***
## Paso 3 — Introducir la hora
Mismo flujo, ahora con formato `HH:MM` (hora local 24h).
*Mismos botones que en la fecha. Cuando termines, pulsa **Centro** para calcular el código.*
La hora puede ser tu hora local exacta — el dispositivo no aplica zona horaria. Si el servicio espera UTC y tú vives en otro huso, ajusta manualmente.
***
## Paso 4 — Ver el código TOTP
Tras introducir hora y fecha, el ATECC608A calcula el HMAC-SHA1 del secreto + epoch/30 y muestra el resultado de 6 dígitos. La barra inferior se vacía conforme se acerca el final del intervalo de 30 segundos.
*Con el cursor sobre el campo de tu app (donde te pide el código 2FA), pulsa **Centro** corto. El dispositivo teclea los 6 dígitos del código al ordenador como si fueran un teclado normal.*
Si la cuenta atrás llega a 0 antes de que confirmes, el código se regenera automáticamente — no tienes que volver a meter la hora.
***
## Paso 5 — Siguientes códigos en la misma sesión
Mientras el dispositivo siga enchufado, no tendrás que volver a meter fecha y hora. La próxima vez que entres a un campo 2FA (en cualquier credencial), saltarás directamente al paso 4 con un código calculado con el tiempo actual.
*Pulsa **Centro** corto para teclear el código al ordenador, o **Izquierda** para volver a la vista principal de la credencial sin teclear nada.*
***
## Tabla rápida
| Pantalla | Botones útiles |
| ---------------- | ----------------------------------------------------------------- |
| Campo `2FA (OK)` | **Centro** → entrar al flujo TOTP |
| Campo `2FA --` | (sin secreto cargado, nada que hacer) |
| Introducir fecha | **Arr/Abj**: dígito · **Izd/Drc**: cursor · **Centro**: confirmar |
| Introducir hora | Igual que la fecha |
| Ver código | **Centro**: teclear al ordenador · **Izquierda**: salir |
***
## Próximos pasos
Cómo cargar el secreto Base32 de tu servicio en una credencial.
Si aún no tienes credenciales con TOTP, empieza por crear una.
# Elemento seguro ATECC608A
Source: https://docs.zerokeyusb.com/es/hardware/atecc608a
Función, configuración de slots, comandos y propiedades de seguridad del ATECC608A en ZeroKeyUSB.
El **Microchip ATECC608A** (SKU: MAHDA-T) es el elemento seguro hardware en el corazón de la arquitectura de seguridad de ZeroKeyUSB. Proporciona la entropía, identidad, rate-limiting **y el cifrador AES en sí** — cada bloque de credencial se cifra y descifra dentro de este chip.
***
## ¿Por qué un elemento seguro?
El MCU SAMD21 por sí solo no puede proporcionar:
* **Números aleatorios verdaderos** — los MCUs generan números pseudo-aleatorios a partir de semillas software; la calidad es difícil de verificar.
* **Contadores monotónicos resistentes a manipulación** — los contadores software se pueden resetear borrando la EEPROM o reflasheando el firmware.
* **Identidad única del dispositivo** — un serial del chip grabado en el die durante la fabricación proporciona un salt hardware no falsificable.
* **Un almacén de claves resistente a inspección I²C** — una vez bloqueada la zona de datos con `IsSecret=1`, la clave maestra AES no se puede leer, ni siquiera por código que corra en el MCU.
El ATECC608A cubre los cuatro roles. Antes se consideraba que AES en este chip era inalcanzable en los chips `MAHDA-T`; el firmware actual lo habilita durante una rutina de aprovisionamiento única en el primer arranque.
***
## Conexión hardware
| Señal | Pin SAMD21 | Pin ATECC608A |
| ----- | ---------- | ------------- |
| SDA | PA08 | SDA |
| SCL | PA09 | SCL |
| GND | GND | GND |
| VCC | 3,3 V | VCC |
Dirección I²C: **`0x60`**\
Velocidad del bus: **100 kHz** (configurado en el arranque y coincide con el bootloader)
***
## Nota sobre el SKU — MAHDA-T
La variante `MAHDA-T` se entrega con:
* Comando AES hardware **deshabilitado en fábrica** (el byte 13 `AES_Enable` tiene el bit 0 a cero). Los bits 6 y 7 del mismo byte están programados de fábrica; cualquier escritura que intente borrarlos es rechazada por el chip con `SS=0x03` (parse error).
* Varios otros bytes en los primeros 16 de la Config Zone están factory-locked (SN, RevNum). Una escritura de 4 bytes que solape con esos bytes se rechaza por completo.
* Comandos estándar TRNG, Counter, CheckMac y ReadSerial habilitados.
* Configuración de slot por defecto de fábrica: cada slot está desbloqueado, legible y escribible hasta que el aprovisionamiento bloquee las zonas.
El firmware sortea estas peculiaridades durante el aprovisionamiento en el primer arranque:
1. Lee los bloques afectados a RAM.
2. Aplica máscara OR solo a los bits que necesitan cambiar (set `AES_Enable` bit 0, set `SlotConfig[8].IsSecret`, set `SlotConfig[8].WriteConfig=Never`, set `KeyConfig[8].KeyType=AES`).
3. Escribe el bloque entero de 32 bytes de vuelta para que el chip ignore los bytes de solo lectura que hay dentro.
4. Re-lee y verifica que cada modificación tuvo efecto antes de bloquear.
Una vez completado el aprovisionamiento con éxito, el chip ejecuta bloques AES durante el resto de la vida del dispositivo.
***
## Mapa de slots
Establecido por el propio dispositivo al primer arranque y bloqueado permanentemente:
| Slot | Tamaño usado | Propósito | SlotConfig / KeyConfig |
| ----- | ------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| **8** | 16 B (primer sub-key AES) | Clave maestra AES-128 — generada por el TRNG del chip, no sale nunca del chip | `IsSecret=1`, `WriteConfig=Never`, `KeyType=6 (AES)`. El chip se niega a devolver los datos del slot vía `Read`. |
| **9** | 32 B | Clave de PIN: `SHA-256(pinArray[16] ∥ chip_serial[9])` | `IsSecret=0`, `WriteConfig=Always`. La app reescribe el slot cuando el usuario cambia su PIN. Legible por I²C. |
Counter0 existe en el chip pero el firmware **no** lo usa para rate-limiting del PIN (ver la nota al final de esta página); el freno de fuerza bruta se hace con un backoff persistente en EEPROM.
> **Nota de seguridad del Slot 9:** Como `MAHDA-T` rechaza escrituras en claro a slots con IsSecret (slots 0–7), la clave de PIN se almacena en el slot 9 que mantiene `IsSecret=0`. Esto significa que el hash de 32 bytes del PIN es **legible** por I²C por cualquiera con acceso físico. Un adversario podría leer el hash e intentar ataques de diccionario offline contra SHA-256(PIN∥serial). Los PINs cortos (\< 6 dígitos) son particularmente vulnerables a este enfoque.
> **Trade-off del Slot 8:** Poner `WriteConfig=Never` significa que la clave AES no puede regenerarse después de que la zona de datos esté bloqueada. Si el chip falla, todas las credenciales cifradas con esa clave son irrecuperables. El precio es `IsSecret=1` (la clave no puede leerse por I²C). Exporta una copia de seguridad por USB-CDC antes de fiarte del dispositivo para algo importante.
***
## Comandos usados
### `RANDOM` (opcode `0x1B`)
* Mode `0x00`: refresca la semilla DRBG interna con entropía hardware antes de generar 32 bytes aleatorios.
* Usado para generar la clave maestra AES (16 B) dentro del chip y el IV (16 B) al aprovisionamiento. La clave nunca sale del chip — el firmware nunca ve sus bytes; solo el IV se copia a EEPROM.
### `AES` (opcode `0x51`)
* Mode `0x00`: cifra un bloque de 16 bytes usando la clave del slot 8.
* Mode `0x01`: descifra un bloque de 16 bytes usando la clave del slot 8.
* Param2 = `0x0008` (slot 8). El chip mira `KeyConfig[8].KeyType`, confirma que es `6` (AES), usa el sub-key de 16 bytes del slot y ejecuta una ronda AES hardware sobre la entrada.
* Llamado una vez por bloque de 16 bytes por `cbcEncrypt32` / `cbcDecrypt32` en `zerokey-security.cpp`. El encadenamiento CBC se aplica alrededor de estas llamadas en el MCU.
### `LOCK` (opcode `0x17`)
* Mode `0x80`: bloquea la zona Config (saltando comprobación CRC).
* Mode `0x81`: bloquea la zona Data + OTP.
* Usado durante el aprovisionamiento. Una vez ejecutado, la zona elegida no se puede modificar jamás.
### `WRITE` (opcode `0x12`)
* Escritura de 32 bytes en claro a la zona Config (`p1=0x80`) — usada por `provisionAesAndLock()` para configurar `AES_Enable`, `SlotConfig[8]` y `KeyConfig[8]`.
* Escritura de 32 bytes en claro a la zona Data (`p1=0x82`) — usada para rellenar el slot 8 con la clave AES recién generada y para (re)escribir el HMAC del PIN en el slot 9.
* Cada escritura va seguida de un verify por re-lectura para que el firmware aborte sin bloquear si el chip rechazó silenciosamente el cambio.
### `INFO` (opcode `0x30`)
* Mode `0x00`: devuelve la palabra de revisión de 4 bytes.
* Usado como comprobación de vida (`ping()`) para detectar un chip sin aprovisionar o ausente al arrancar.
### `READ` (opcode `0x02`)
* Lectura de bloques de 32 bytes desde la zona Config.
* Usado por `readSerial()` para extraer el serial de 9 bytes del chip (bytes 0–3 y 8–12 del bloque 0 de Config).
* Usado por `readConfigBlock()` (solo aprovisionamiento) y `readLockStatus()`.
### `COUNTER` (opcode `0x24`)
* Mode `0x00` (read): devuelve el valor actual de Counter0.
* Mode `0x01` (increment): incrementa Counter0 atómicamente y devuelve el nuevo valor.
* Monotónico hardware — no hay forma software de decrementarlo. **No lo usa la ruta actual de verificación del PIN** (lo usaba el lockout por Counter0 ya eliminado); disponible para firmware futuro.
### `CHECKMAC` (opcode `0x28`) — definido pero no usado en la ruta de verificación principal
* Computa `SHA-256(slot_key ∥ ClientChallenge ∥ OtherData)` dentro del chip y la compara con una respuesta computada por el host.
* Implementado en `checkMacAgainstPin()` para uso futuro. La `verifySignature()` actual usa una comparación directa de hash en EEPROM.
***
## Protocolo de wake / sleep
El ATECC608A usa una secuencia de wake I²C no estándar:
1. Llevar SDA a bajo durante > 60 µs. Conseguido direccionando `0x00` a 100 kHz (NACK ignorado esperado).
2. Esperar ≥ 1,5 ms (t\_WHI).
3. Leer la respuesta de wake de 4 bytes: esperar `[0x04, 0x11, CRC_lo, CRC_hi]`.
4. Validar CRC-16 (poli `0x8005`, init `0x0000`, sin reflexión) sobre los 2 primeros bytes.
Todos los comandos siguen el patrón: `wake()` → `execute()` → `sleep()`. El chip vuelve a sleep de bajo consumo tras cada operación.
***
## Derivación de la clave del PIN
```
derivePinKey(pin_bytes[16], out[32]):
serial[9] = ATECC608A.readSerial()
buf[25] = pin_bytes[16] || serial[9]
out[32] = SHA-256(buf)
```
* `pin_bytes` son los valores crudos de dígitos de `pinArray[16]` (cada byte = 0–9 de la entrada táctil).
* `serial` es el identificador único de 9 bytes del dispositivo (irreversible, programado en fábrica).
* La misma fórmula la usa el **kit de aprovisionamiento** al escribir el slot 9, garantizando que la app y el chip estén de acuerdo.
Como `serial` es único por chip, el mismo PIN en dos dispositivos ZeroKeyUSB diferentes produce claves de 32 bytes completamente diferentes.
***
## Rate-limiting del PIN (sin lockout por Counter0)
Un diseño anterior usaba Counter0 como límite hard destructivo: un umbral de `cur_counter + 50` en EEPROM `0x0020`, y un borrado (`eraseAll()`) cuando el contador lo cruzaba tras 50 PINs incorrectos. **Ese mecanismo se eliminó.** `verifySignature()` ya no incrementa Counter0 ni lee ningún umbral, y los PINs incorrectos nunca borran la bóveda.
La defensa real es un **backoff exponencial persistente**: un contador de intentos fallidos en EEPROM `0x0002` hace crecer el retardo hasta ≈ 43 minutos, y `waitFromEeprom()` se llama en cada arranque antes de que la pantalla de PIN acepte entrada, así que la penalización no se puede saltar apagando y encendiendo. `eraseAll()` sigue existiendo pero solo corre en un reset de fábrica iniciado por el usuario. Ver [Verificación del PIN](/es/firmware/security/pin-verification) para el flujo completo.
El comando `COUNTER` y Counter0 siguen disponibles en el chip (documentados arriba) y podrían reactivarse en firmware futuro, pero no forman parte de la ruta de verificación actual.
***
## Protocolo CRC
Todos los paquetes de comando del ATECC608A usan un CRC-16 personalizado:
* Polinomio: `0x8005`
* Valor inicial: `0x0000`
* Sin reflexión de entrada/salida
* Sin XOR final
El CRC cubre los bytes del paquete desde `count` hasta el último byte de datos, excluyendo los propios bytes CRC. El CRC de respuesta cubre del byte 0 (`count`) al último byte de datos.
***
## Estado de bloqueo
`getLockStatus()` lee el bloque 2 de la Config Zone (bytes 64–95):
* Byte 86 (`LockValue`): `0x55` = zona Data+OTP desbloqueada; cualquier otro valor = bloqueada.
* Byte 87 (`LockConfig`): `0x55` = zona Config desbloqueada; cualquier otro valor = bloqueada.
Un dispositivo totalmente aprovisionado tiene ambas zonas bloqueadas. La rutina de aprovisionamiento bloquea primero la zona Config (tras escribir `AES_Enable`, `SlotConfig[8]`, `KeyConfig[8]`), luego escribe la clave AES aleatoria en el slot 8, y finalmente bloquea la zona Data.
Si el firmware arranca un chip con ambas zonas bloqueadas pero `KeyConfig[8].KeyType ≠ 6`, se para con `CHIP BRICKED KT=` en el OLED en lugar de dejar que las llamadas AES posteriores fallen con códigos de estado opacos. Ese estado significa que una versión anterior del firmware bloqueó el chip con una configuración inválida de clave AES; el chip es físicamente irrecuperable.
# Pantalla: OLED SSD1306
Source: https://docs.zerokeyusb.com/es/hardware/display-ssd1306
Conexión, presupuesto de energía y uso desde firmware del display monocromo 128×32.
ZeroKeyUSB usa un **módulo OLED de 0,91" basado en SSD1306** para mostrar menús, credenciales e iconos de estado. La pantalla es brillante, de bajo consumo y legible desde múltiples ángulos — ideal para echar un vistazo rápido durante un login.
***
## Características eléctricas
| Parámetro | Valor |
| ----------------------- | ------------------------ |
| Resolución | 128 × 32 píxeles |
| Interfaz | I²C (dirección `0x3C`) |
| Voltaje de alimentación | 3,3 V |
| Corriente típica | 10–12 mA a brillo máximo |
| Controlador | Solomon Systech SSD1306 |
El módulo se conecta directamente al bus I²C SERCOM3 del SAMD21, compartido con la EEPROM externa. Las resistencias pull-up (4,7 kΩ) están en el PCB, así que las resistencias del módulo breakout deberían desactivarse al ensamblar.
***
## Asignación de pines
| Pin OLED | Señal | Notas |
| -------- | ------------ | ---------------------------------------------------- |
| VCC | 3V3 | Alimentado desde el regulador del MCU |
| GND | GND | Tierra común |
| SCL | PA23 | Reloj I²C compartido |
| SDA | PA22 | Datos I²C compartidos |
| RES | PA14 | Controlado por firmware durante init |
| DC | A nivel bajo | Comando/datos gestionado automáticamente en modo I²C |
| CS | A nivel bajo | No se usa en modo I²C |
El firmware togglea la línea **RES** al arrancar para garantizar una secuencia de boot limpia incluso si la alimentación es inestable.
***
## Estrategia de frame buffer
* El SSD1306 espera datos en **páginas de 8 píxeles verticales**.
* El firmware mantiene un buffer de 512 bytes en SRAM (`128 × 32 / 8`).
* Las actualizaciones usan **escrituras parciales** para minimizar el tráfico I²C cuando solo cambian unos pocos caracteres.
* Un diff simple de double-buffer rastrea las regiones sucias para que el refresco quede por debajo de 5 ms.
Las animaciones como el scroll suave para contraseñas largas se basan en interrupciones de timer que desplazan el buffer entre refrescos.
***
## Control de brillo
* Valor de contraste por defecto: `0x7F` (50%).
* Una opción del menú permite atenuar hasta `0x20` para entornos oscuros.
* Tras 60 segundos de inactividad el firmware envía `DISPLAY OFF` manteniendo los datos en RAM.
* Cualquier entrada táctil o actividad USB enciende la pantalla al instante.
Este enfoque equilibra legibilidad y vida útil del OLED.
***
## Resolución de problemas
| Síntoma | Causa posible | Solución |
| ------------------------------------ | ---------------------------------- | --------------------------------------------------------------------------- |
| Sin imagen, backlight apagada | Pin RES a nivel bajo | Comprueba la soldadura o asegúrate de que el logo de arranque ha terminado. |
| La pantalla parpadea o muestra ruido | Conflicto I²C con la EEPROM | Inspecciona resistencias pull-up y longitud de cable. |
| Ghosting / quemado | Contenido estático a máximo brillo | Reduce el contraste o activa el auto-dim en ajustes. |
Si el OLED necesita reemplazo, cualquier módulo SSD1306 I²C con el mismo orden de pines se puede sustituir sin cambios en el firmware.
# Mapa de memoria EEPROM
Source: https://docs.zerokeyusb.com/es/hardware/eeprom
Cómo ZeroKeyUSB almacena credenciales cifradas y por qué su arquitectura de memoria está diseñada para la máxima seguridad.
## Memoria segura, no solo almacenamiento
ZeroKeyUSB usa una **EEPROM industrial ST M24C64-WMN6TP**, un chip de memoria no volátil de 64 kilobits (8 KB en total).\
Se seleccionó no por capacidad, sino por **fiabilidad e integridad de datos a largo plazo** — crítica para un dispositivo que se espera proteja tus credenciales durante años.
Toda la información dentro de este chip se **cifra en el MCU antes de escribirse**.\
Incluso si la memoria se extrajera físicamente, solo revelaría **bloques de ciphertext** — nunca datos legibles.
***
## Características clave
| Especificación | Descripción |
| ------------------------ | -------------------------------- |
| **Modelo del chip** | ST M24C64-WMN6TP |
| **Capacidad** | 64 Kbit (8 192 bytes) |
| **Interfaz** | I²C, direccionamiento de 2 bytes |
| **Endurance** | > 1 000 000 ciclos de escritura |
| **Retención de datos** | > 40 años |
| **Voltaje de operación** | 1,8 V – 5,5 V |
| **Tamaño de página** | 32 bytes |
Toda la comunicación entre chips usa I²C para acceso a memoria y USB HID para la interacción con el host.\
El bus I²C en sí no está cifrado — en su lugar, **los datos se cifran en firmware antes de la transmisión**, garantizando confidencialidad incluso si el bus fuera interceptado.
***
## Visión general de la estructura interna
La EEPROM de ZeroKeyUSB está dividida en regiones aisladas.\
Cada una sirve una función de seguridad dedicada y se accede exclusivamente a través de rutinas del firmware.
| Rango de direcciones | Tamaño | Propósito |
| -------------------- | ------- | -------------------------------------------------------------------- |
| `0x0000–0x0001` | 2 B | Flags de configuración / marcador de setup |
| `0x0002` | 1 B | **Contador de intentos fallidos** (persistente entre apagados) |
| `0x0005–0x000C` | 8 B | Firma de verificación del PIN |
| `0x0010–0x001F` | 16 B | Vector de inicialización AES (IV) |
| `0x0020–0x03DF` | ≈ 960 B | Metadatos del sistema y TOTP (incluyendo 2 bytes por estado de slot) |
| `0x03E0–0x03EF` | 8 B | Último epoch TOTP (tiempo Unix, 64 bits) |
| `0x0400–0x1FFF` | ≈ 7 KB | Almacenamiento de credenciales cifradas (datos de usuario) |
Cada credencial ocupa **tres páginas de 32 bytes cifradas** (96 B en total):
1. Nombre del sitio / servicio
2. Usuario o email
3. Contraseña
Se usa una cuarta página opcional para el **secreto TOTP** cuando se habilita 2FA.
***
## Segmentación de datos
Almacenar cada campo en una página cifrada separada ofrece ventajas clave:
* 🔐 **Cifrado independiente:** Cada campo (sitio, usuario, contraseña, TOTP) se cifra por separado.
* 🧩 **Sin correlación de patrones:** Incluso credenciales idénticas producen ciphertext distinto.
* 💥 **Aislamiento de corrupción:** Si una página falla, las demás permanecen intactas.
* ⚡ **Escrituras eficientes:** Editar un campo solo reescribe esa página, alargando la vida de la EEPROM.
***
## Metadatos de seguridad
### 🔑 Vector de inicialización (IV)
Un valor único de 16 bytes generado por el TRNG del ATECC608A en la primera configuración.\
Garantiza que incluso datos idénticos cifrados dos veces produzcan ciphertext distinto.
### 🧩 Bloque de firma del PIN
Una huella criptográfica de 8 bytes almacenada en la dirección `0x0005`.\
Permite que ZeroKeyUSB verifique el PIN maestro correcto sin almacenar el PIN en sí.
### 🕒 Contador de intentos fallidos
Almacenado en `0x0002`, este byte rastrea las entradas consecutivas de PIN incorrectas.\
Si un usuario introduce un PIN incorrecto varias veces, el firmware aplica retrasos exponenciales antes del siguiente intento.\
Como el contador se almacena en EEPROM, los timers de lockout persisten incluso tras un ciclo de alimentación o al desconectar el dispositivo.
### ⏱️ Último epoch TOTP
Un timestamp Unix de 64 bits representando la última hora sincronizada.\
Permite generar TOTP offline sin re-sincronizar en cada uso.
***
## Ejemplo de layout de credencial
| Página | Contenido | ¿Cifrado? | Tamaño |
| ------------------ | ----------------------- | --------- | ------------ |
| 0 | Sitio / dominio | ✅ | 32 B |
| 1 | Usuario | ✅ | 32 B |
| 2 | Contraseña | ✅ | 32 B |
| 3 | Secreto TOTP (opcional) | ✅ | 32 B |
| — | — | — | — |
| **Total por slot** | — | — | **96–128 B** |
Caben hasta **64 credenciales** de forma segura en la memoria de 8 KB, según el uso de TOTP.
***
## Integridad de datos y manejo de errores
Cada escritura de EEPROM se confirma a nivel I²C para asegurar el éxito.\
Si una escritura falla o expira, el firmware reintenta automáticamente.\
Los errores persistentes disparan un mensaje en pantalla (`EEPROM Error`) y abortan la operación de forma segura.
ZeroKeyUSB nunca almacena texto plano ni registros parciales — las credenciales están o bien **totalmente cifradas** o **no se escriben en absoluto**.
***
## Por qué importa
Los gestores de contraseñas típicos dependen del almacenamiento del SO y cifrado por software.\
ZeroKeyUSB lo guarda todo en hardware, con:
* Una EEPROM dedicada con **40+ años de retención**.
* Cifrado y generación de IV gestionados por el **microcontrolador SAMD21** + ATECC608A.
* Sin interfaces wireless ni conectividad a Internet que explotar.
Incluso con acceso físico al chip de memoria, los contenidos no se pueden descifrar sin la clave AES correcta (que vive en el ATECC608A) y el IV.
***
La transparencia construye confianza: el mapa de memoria es público para que cualquiera pueda verificar el comportamiento del firmware, pero todas las regiones permanecen cifradas y bloqueadas durante la operación normal.
# MCU: Microchip SAMD21
Source: https://docs.zerokeyusb.com/es/hardware/mcu-samd21
Responsabilidades del microcontrolador, configuración de reloj y uso de periféricos dentro de ZeroKeyUSB.
El **Microchip ATSAMD21G18** es el núcleo de ZeroKeyUSB. Combina una CPU ARM Cortex-M0+ de 32 bits, un controlador USB integrado y suficientes periféricos para coordinar la pantalla, las entradas táctiles y la EEPROM externa.
***
## Especificaciones clave
| Característica | Valor |
| -------------- | ----------------------------------------------------------- |
| CPU | ARM Cortex-M0+ a 48 MHz |
| Flash | 256 KB (el firmware ocupa \~60 KB) |
| SRAM | 32 KB |
| USB | Dispositivo Full-Speed con soporte compuesto HID + CDC |
| GPIO | 38 pines de propósito general |
| Timers | 9 (TC/TCC) usados para PWM, debouncing y temporización TOTP |
| ADC | 12 bits, usado para sampling de entropía del IV |
El firmware se ejecuta desde la flash interna y trabaja completamente desde memoria sin estados de espera, manteniendo la latencia baja incluso al actualizar la pantalla OLED.
***
## Configuración de reloj
1. El **oscilador interno de 8 MHz** alimenta el Digital Frequency Locked Loop (DFLL).
2. El DFLL multiplica a **48 MHz** para la CPU y los periféricos síncronos.
3. El **controlador de reloj genérico** divide los 48 MHz para:
* I²C a 1 MHz (SERCOM3) usado por EEPROM y OLED
* SERCOM1 a 2 MHz para el SPI del controlador táctil
* Referencia de 32 kHz para timing en milisegundos (vía `SysTick`)
Esta configuración equilibra rendimiento con bajo ruido para el sensor táctil.
***
## Asignación de periféricos
| Periférico | SERCOM | Función |
| ---------- | -------- | ------------------------------------------- |
| SERCOM0 | USART | Canal serie CDC (TX/RX en los pads USB) |
| SERCOM1 | SPI | Controlador táctil (TS06) |
| SERCOM2 | I²C | Reservado/cabeceras de debug |
| SERCOM3 | I²C | EEPROM (M24C64-W) + pantalla OLED (SSD1306) |
| SERCOM4 | Sin usar | Disponible para expansiones futuras |
| SERCOM5 | USB | Interfaz USB nativa Full-Speed |
El multiplexor `PORT` asigna cada SERCOM a pines específicos; consulta el diseño en KiCad para los números de pad exactos.
***
## Mapa de memoria
* **Bootloader (8 KB)** – Cargador compatible con UF2 para flasheo de fábrica y actualizaciones de la comunidad.
* **Aplicación (240 KB máx.)** – Firmware de ZeroKeyUSB; actualmente usa menos del 30% de la flash disponible.
* **Emulación EEPROM** no se usa; todos los datos persistentes viven en el M24C64-W externo.
* **Buffers en SRAM**:
* 512 bytes para el frame buffer del OLED
* 128 bytes para los informes HID USB
* 96 bytes de scratch para bloques AES
El linker script reserva espacio de pila para el renderizado de menús anidados y las rutinas criptográficas.
***
## Alimentación y reposo
* El MCU corre en **modo activo** mientras está conectado; el consumo se mantiene por debajo de 25 mA para toda la placa.
* Tras 60 segundos de inactividad el firmware atenúa el OLED y pone la CPU en **Standby** manteniendo USB activo.
* La actividad táctil o USB despierta el chip en menos de 3 ms.
Este comportamiento garantiza interacción responsiva sin exceder los límites de corriente USB.
***
## Responsabilidades del firmware
* Autenticar el PIN maestro usando rutinas AES.
* Orquestar las interacciones de menú, pantalla y táctil.
* Gestionar las operaciones de lectura/escritura de EEPROM con seguimiento de wear-level.
* Generar informes HID USB y procesar comandos CDC.
* Calcular códigos TOTP usando aritmética entera (no se requiere coma flotante).
El SAMD21 ofrece suficiente margen para añadir características como múltiples layouts de teclado o controles de seguridad adicionales sin cambios de hardware.
# Visión general del hardware
Source: https://docs.zerokeyusb.com/es/hardware/overview
Dentro de ZeroKeyUSB — componentes de grado industrial diseñados para seguridad offline y fiabilidad a largo plazo.
## Construido para la confianza
ZeroKeyUSB es un gestor de contraseñas autocontenido y basado en hardware.\
Diseñado con un único objetivo: **proteger tus credenciales sin conectarse jamás a Internet**.
Cada unidad se ensambla, se prueba y se encapsula en **resina epoxy de grado industrial** para evitar manipulación externa, haciéndolo resistente al agua, al polvo y libre de mantenimiento.
***
## Arquitectura del sistema
```mermaid theme={null}
graph TB
USBC["Conector USB-C Alimentación + Datos"]
subgraph PCB["PCB de ZeroKeyUSB"]
MCU["SAMD21E18A ARM Cortex-M0+ 48 MHz / 256 KB Flash"]
ATECC["ATECC608A MAHDA-T Elemento seguro"]
EEPROM["M24C64-WMN6TP EEPROM 64 Kbit"]
OLED["SSD1306 OLED 128×32"]
TS06["TS06 IC táctil 6 canales"]
PADS["5 pads táctiles dorados"]
end
USBC -->|"USB FS"| MCU
MCU -->|"I²C 0x60"| ATECC
MCU -->|"I²C 0x50"| EEPROM
MCU -->|"I²C 0x3C"| OLED
MCU -->|"I²C 0x69"| TS06
TS06 --- PADS
style MCU fill:#dbeafe,stroke:#2563eb,color:#000
style ATECC fill:#fef3c7,stroke:#d97706,color:#000
style EEPROM fill:#dcfce7,stroke:#16a34a,color:#000
style OLED fill:#e0e7ff,stroke:#4f46e5,color:#000
style TS06 fill:#fce7f3,stroke:#db2777,color:#000
```
***
## Lista de componentes
| Componente | Modelo | Dir I²C | Función |
| ---------------------- | ----------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **MCU** | Microchip SAMD21E18A | — | ARM Cortex-M0+ a 48 MHz. Ejecuta el firmware, el cifrado AES-128 CBC, el teclado HID USB y la serie CDC. |
| **Elemento seguro** | Microchip ATECC608A (MAHDA-T) | `0x60` | TRNG hardware para generación de clave/IV, motor AES-128 hardware, serial del chip de 9 bytes como salt del PIN. |
| **EEPROM** | ST M24C64-WMN6TP | `0x50` | Almacenamiento no volátil de 64 Kbit (8 KB). Contiene credenciales cifradas, hash del PIN, IV y metadatos TOTP. >1M ciclos de escritura. |
| **Pantalla** | OLED SSD1306 | `0x3C` | OLED blanco monocromo de 128×32 píxeles. Muestra credenciales, menús, entrada de PIN, códigos TOTP y barras de progreso. |
| **Controlador táctil** | TS06 | `0x69` | IC táctil capacitivo de 6 canales (5 usados). Pads dorados en el PCB para Arriba/Abajo/Izquierda/Derecha/Centro. |
| **USB** | Conector USB-C | — | USB Full-Speed. Alimenta el dispositivo (\~20 mA) y proporciona interfaces de teclado HID y serie CDC. |
| **Write Protect** | GPIO PA01 | — | Pin de protección contra escritura del EEPROM. Se puede poner a alto para bloquear escrituras por hardware. |
***
## Por qué estos componentes
### 🧠 Microcontrolador SAMD21E18A
El procesador ARM Cortex-M0+ equilibra rendimiento, tamaño y eficiencia energética:
* **256 KB Flash** — espacio para firmware, fuentes, 9 layouts de teclado y bitmaps de iconos en PROGMEM.
* **32 KB SRAM** — suficiente para el buffer del display, caché de credenciales y workspace TOTP sin asignación dinámica.
* **USB nativo** — el periférico USB Full-Speed por hardware elimina la necesidad de chips puente USB externos.
* **DSU hardware** — la Data Scrambling Unit ofrece CRC32 hardware para comprobaciones rápidas de integridad del firmware al arranque.
* **Fuse BOOTPROT** — `BOOTPROT=7` bloquea los primeros 16 KB de Flash, evitando que el código de aplicación modifique el bootloader.
### 🔐 Elemento seguro ATECC608A
El ATECC608A proporciona cuatro capacidades que el software por sí solo no puede garantizar:
| Capacidad | Por qué importa |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **TRNG hardware** | Genera la clave maestra AES (16 B, dentro del chip) y el IV (16 B) con entropía hardware verdadera — no pseudo-aleatoria. |
| **Motor AES-128** | Cada bloque de credencial se cifra y descifra con el AES hardware del chip. La clave vive en el slot 8 con `IsSecret=1` y nunca cruza el bus I²C. |
| **Zonas Config y Data bloqueadas** | Al primer arranque el chip bloquea permanentemente ambas zonas (irreversible), sellando la clave AES del slot 8 y la configuración AES-enable para que no se puedan alterar ni leer nunca. (El freno de fuerza bruta del PIN lo gestiona por separado un backoff persistente en EEPROM, no el chip.) |
| **Serial del chip (9 B)** | Identificador único programado de fábrica usado como salt en el hashing del PIN: `SHA-256(PIN ∥ serial)`. El mismo PIN en otro dispositivo produce un hash completamente distinto. |
> El SKU MAHDA-T se entrega con el comando AES hardware deshabilitado. La rutina de aprovisionamiento del primer arranque lo habilita, configura el slot 8 como contenedor de clave AES, genera la clave con el TRNG del chip y bloquea irreversiblemente las zonas Config y Data.
### 💾 EEPROM M24C64-WMN6TP
* **8 KB** de almacenamiento no volátil organizados en páginas de 32 bytes.
* **>1 millón de ciclos de escritura por página** — décadas de uso normal.
* Todos los datos de credenciales se **cifran con AES-128 CBC antes de escribirse** — el bus I²C solo ve ciphertext.
* Consciente de límites de página: el firmware divide las escrituras que cruzan límites de 32 bytes para evitar el wrap-around de direcciones del M24C64.
### 🖐️ Controlador táctil TS06
* **IC táctil capacitivo sellado de seis canales** (cinco activamente usados).
* Calibración interna de baseline — no requiere ajuste analógico.
* Sensibilidad mínima (`0x3F`) puesta al arranque para evitar disparos falsos a través del encapsulado epoxy.
* Debounce 80 ms, umbral de pulsación larga 800 ms, lockout de canal 150 ms — todo gestionado en firmware.
### 💡 OLED SSD1306
* **128×32 píxeles**, blanco sobre negro, alto contraste.
* Conectado vía I²C en la dirección `0x3C`.
* Refresco de frame completo (\~512 bytes por frame) vía librería `Adafruit_SSD1306`.
* Excelente visibilidad tanto a la luz del día como en la oscuridad.
* Protegido tras la ventana sellada de epoxy.
### ⚡ Conexión USB-C
* Consumo aproximado de **20 mA** — similar a un ratón inalámbrico.
* **Sin batería** — totalmente alimentado desde el puerto USB del host.
* **Sin wireless** — no hay hardware Wi-Fi, Bluetooth ni NFC en el PCB.
* Funciona con Windows, macOS, Linux, Android e iPadOS.
***
## Bus I²C
Todos los periféricos comparten un único bus I²C a **100 kHz**:
| Dispositivo | Dirección | Función |
| ------------- | --------- | ------------------------------ |
| OLED SSD1306 | `0x3C` | Pantalla |
| EEPROM M24C64 | `0x50` | Almacenamiento de credenciales |
| ATECC608A | `0x60` | Elemento seguro |
| TS06 | `0x69` | Controlador táctil |
SDA y SCL están en **PA08** y **PA09** respectivamente. Hay resistencias pull-up externas en el PCB.
***
## Diseño físico
* **Encapsulado en resina epoxy** — previene corrosión, polvo, humedad y manipulación física.
* **Sin interfaces wireless** — elimina por completo las superficies de ataque remoto.
* **Sin tornillos ni juntas externas** — el dispositivo no se puede abrir de forma no destructiva.
* **Pads táctiles dorados** — duraderos, resistentes a la corrosión y visibles a través de la resina.
***
## Transparencia, no exposición
ZeroKeyUSB es **totalmente open source**. El firmware y los esquemas de hardware están públicamente disponibles en\
[GitHub → Depbit-lab/zerokeyusb](https://github.com/Depbit-lab/zerokeyusb).\
Cualquiera puede verificar exactamente qué código corre en su dispositivo.
Las actualizaciones de firmware requieren **acceso físico** — vía pogo pins SWD o el bootloader USB con firmware firmado. No existe ningún mecanismo de actualización remota.
ZeroKeyUSB es un producto sellado — abrir o reprogramar el dispositivo invalida la garantía y destruye el encapsulado epoxy.
# Sensor táctil
Source: https://docs.zerokeyusb.com/es/hardware/touch-sensor
Los cinco puntos dorados que sustituyen a los botones — una interfaz duradera e intuitiva para un control sin fisuras.
## Diseñado para durar
ZeroKeyUSB **no tiene partes móviles**.\
En lugar de botones o interruptores frágiles, usa un **controlador táctil capacitivo de seis canales (TS06)** que detecta el contacto preciso del dedo a través de la superficie de resina del dispositivo.
El resultado: una **interfaz sellada y a prueba de desgaste** que permanece perfectamente sensible incluso después de años de uso diario.
***
## Cómo funciona
El **controlador táctil TS06** monitoriza continuamente pequeños cambios eléctricos en cinco puntos dorados de contacto situados en la superficie frontal del dispositivo.\
Cuando tu dedo se acerca, el chip detecta un cambio de capacitancia — identificando al instante qué área se ha tocado.
Esta detección ocurre **miles de veces por segundo**, permitiendo una navegación fluida sin retraso ni disparos falsos.
***
## Distribución táctil
Los cinco puntos dorados están dispuestos ergonómicamente en cruz:
| Dirección | Función |
| ----------------- | --------------------------------- |
| **Arriba (↑)** | Scroll o cambiar carácter |
| **Abajo (↓)** | Scroll inverso o decrementar |
| **Izquierda (←)** | Volver / Pantalla anterior |
| **Derecha (→)** | Continuar / Confirmar / Siguiente |
| **Centro (•)** | Seleccionar o ejecutar acción |
El sexto canal oculto se usa internamente para estabilizar las lecturas y filtrar ruido ambiental.
***
## Pulsación corta vs. larga
Cada toque se puede interpretar como **toque corto** o **pulsación larga**, según la duración:
| Tipo de pulsación | Tiempo | Uso típico |
| ------------------- | --------- | --------------------------------------------------------------------------------------------------- |
| **Pulsación corta** | \< 800 ms | Mover, seleccionar, confirmar |
| **Pulsación larga** | > 800 ms | Disparar acciones especiales (p. ej., abrir menú, siguiente credencial, confirmar reset de fábrica) |
El firmware usa un **filtro adaptativo de debounce** para asegurar entrada fiable incluso con dedos mojados o contacto ligero.
***
## Feedback visual
Cuando tocas un punto, la pantalla OLED reacciona al instante con:
* **Animación de resaltado** para la opción seleccionada.
* **Indicador de progreso** para pulsaciones largas.
* **Transiciones suaves** entre pantallas del menú.
Este feedback inmediato ayuda a los usuarios a tener seguridad de que cada acción se ha registrado — incluso sin sonido ni vibración.
***
## Por qué táctil en vez de botones
| Característica | Botones físicos | Sistema táctil de ZeroKeyUSB |
| ----------------------- | ------------------ | ---------------------------------------------------- |
| Desgaste mecánico | Alto | Ninguno |
| Resistencia al agua | Limitada | Totalmente sellado |
| Protección contra polvo | Requiere juntas | Encapsulado herméticamente |
| Ruido | Click audible | Silencioso |
| Vida útil | \~100k pulsaciones | Prácticamente ilimitada |
| Flexibilidad de diseño | Fija | Detección capacitiva invisible a través de la resina |
***
## Sensibilidad adaptativa
ZeroKeyUSB calibra automáticamente la sensibilidad táctil durante el arranque.\
Esto garantiza un rendimiento estable en distintos entornos — ya sea en aire seco, en condiciones húmedas o incluso con guantes ligeros.
El firmware ajusta dinámicamente los umbrales para rechazar toques accidentales de objetos cercanos o ruido eléctrico.
***
## Consumo energético mínimo
A pesar de escanear todos los canales continuamente, el sistema táctil consume solo unos pocos microamperios.\
Esto permite que ZeroKeyUSB siga siendo **altamente responsivo** mientras se mantiene **extremadamente eficiente energéticamente** — perfecto para un dispositivo USB siempre listo.
***
## Construido para durar
Como no hay partes mecánicas, la interfaz táctil contribuye directamente a la vida útil y fiabilidad del dispositivo.\
Incluso después de años de uso, la respuesta sigue siendo idéntica al primer día.
Combinado con el encapsulado a prueba de agua, este diseño garantiza que **ZeroKeyUSB seguirá funcionando perfectamente mucho después de que la mayoría de los dispositivos electrónicos fallen**.
***
La interfaz táctil se diseñó para interacción humana — ignora electricidad estática, humedad o contactos aleatorios de objetos.\
Solo se reconocen los toques intencionados.
# Interfaz USB
Source: https://docs.zerokeyusb.com/es/hardware/usb-interface
Cómo ZeroKeyUSB arranca, se enumera y asegura las comunicaciones por USB-C.
ZeroKeyUSB se conecta a través de un **receptáculo USB-C** pero se comporta como un dispositivo USB 2.0 full-speed clásico. La placa mantiene el cableado simple para que cualquier host USB-A o USB-C pueda alimentarse y comunicarse con la llave.
***
## Cableado del conector
| Pin | Función | Notas |
| ------ | ------- | ---------------------------------------------- |
| A1/B12 | GND | Unidos para cables simétricos |
| A4/B9 | VBUS | Entrada 5 V (hasta 500 mA) |
| A5 | CC1 | Pull-down 5,1 kΩ (Rd) anuncia modo dispositivo |
| B5 | CC2 | Pull-down 5,1 kΩ (Rd) para cables reversibles |
| A6/B6 | D+ | Conectado a los pines USB del SAMD21 |
| A7/B7 | D− | Conectado a los pines USB del SAMD21 |
No se usan pares de alta velocidad ni pines USB 3.x. La pantalla está conectada a tierra a través de una resistencia de 1 MΩ y un condensador de 10 nF para supresión de ESD.
***
## Ruta de alimentación
* VBUS alimenta un **regulador LDO de 3,3 V** (TPS73533) que alimenta el SAMD21, el OLED, el controlador táctil y la EEPROM.
* La corriente total se mantiene por debajo de **120 mA** durante las animaciones del OLED, dentro de los límites USB 2.0.
* La protección contra corriente inversa evita alimentar el host cuando el dispositivo está apagado.
* Un fusible PTC resetable (250 mA) añade protección contra cortocircuitos.
Como ZeroKeyUSB no tiene batería, desenchufar el cable corta inmediatamente la alimentación y limpia la RAM volátil.
***
## Descriptores USB
| Interfaz | Clase | Propósito |
| ---------- | ------------------ | ----------------------------------------------------------------- |
| Interfaz 0 | Teclado HID (0x03) | Teclea automáticamente usuarios y contraseñas |
| Interfaz 1 | CDC ACM (0x02) | Consola serie para backups, diagnósticos y sincronización horaria |
Cada interfaz tiene su propio par de endpoints, permitiendo comunicación simultánea de teclado y serie sin re-enumeración.
***
## Consideraciones de seguridad
* El firmware **ignora peticiones de control específicas de fabricante** y solo responde a descriptores USB estándar.
* Los informes HID se generan solamente a partir de acciones confirmadas por el usuario; no hay tecleo disparado por host.
* Los comandos CDC requieren que el dispositivo esté desbloqueado y, para acciones destructivas, una confirmación de pulsación larga.
* El suspend USB dispara un bloqueo inmediato tras 30 segundos de inactividad.
Estas salvaguardas garantizan que enchufar la llave en un host desconocido no exponga los secretos almacenados.
***
## Resolución de problemas de enumeración
| Síntoma | Causa | Solución |
| --------------------------------------------- | ------------------------------------------ | -------------------------------------------------------------- |
| El dispositivo se alimenta pero no se detecta | Resistencias CC ausentes o incorrectas | Verifica los pull-downs de 5,1 kΩ en CC1/CC2. |
| Se enumera como "Dispositivo desconocido" | Firmware no en ejecución o modo bootloader | Reflashear vía UF2 o comprobar reset por doble pulsación. |
| Puerto serie no aparece | Driver CDC bloqueado | En Windows instala el `.inf` provisto en el repo del firmware. |
Si las líneas de datos USB están dañadas, el dispositivo aún puede encenderse, pero no se tecleará ninguna credencial. Inspecciona el conector por residuos o estrés mecánico.
# Documentación de ZeroKeyUSB
Source: https://docs.zerokeyusb.com/es/index
Centro principal de guías, especificaciones de hardware, internals del firmware y recursos de soporte.
## Bienvenida
**ZeroKeyUSB** es un gestor de contraseñas independiente y basado en hardware, diseñado para mantener tus credenciales completamente offline.\
Se comporta como un teclado USB: teclea tus usuarios cifrados, contraseñas y códigos TOTP opcionales donde los necesites.\
Sin apps. Sin nube. Sin suscripciones.
Todo lo que necesitas para **montar el hardware**, **entender el firmware** y **mantener tu dispositivo seguro** está organizado a continuación.
Aprende cómo flashear el firmware, fijar tu PIN maestro y empezar a usar ZeroKeyUSB de forma segura.
***
## Funciones principales
Protege tus datos con **cifrado AES-128 CBC**. La clave se genera con el TRNG del ATECC608A y permanece dentro del slot 8 del chip — el cifrado y descifrado se ejecutan en el motor AES hardware del elemento seguro, sin exponer nunca la clave al MCU.\
El PIN se verifica vía SHA-256 y un contador monotónico hardware. No requiere conexión a Internet nunca.
Un elemento seguro **ATECC608A** proporciona números aleatorios verdaderos de calidad hardware, un **contador monotónico** resistente a manipulación para el rate-limiting del PIN, y un serial único del chip usado como salt del PIN.
Construido alrededor de un microcontrolador **SAMD21E18A** y una EEPROM **M24C64-WMN6TP**, completamente alimentado por **USB-C**.\
Sin baterías, sin chips wireless — verdaderamente air-gapped.
Actúa como un **teclado HID USB** estándar con soporte para 9 layouts de idioma (EN-US, DE, FR, ES, IT, PT, SV, DA, HU) para teclear credenciales en cualquier campo enfocado.\
Funciona en todos los sistemas operativos importantes.
Navega las credenciales almacenadas y los códigos 2FA en una **pantalla OLED SSD1306 de 128×32 píxeles** con autoscroll para textos largos.
Genera códigos 2FA de 6 dígitos **localmente y offline**.\
Requiere una **sincronización horaria** única desde el host vía USB — nunca a través de Internet.
Cada línea de firmware y cada esquema de hardware es público.\
Audita, verifica y contribuye para mejorar el dispositivo.
Almacena hasta **61 entradas de sitio/usuario/contraseña** más secretos TOTP opcionales, todas cifradas con AES-128 CBC en reposo en la EEPROM externa.
***
## Seguridad de un vistazo
| Capa | Tecnología | Ubicación |
| ---------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------ |
| Cifrado | AES-128 ECB (hardware, comando `AES` del ATECC608A) + encadenamiento CBC software | ATECC608A + MCU SAMD21 |
| Clave de cifrado | 16 B aleatorios (TRNG del ATECC608A, escritos por el chip al primer arranque) | Slot 8 del ATECC608A (IsSecret=1, nunca legible) |
| IV | 16 B aleatorios (TRNG del ATECC608A) | EEPROM `0x0010` |
| Hashing del PIN | SHA-256(PIN ∥ chip\_serial) | MCU (software) |
| Salt del PIN | Serial del chip de 9 bytes | Config Zone del ATECC608A |
| Rate-limit del PIN | Backoff exponencial persistente, reaplicado al arrancar (sin borrado) | EEPROM `0x0002` |
| Integridad de arranque | Verificación de firma ECDSA P-256 | Bootloader |
| Protección física | Encapsulado en resina epoxy + fuse BOOTPROT | PCB / fuses del SAMD21 |
***
## Resumen de la documentación
Guía paso a paso para flashear el firmware, crear tu PIN maestro y almacenar tus primeras credenciales.
Esquemas eléctricos, lista de componentes y mapa de memoria EEPROM para el **M24C64-WMN6TP**.
Explora cómo interactúan los módulos: display, navegación por menús, almacenamiento EEPROM, salida de teclado y generación TOTP.
Entiende cómo el cifrado AES-128 CBC, la verificación de PIN asistida por el ATECC608A, la generación del IV y el rate-limiting hardware aseguran tus datos.
Certificaciones, licencias y directrices de contribución de la comunidad.
Preguntas frecuentes, pasos de resolución de problemas y cómo contactar con la comunidad de ZeroKeyUSB o solicitar ayuda profesional.
***
## ¿Necesitas ayuda?
Encuentra guías de usuario, actualizaciones y recursos de la comunidad para ZeroKeyUSB.
# Open source y transparencia
Source: https://docs.zerokeyusb.com/es/open-source
Cómo publicamos el firmware, esquemas y proceso de build para que puedas verificar — no confiar ciegamente — en ZeroKeyUSB.
## Filosofía
ZeroKeyUSB es intencionadamente **offline y cerrado a modificación**, pero **abierto a inspección**.
Publicar el firmware completo y la documentación de hardware permite a cualquiera auditar el modelo de seguridad mientras mantiene los dispositivos de producción sellados y resistentes a manipulación.
***
## Visión general del repositorio
Todos los materiales públicos viven en el repositorio [Depbit-lab/zerokeyusb](https://github.com/Depbit-lab/zerokeyusb).
Encontrarás:
* `firmware/` → Código fuente C++ para la aplicación del SAMD21, incluyendo helpers de crypto y drivers de dispositivos.
* `hardware/` → Esquemas, layout del PCB y ficheros BOM para cada revisión de hardware.
* `tests/` → Tests unitarios que validan rutinas AES, transacciones EEPROM y cálculos TOTP.
* `docs/` → Guías Markdown que reflejan esta base de conocimiento.
Cada release etiquetado incluye el binario de firmware firmado (`zerokeyusb-vX.Y.Z.bin`) y checksums SHA-256 para verificación independiente.
***
## Builds reproducibles
Publicamos la configuración exacta del toolchain usada en fábrica:
```bash theme={null}
docker pull ghcr.io/depbit-lab/zerokeyusb-toolchain:latest
docker run --rm -v "$PWD":/project ghcr.io/depbit-lab/zerokeyusb-toolchain make release
```
* El contenedor incluye ARM GCC, openocd y todas las dependencias fijadas.
* Ejecutar `make release` produce una imagen de firmware idéntica a la oficial (checksum coincidente).
* Los artefactos del build incluyen un manifiesto con commit de git, timestamp de build y flags del compilador.
***
## Contribuciones security-first
Damos la bienvenida a issues y pull requests que mejoren documentación, testing o herramientas.
Para mantener el firmware de producción auditable:
1. El desarrollo ocurre en ramas feature.
2. Cada cambio requiere dos reviews de mantenedores enfocados en impacto de seguridad.
3. CI ejecuta tests unitarios y análisis estático (cppcheck, clang-tidy) en cada commit.
4. Los release candidates pasan por pruebas hardware manuales antes de crear un nuevo tag.
Nunca se flashea firmware sin firmar a los dispositivos de los clientes.
***
## Verificar tu dispositivo
Puedes confirmar que tu ZeroKeyUSB corre el firmware firmado oficialmente:
1. Comprueba la versión del firmware en **Menú → Settings → About**.
2. Descarga el binario del release correspondiente desde GitHub y calcula su hash SHA-256.
3. Compáralo contra el checksum impreso en las release notes.
4. (Opcional) Si tienes herramientas de fábrica, puedes leer la memoria flash y verificar el bloque de firma — el repositorio documenta el proceso.
Esta transparencia te da confianza de que lo que auditas es exactamente lo que se entrega.
***
## Canales de comunidad
* **Issues** → Reporta bugs, propón funciones o pide aclaraciones.
* **Discussions** → Comparte trucos, scripts de automatización o habla sobre backups auto-hospedados.
* **Buzón de seguridad** → Envía email a `security@zerokeyusb.com` para divulgación coordinada de vulnerabilidades.
Creemos que la confianza se gana. La documentación abierta y los builds reproducibles son nuestra forma de demostrarlo.
***
Open source no significa firmware modificable en unidades de venta. El código publicado es para transparencia, auditorías y propósitos educativos.
# Primeros pasos
Source: https://docs.zerokeyusb.com/es/quickstart
Aprende a usar ZeroKeyUSB pantalla a pantalla. Tutorial inicial, crear y editar credenciales, 2FA, copias de seguridad y más.
## Bienvenido a ZeroKeyUSB
Esta sección te guía pantalla a pantalla, con ilustraciones del dispositivo y los botones que tienes que pulsar en cada paso. No necesitas conocimientos técnicos previos — solo el dispositivo y un cable USB-C.
***
## Los cinco botones
Todo se controla con cinco pads dorados dispuestos en cruz, a la derecha de la pantalla:
| Pad | Símbolo | Toque corto | Pulsación larga (\~800 ms) |
| ------------- | ------- | -------------------------------------------- | ------------------------------- |
| **Arriba** | ⬆ | Mover/scroll arriba, subir dígito | — |
| **Abajo** | ⬇ | Mover/scroll abajo, bajar dígito | — |
| **Izquierda** | ⬅ | Volver, retroceder dígito | Saltar 10 credenciales atrás |
| **Derecha** | ➡ | Avanzar, añadir dígito | Saltar 10 credenciales adelante |
| **Centro** | ● | Confirmar, seleccionar, teclear al ordenador | Editar el campo actual |
A lo largo de las guías verás el botón resaltado en **cian** sobre las ilustraciones. Eso indica el botón que tienes que pulsar para pasar al siguiente paso.
***
## Índice de escenarios
Recorrido completo del asistente que aparece la primera vez que enchufas el dispositivo: orientación, layout del teclado y creación del PIN maestro.
Desde desbloquear el PIN hasta guardar tu primera contraseña usando la pantalla "Anadir Nuevo" y el editor del dispositivo.
Cómo entrar al editor, mover el cursor, usar las tres páginas de teclado, generar contraseñas aleatorias y guardar.
Cómo introducir la fecha y hora la primera vez, ver el código de 6 dígitos con cuenta atrás y teclearlo automáticamente al ordenador.
Cómo acceder al menú principal desde la lista de credenciales y moverte por Tools, Ajustes, Peligro e Info.
Exportar todas tus credenciales como CSV cifrado por hardware. Cuándo hacerlo y cómo guardarlas de forma segura.
Recuperar credenciales desde una copia de seguridad anterior o migrar de otro gestor de contraseñas.
***
## Antes de empezar
Enchufa ZeroKeyUSB en cualquier puerto USB-C de tu ordenador, tablet o móvil. El dispositivo consume unos 20 mA — menos que un ratón inalámbrico — y no necesita batería.
Si los puntos dorados quedan a tu izquierda en lugar de a la derecha, simplemente dale la vuelta al dispositivo. Durante el tutorial inicial podrás girar también la pantalla por software.
Antes de empezar el tutorial inicial conviene tener pensado un PIN de 4 a 16 dígitos. Si lo olvidas, **no hay forma de recuperarlo** — solo borrar y volver a empezar.
***
## Convenciones de estas guías
| Notación | Significado |
| ---------------------------- | ----------------------------------------------------------------------------- |
| **Pulsar X** | Toque corto en el botón X (menos de 800 ms) |
| **Mantener X** | Pulsación larga en el botón X (más de 800 ms — verás el anillo cian aparecer) |
| Botón cian en la ilustración | Es el botón que tienes que pulsar para avanzar al siguiente paso |
| Botón cian con halo grande | Pulsación larga |
| `texto monoespaciado` | Texto que aparece literalmente en la pantalla OLED |
¿Quieres probar antes de tener el dispositivo en la mano? Abre [`Animaciones/simulador.html`](https://github.com/Depbit-lab/zerokeyusb) en cualquier navegador moderno: emula la pantalla y los botones con el teclado (W/A/S/D/flechas/Espacio).
# FAQ
Source: https://docs.zerokeyusb.com/es/support/faq
Respuestas a las preguntas más comunes sobre ZeroKeyUSB — seguridad, compatibilidad y uso diario.
## General
### 🧩 ¿Qué es ZeroKeyUSB?
ZeroKeyUSB es un **gestor de contraseñas hardware** que guarda tus credenciales completamente **offline**.\
Se comporta como un teclado USB normal: cuando seleccionas una cuenta guardada, simplemente **teclea tus datos de login** automáticamente — sin software ni conexión a Internet.
***
### 🔌 ¿Necesita una app o suscripción?
No.\
ZeroKeyUSB no depende de software, extensiones ni suscripciones.\
Funciona al instante cuando lo enchufas a cualquier ordenador, móvil o tablet que acepte un teclado USB.
***
### 💻 ¿Con qué dispositivos es compatible?
ZeroKeyUSB funciona universalmente con:
* Windows
* macOS
* Linux
* Android (vía USB-C o adaptador)
* iPadOS (modelos USB-C)
Como emula un teclado estándar, funciona allá donde puedas teclear.
***
### 🔋 ¿Tiene batería?
No.\
ZeroKeyUSB consume una mínima cantidad de energía (alrededor de 20 mA) directamente del puerto USB.\
Esto lo hace **libre de mantenimiento** y garantiza que tus credenciales estén siempre disponibles — incluso años después.
***
### 🧑💻 ¿Cuántas credenciales puede guardar?
Hasta **61 credenciales cifradas**, cada una conteniendo:
* Nombre de sitio web o servicio (hasta 32 caracteres)
* Usuario o email (hasta 32 caracteres)
* Contraseña (hasta 32 caracteres)
* (Opcional) Secreto 2FA TOTP
***
## Seguridad
### 🔐 ¿Cómo se protegen mis contraseñas?
Tus credenciales están protegidas por **tres capas**:
1. **Cifrado AES-128 CBC** — cada credencial se cifra con una clave de 128 bits generada por el generador de números aleatorios hardware del ATECC608A. Esta clave es única para tu dispositivo.
2. **Verificación de PIN** — tu PIN maestro se hashea con SHA-256 usando el número de serie único del chip como salt. El hash se compara a tiempo constante para evitar ataques de timing.
3. **Rate-limiting persistente** — cada PIN incorrecto dispara un retardo exponencial (hasta ≈ 43 minutos) guardado en EEPROM y reaplicado en cada arranque, así que no se puede saltar apagando y encendiendo. Los intentos se frenan hasta hacerlos impracticables, sin destruir jamás tus datos.
Incluso si alguien extrajera físicamente el chip de memoria, la clave AES requerida para descifrar los datos fue generada por el TRNG hardware y no se deriva de tu PIN.
***
### 🧠 ¿Qué pasa si olvido mi PIN?
Por razones de seguridad, **no hay recuperación de PIN**.\
La única opción es un **Reset de fábrica**, que borra todos los datos cifrados y te permite crear un nuevo PIN.\
Esto garantiza que nadie — ni siquiera el fabricante — pueda acceder a tu información.
> 💡 Consejo: Elige un PIN memorable y mantén un backup cifrado de tus credenciales.
***
### 🕐 ¿Se conecta a Internet?
Jamás.\
ZeroKeyUSB es un **sistema totalmente offline**.\
No tiene módulos Wi-Fi, Bluetooth ni NFC, y nunca intercambia datos con servidores externos.\
Tú eres el único que puede acceder a la información almacenada.
***
### 💾 ¿Puede alguien clonar mi dispositivo?
No.\
Cada ZeroKeyUSB contiene un **elemento seguro ATECC608A** con un número de serie único de 9 bytes programado de fábrica. Este serial se usa como salt en el hash del PIN, lo que significa que el mismo PIN en dos dispositivos diferentes produce claves criptográficas completamente diferentes.\
La clave maestra AES de 128 bits también es única por dispositivo (generada por el TRNG en chip al aprovisionar).
***
### 🚫 ¿Qué pasa tras demasiados intentos de PIN incorrectos?
Dos niveles de protección:
**Backoff soft (capa UX):**
| Intentos fallidos | Tiempo de espera |
| ----------------- | ---------------------- |
| 1 | 5 segundos |
| 2 | 10 segundos |
| 3 | 20 segundos |
| 4 | 40 segundos |
| … | Dobla hasta 43 minutos |
**Aplicación persistente:**
El contador de intentos fallidos vive en EEPROM y el retardo acumulado se reaplica **en cada arranque, antes de que la pantalla de PIN acepte entrada** — así que cortar la corriente a mitad de la cuenta atrás no lo salta. Tras \~10 PINs incorrectos, cada intento adicional cuesta ≈ 43 minutos, lo que hace la fuerza bruta impracticable. Tus datos **nunca se borran automáticamente** por PINs incorrectos; la bóveda solo se borra con un Reset de fábrica iniciado por el usuario.
> Nota: un atacante que abra físicamente el dispositivo y alcance el bus I²C podría leer el hash del PIN y crackearlo offline, saltándose este retardo. Por eso la placa va encapsulada en resina epoxy y por eso importa usar un PIN largo.
***
### 🧰 ¿Se puede actualizar el firmware?
Sí, pero solo con **acceso físico**.\
Desde el menú (**Danger Zone → Bootloader Mode**), el dispositivo reinicia en un bootloader DFU USB.\
El nuevo firmware debe estar **criptográficamente firmado** — el bootloader comprueba tanto un CRC32 como un MAC BLAKE2s antes de aceptar cualquier imagen. El firmware sin firmar dispara un delay de penalización de 15 segundos.
No hay mecanismo de actualización remota ni over-the-air.
***
### 🔍 ¿Es realmente open source?
Sí — para **transparencia y auditabilidad**.\
Publicar el código permite a cualquiera verificar que:
* No hay puertas traseras ni mecanismos de recolección de datos.
* El cifrado sigue estándares establecidos (AES-128, SHA-256, HMAC-SHA1).
* Todas las funciones operan exactamente como se describen.
El dispositivo corre una **versión firmada** del mismo código, verificada por el bootloader en cada arranque.
***
## Uso
### 🌍 El teclado teclea símbolos incorrectos — ¿qué puedo hacer?
Ve a **Menú → Settings → Keyboard** y cicla por los 9 layouts soportados:\
`EN-US`, `DA-DK`, `DE-DE`, `ES-ES`, `FR-FR`, `HU-HU`, `IT-IT`, `PT-PT`, `SV-SE`.
También puedes ajustarlo durante el asistente de configuración inicial.
***
### 🧾 ¿Puedo hacer backup de mis datos?
Sí.\
Usa **Menú → Backup → Export** para enviar todas las credenciales por USB serie en formato CSV en texto plano.\
Más tarde puedes **importar** el mismo backup.
> ⚠️ **Los ficheros de backup son texto plano.** Cífralos con GPG, age o un ZIP protegido con contraseña, y guárdalos offline.
***
### ⏱️ ¿Cómo funciona la característica 2FA (TOTP)?
ZeroKeyUSB puede generar **contraseñas de un solo uso basadas en tiempo (TOTP)** offline.\
Soporta los algoritmos **SHA-1**, **SHA-256** y **SHA-512**.\
Una vez que importas un secreto TOTP y sincronizas el tiempo, el dispositivo muestra un código de 6 dígitos con cuenta atrás de 30 segundos — sin necesidad de tu móvil ni Internet.
***
### 🌙 La pantalla se apagó sola — ¿está rota?
No. Tras **1 minuto sin tocarla**, la pantalla se apaga para cuidar el OLED. El
dispositivo **no se bloquea** — solo toca cualquier pad y se enciende en la misma
pantalla donde lo dejaste. Usar la [extensión de navegador](/es/getting-started/browser-extension)
también la enciende. Tu sesión y el estado del PIN no se tocan.
***
### 🧼 ¿Es resistente al agua?
Sí.\
Cada unidad ZeroKeyUSB está **totalmente encapsulada en resina**, haciéndola resistente al agua, polvo y uso diario.\
No está diseñada para sumergirse, pero sobrevivirá derrames accidentales o exposición a la lluvia.
***
### 🧱 ¿Y si se rompe la pantalla?
Tus datos siguen seguros — siguen cifrados dentro de la EEPROM.\
Sin embargo, tendrás que contactar con soporte para una sustitución, ya que el dispositivo no se puede desmontar sin romper el sello de resina.\
Aún puedes exportar tus credenciales por la interfaz serie USB (el canal CDC funciona sin la pantalla).
***
### 🛡️ ¿Cuánto durará?
ZeroKeyUSB no tiene partes móviles ni baterías.\
La EEPROM está valorada en **>1 millón de ciclos de escritura por página**, y todos los demás componentes son state-solid.\
Con uso normal, debería durar **bastante más de una década**.
***
### 💬 ¿Cómo puedo contactar con soporte?
Para cualquier pregunta, contacta directamente en\
📧 **[support@zerokeyusb.com](mailto:support@zerokeyusb.com)**\
o visita **[zerokeyusb.com/support](https://zerokeyusb.com/support)**
***
ZeroKeyUSB está diseñado para darte tranquilidad — tú eres dueño de tus contraseñas, y tus datos nunca salen de tus manos.
# Resolución de problemas
Source: https://docs.zerokeyusb.com/es/support/troubleshooting
Pasos recomendados para diagnosticar y solucionar problemas comunes con ZeroKeyUSB.
> Sigue las secciones en orden — muchos problemas se resuelven tras completar los pasos previos.
## Antes de empezar
1. Asegúrate de que el dispositivo recibe alimentación estable a través de la conexión USB-C.
2. Confirma que estás usando el **firmware original de fábrica** (las actualizaciones rara vez son necesarias).
3. Toma nota de cualquier mensaje de error en pantalla y cambios de configuración recientes.
4. Si es posible, haz backup de tus credenciales vía el **web manager local** antes de hacer cambios.
***
## Tabla rápida de síntoma y solución
| Síntoma | Causa posible | Acción recomendada |
| ----------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| El dispositivo no enciende | Cable o puerto USB no proporciona alimentación | Prueba otro cable o puerto USB-C; evita hubs USB; conecta directamente a PC o powerbank |
| La pantalla OLED permanece en blanco | Retraso de inicialización del display o EEPROM no responde | Espera 5 segundos tras la conexión; si persiste, reconecta o comprueba las soldaduras de la EEPROM |
| Los botones táctiles no responden | Controlador TS06 no detectado o mal calibrado | Limpia los pads dorados y reconecta; si sigue sin responder, realiza un reset de fábrica |
| `EEPROM ERROR` o `IV MISSING` en pantalla | Comunicación con memoria o corrupción de datos | Power-cycle e intenta; si persiste, contacta soporte para inspección |
| El PIN incorrecto retrasa el acceso | Lockout exponencial disparado tras intentos fallidos | Espera a que termine la cuenta atrás e intenta con el PIN correcto |
| TOTP muestra `REQTIME` | Tiempo no sincronizado con el host | Usa el web manager y pulsa **Sync Time** antes de generar códigos |
***
## Procedimientos paso a paso
### 1. Reinicio seguro
* Desenchufa ZeroKeyUSB.
* Espera unos **5 segundos**.
* Reconéctalo a una fuente de alimentación USB (PC o móvil).\
La pantalla de bienvenida debería aparecer en 3–5 segundos.
### 2. Resetear el controlador táctil
1. Desconecta y reconecta el dispositivo.
2. Espera a que aparezca el logo de **ZeroKeyUSB**.
3. Toca cada pad dorado para confirmar que los cinco responden.\
Si el táctil sigue sin responder, realiza un **reset de fábrica** para recalibrar automáticamente.
### 3. Flasheo de firmware (solo unidades de desarrollo)
> ⚠️ Los dispositivos de producción están encapsulados en resina y no pueden ser reflasheados por el usuario.\
> Esta sección se aplica **solo a placas de pre-producción**.
1. Reconecta rápidamente el cable USB dos veces en menos de un segundo para entrar en modo bootloader.
2. Copia el fichero de firmware `.uf2` al drive llamado **ZEROBOOT**.
3. Espera a que la transferencia termine, luego reconecta normalmente.
4. Verifica la versión del firmware en **Menú → Settings → About**.
### 4. Reset de fábrica y reconfiguración
* Exporta credenciales vía el web manager (**Backup → Export**).
* Mantén pulsado el **pad táctil central** durante unos 10 segundos hasta que aparezca la cuenta atrás de confirmación.
* El proceso borra toda la memoria, incluyendo credenciales, firma del PIN e IV.
* Tras el reinicio, re-importa tu backup o ejecuta el setup inicial de nuevo.
***
## Recoger datos para soporte
* Conecta el dispositivo y abre el **Serial Monitor** a **115200 bps** para capturar la salida de log.
* Haz fotos de cualquier error en pantalla.
* Anota el **número de serie** (`SN ZK-XXXXXXXX`) y la **versión del firmware** desde **Menú → Settings → About**.
***
## Contactar con soporte
Si los problemas persisten:
* Envía un ticket de soporte en [zerokeyusb.com/support](https://zerokeyusb.com/support) incluyendo:
* Número de serie del dispositivo
* Versión del firmware
* Capturas o fotos del problema
* Pasos que ya has probado
* Nuestro equipo responderá en **24 horas laborables** con más orientación.
> ⚠️ **No intentes abrir ni reflashear un dispositivo sellado.**\
> Hacerlo destruirá el encapsulado impermeable e invalidará la garantía.
***
Esta guía cubre diagnóstico a nivel de usuario.\
Para calibración de fábrica o debug avanzado, contacta directamente con los partners de servicio autorizados.
# Firmware Architecture
Source: https://docs.zerokeyusb.com/firmware/architecture
Understand how the ZeroKeyUSB firmware is organized, from hardware drivers to the secure credential manager.
## Modular by design
ZeroKeyUSB firmware is written in C++ for the **Microchip SAMD21E18A** microcontroller (ARM Cortex-M0+, 48 MHz, 256 KB flash, 32 KB SRAM).\
It follows a layered architecture that keeps hardware drivers, security primitives, and the user interface cleanly separated.
```mermaid theme={null}
graph TB
subgraph Application["Application Layer"]
MENU["zerokey-menu.cpp Menu system + wizard"]
IO["zerokey-io.cpp Touch event routing"]
SETUP["zerokey-setup.cpp Boot + config flag"]
end
subgraph Security["Security & Services"]
SEC["zerokey-security.cpp CBC chaining + IV (blocks via ATECC AES)"]
TOTP["zerokey-totp.cpp TOTP code generation"]
ATECC["zerokey-atecc.cpp ATECC608A driver + AES + provisioning"]
end
subgraph Drivers["Hardware Drivers"]
DISP["zerokey-display.cpp SSD1306 OLED (I²C)"]
EEPROM["zerokey-eeprom.cpp M24C64 EEPROM (I²C)"]
USB["Keyboard.h + SerialUSB HID + CDC composite"]
UTILS["zerokey-utils.cpp Screen orient, typing"]
end
subgraph HAL["SAMD21 HAL / CMSIS"]
WIRE["Wire (I²C)"]
USBHAL["USB FS peripheral"]
end
MENU --> SEC
IO --> MENU
IO --> SEC
SETUP --> SEC
SETUP --> DISP
SEC --> ATECC
SEC --> EEPROM
TOTP --> EEPROM
TOTP --> ATECC
DISP --> WIRE
EEPROM --> WIRE
ATECC --> WIRE
USB --> USBHAL
style Application fill:#dbeafe,stroke:#2563eb
style Security fill:#fef3c7,stroke:#d97706
style Drivers fill:#dcfce7,stroke:#16a34a
style HAL fill:#f3e8ff,stroke:#7c3aed
```
Each module can evolve independently while keeping critical security routines auditable and easy to review.
***
## Source file map
| File | Lines | Role |
| ------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `zerokey-security.cpp/.h` | \~750 | AES-CBC chaining around the chip's single-block AES command, PIN verification, erase, backup/restore |
| `zerokey-atecc.cpp/.h` | \~640 | ATECC608A I²C driver: TRNG, Counter, ReadSerial, CheckMac, SHA-256, hardware AES, Lock, and the one-shot AES provisioning routine |
| `zerokey-io.cpp/.h` | \~1561 | Touch event dispatch, TOTP date/time input, serial command handler |
| `zerokey-menu.cpp/.h` | \~1051 | Menu tree, setup wizard (10 pages), confirm/info/activity pages |
| `zerokey-display.cpp/.h` | \~750 | SSD1306 rendering: main screen, PIN screen, editor, progress, scrolling |
| `zerokey-eeprom.cpp/.h` | \~257 | Page read/write, TOTP metadata, keyboard layout, epoch persistence |
| `zerokey-totp.cpp/.h` | \~500 | HMAC-SHA1/SHA256/SHA512, Base32 decode, TOTP code generation |
| `zerokey-setup.cpp/.h` | \~159 | Boot sequence, config flag (`0x42`), TS06 init, hardware probe |
| `zerokey-utils.cpp/.h` | \~500 | Typing engine, screen orientation, error screen, serial number |
| `zerokey-globals.h` | \~317 | Constants, icons (PROGMEM), global variable externs |
| `zerokey-memorymap.h` | \~38 | EEPROM address calculations, credential layout constants |
***
## Boot sequence
```mermaid theme={null}
sequenceDiagram
participant PWR as USB Power
participant BL as Bootloader
participant FW as Firmware
participant HW as Hardware
PWR->>BL: Power-on
BL->>BL: CRC32 + BLAKE2s MAC check
alt Firmware valid
BL->>FW: Jump to application
else Invalid
BL->>BL: 15 s penalty delay
BL->>BL: Enter USB-CDC DFU mode
end
FW->>HW: Wire.begin() — I²C bus at 100 kHz
FW->>HW: Probe TS06 touch controller (5 retries)
FW->>HW: Configure TS06 sensitivity (reg 0x00–0x02 = 0x3F)
FW->>HW: Init SSD1306 OLED at 0x3C
FW->>HW: Read keyboard layout from EEPROM 0x003E
FW->>HW: Read last TOTP epoch from EEPROM 0x0040
FW->>HW: Ping ATECC608A + read lock status
FW->>HW: If first boot — provision AES + lock zones
FW->>HW: AES self-test (encrypt + decrypt round-trip)
FW->>FW: Read config flag from EEPROM 0x0000
alt Flag ≠ 0x42
FW-->>FW: Launch setup wizard
else Flag = 0x42
FW->>FW: Apply pending backoff delay
FW-->>FW: Show PIN screen
end
```
The configuration flag at `0x0000` determines whether the device shows the setup wizard (`flag ≠ 0x42`) or the PIN unlock screen (`flag = 0x42`). The wizard writes `0x42` after successful PIN creation.
***
## Main loop
The firmware runs a **cooperative main loop** — no RTOS, no interrupts for application logic, no dynamic memory allocation:
```mermaid theme={null}
graph LR
A["Poll TS06 touch status"] --> B["Debounce 80 ms threshold"]
B --> C["Dispatch event to current screen"]
C --> D["Update display full frame refresh"]
D --> E["Check SerialUSB for host commands"]
E --> F["Update TOTP millis-based epoch"]
F --> A
style A fill:#fef3c7,stroke:#d97706,color:#000
style D fill:#dbeafe,stroke:#2563eb,color:#000
```
Each iteration is deterministic. Timing-sensitive operations (TOTP countdown, lockout delays) use `millis()` instead of blocking delays.
***
## Screen state machine
Every interactive view is a state identified by a `programPosition` constant. Touch events are dispatched based on this value:
```mermaid theme={null}
stateDiagram-v2
[*] --> SPLASHSCREEN
SPLASHSCREEN --> SETUP : First boot
SPLASHSCREEN --> PIN_SCREEN : Configured
SETUP --> PIN_SCREEN : Wizard complete
PIN_SCREEN --> MAIN_INDEX : PIN correct
PIN_SCREEN --> PIN_SCREEN : PIN wrong + delay
state "Main Views" as main {
MAIN_INDEX --> MAIN_SITE
MAIN_SITE --> MAIN_USER
MAIN_USER --> MAIN_PASS
MAIN_PASS --> MAIN_2FA
MAIN_2FA --> MAIN_INDEX
}
MAIN_SITE --> EDIT : Long-press Center
MAIN_USER --> EDIT : Long-press Center
MAIN_PASS --> EDIT : Long-press Center
EDIT --> MAIN_INDEX : Long-press Center (save)
main --> MENU : Scroll past last slot
MENU --> main : Right or Back
MAIN_2FA --> TOTP_SHOW_CODE : Has secret + time synced
MAIN_2FA --> TOTP_DATE_ENTRY : Has secret, no time
```
***
## I²C bus topology
All peripherals share a single I²C bus:
```mermaid theme={null}
graph LR
MCU["SAMD21 PA08/PA09 I²C Master"]
MCU -->|"0x3C"| OLED["SSD1306 OLED 128×32"]
MCU -->|"0x50"| EEP["M24C64 EEPROM 8 KB"]
MCU -->|"0x60"| ATECC["ATECC608A Secure Element"]
MCU -->|"0x69"| TS06["TS06 Touch Controller"]
style MCU fill:#dbeafe,stroke:#2563eb,color:#000
style ATECC fill:#fef3c7,stroke:#d97706,color:#000
style EEP fill:#dcfce7,stroke:#16a34a,color:#000
```
Bus speed: **100 kHz** (set at boot, matches bootloader).\
The TS06 address is `0xD2 >> 1 = 0x69`.
***
## USB composite device
ZeroKeyUSB enumerates as a **composite USB Full-Speed device** with two interfaces:
| Interface | Class | Purpose |
| ---------------- | ----- | ------------------------------------------------------------------------ |
| **HID Keyboard** | 0x03 | Types credentials to the host — appears as a standard keyboard |
| **CDC Serial** | 0x0A | 115200 bps ASCII protocol for backup/restore, time sync, and diagnostics |
Both interfaces are active simultaneously after boot. The CDC channel requires PIN unlock before accepting any data-modifying commands.
***
## Memory footprint
| Region | Size | Usage |
| ---------- | -------------------------- | ----------------------------------------------------------------------------- |
| **Flash** | 256 KB total, \~64 KB used | Firmware code, fonts, PROGMEM icons, keyboard maps, constant data |
| **SRAM** | 32 KB total, \~16 KB used | UI buffers, `currentSite/User/Pass[16]`, `pinArray[16]`, TOTP workspace |
| **EEPROM** | 8 KB (M24C64) | Encrypted credentials (61 slots × 128 B), IV, PIN hash, config, TOTP metadata |
No dynamic memory allocation (`malloc`/`new`) is used anywhere. All buffers are stack-allocated or static.
***
## Build & verification
* Compiled with **ARM GCC** using the Arduino SAMD core.
* Build process managed by `Makefile` — supports selective compilation and J-Link flashing via `Dashboard.bat`.
* Firmware binary is signed with a **BLAKE2s MAC** and appended with a 28-byte security footer.
* The bootloader verifies this signature at every boot using CRC32 + BLAKE2s before jumping to application code.
* Unsigned or tampered firmware triggers a **15-second penalty delay** and falls into USB-CDC DFU mode.
ZeroKeyUSB runs on a minimal firmware stack: no RTOS, no dynamic memory allocation, and no hidden debug backdoors.\
All tasks are cooperative and time-deterministic — simplicity is treated as a security feature.
# Bitcoin Signer (technical / audit)
Source: https://docs.zerokeyusb.com/firmware/bitcoin-signer
How the airgapped Bitcoin wallet is implemented — entropy source, BIP39/32/84 derivation, encrypted seed storage, PSBT signing and the exact trust boundary — so you can audit it against the source.
This page documents the **implementation** of the Bitcoin signer so it can be
independently audited. For the step‑by‑step user guide see
[Bitcoin Wallet](/getting-started/bitcoin).
All Bitcoin code lives in **`ZerokeyOS/zerokey-bitcoin.cpp`** and the vendored
**uBitcoin** library (`ZerokeyOS/libraries/uBitcoin`, "Bitcoin" by Stepan
Snigirev). Everything below is verifiable in that source.
**Design constraint.** The on‑board **ATECC608A is a secp256r1 (NIST P‑256)**
chip — it *cannot* produce Bitcoin's **secp256k1** signatures. Therefore every
Bitcoin operation (BIP32/39/84 derivation, secp256k1 ECDSA, PSBT) is done in
**software** with uBitcoin. The ATECC is used only as a **hardware TRNG** and,
separately, to protect the AES master key that encrypts the seed.
## Trust boundary
The single most important property: **the seed never leaves the device over
USB.** Only public data and signatures cross the wire.
| Data | Leaves over USB? | Notes |
| ---------------------------------- | ---------------- | ------------------------------------------------------- |
| 12‑word seed / 16‑byte entropy | **Never** | Shown only on the OLED (paginated, 3 words/page) |
| Account public key `zpub` | Yes | Public — safe to export |
| Master key fingerprint | Yes | Public — needed so the signer recognises its own inputs |
| Output descriptor `wpkh(...)` | Yes | Public |
| Signatures (`PSBT_IN_PARTIAL_SIG`) | Yes | Produced only after an on‑device hold‑to‑confirm |
There is **no serial command** that reads the seed, the entropy or the private
key. The only seed egress path is the on‑screen 12‑word viewer (`btcDisplaySeed`),
which draws to the OLED and nothing else.
## 1 · Entropy & RNG
Key material comes exclusively from the **ATECC608A hardware TRNG** (`RANDOM`
command via `zerokeyAtecc.random`).
* `btcGenEntropy()` draws 16 fresh bytes with **up to 5 retries** (the chip can
NAK the first command after sleep), **rejects an all‑zero result**, and
**refuses** (returns `false`, sets `g_btcRngHardwareOk = false`) rather than
ever falling back to a weak source. Seed generation aborts on refusal.
* uBitcoin's Trezor crypto calls the weak `random32()`/`random_buffer()` symbols.
The firmware **overrides** them (`extern "C"` in `zerokey-bitcoin.cpp`) with
versions backed by the ATECC TRNG through a 32‑byte cache.
* If the TRNG ever fails mid‑run, the override sets `g_btcRngHardwareOk = false`
and fills from a **non‑trusted `micros()` mix *only*** to keep non‑key paths
from looping — **never** for seed material (that path has already refused).
```c theme={null}
// zerokey-bitcoin.cpp — the override that replaces Trezor's weak PRNG
extern "C" void random_buffer(uint8_t *buf, size_t len); // -> ATECC TRNG cache
extern "C" uint32_t random32(void); // -> random_buffer()
```
**Audit check:** a successful firmware *link* proves the strong override won —
a duplicate strong symbol would be a linker error. Confirm the device **refuses
to create a wallet** when the ATECC is unavailable.
## 2 · Seed & encrypted storage
The 16 bytes of entropy become a **BIP39 12‑word mnemonic**
(`mnemonicFromEntropy(entropy, 16)`). The device stores the **entropy**, not the
words, in **one AES‑encrypted EEPROM page** at `BITCOIN_WALLET_ADDR`.
```text theme={null}
Plaintext page (32 bytes), before encryption:
[0..3] magic "ZKBW"
[4] version = 1
[5] entropy length = 16
[6..21] 16-byte BIP39 entropy
[22..31] reserved (zero)
```
* Encrypted with the **same AES‑128‑CBC + ATECC‑protected master key** as your
credentials — so the seed is bound to your **PIN** (see
[AES‑128 encryption](/firmware/security/aes-128-encryption)).
* `BITCOIN_WALLET_ADDR` is the last 128‑byte slot of the credential region
(freed by lowering `MAX_CREDENTIALS` 62→61 in `zerokey-memorymap.h`); a
`static_assert` pins it.
* On read, `btcReadWallet()` verifies magic/version/length before use and zeroes
the plaintext buffer afterward.
## 3 · Key derivation (BIP84, mainnet)
```c theme={null}
HDPrivateKey hd(mnemonic, ""); // BIP39 seed, empty passphrase
HDPrivateKey account = hd.derive("m/84'/0'/0'/");
String zpub = account.xpub(); // native SegWit account key
String addr0 = account.derive("m/0/0/").address(); // bc1q... first receive
```
* **BIP84 native SegWit**, path `m/84'/0'/0'`, `bc1q…` addresses, `zpub`
watch‑only. **Mainnet only.**
* No passphrase (BIP39 passphrase is empty by design in this build).
## 4 · Watch‑only export
`bitcoinExportWatchOnly()` prints **public** data over serial (no PIN gate — it
is not secret):
```text theme={null}
fingerprint: <8 hex> # hd.fingerprint()
zpub: # account.xpub()
descriptor: wpkh([/84h/0h/0h]/0/*)
first addr: bc1q...
```
The **master‑key fingerprint origin** in the descriptor is mandatory: uBitcoin's
`PSBT::sign` matches inputs by `memcmp(root.fingerprint(), derivation.fingerprint, 4)`
**and** `derivation.pubkey == derived pubkey`. Without the origin, a wallet
(Sparrow, BlueWallet, Nunchuk…) would build PSBTs the device won't recognise. The
`bitcoin.html` webtool appends the Bitcoin Core descriptor **checksum** in JS.
## 5 · PSBT signing
`bitcoinReviewPsbt(base64)` → on‑screen review → **hold Center** →
`bitcoinConfirmSign()`.
`psbt.parseBase64()`; the OLED shows destination address, amount sent and fee
(`psbt.fee()`), with change detected via each output's derivation. This is a
**sticky** review — the device never blind‑signs.
A center long‑press arms `bitcoinConfirmSign()`. Any other button cancels
(`PSBT CANCEL`).
`psbt.sign(hd)` produces **deterministic RFC 6979 low‑s** ECDSA signatures
over the **BIP143** sighash for every input that matches this wallet. If it
matches **0 inputs** the device replies `PSBT ERR NO_INPUTS` and signs
nothing (expected for a PSBT from a different wallet).
The signed PSBT (base64) is sent back as `PSBT SIGNED`. Finalising,
extracting the raw transaction and broadcasting happen off‑device.
## Threat model
* **Compromised host:** it can lie in the browser, but it cannot forge the
**OLED review** or the **hold‑to‑confirm**. Always verify address/amount/fee on
the device screen. A signature commits to the exact outputs via the sighash, so
a tampered transaction can only be *rejected* by the network, never redirected.
* **Physical extraction of EEPROM:** yields only the AES‑encrypted `ZKBW` page;
without the PIN‑bound master key it is meaningless.
* **Weak randomness:** ruled out by the TRNG‑only entropy path that refuses on
hardware failure.
## How to audit it yourself
Read the 12 words with **Tools → Bitcoin → Show seed**. Feed them into any
independent BIP84 tool (Sparrow, an offline Ian Coleman page, or a Python
`bip_utils` script). Derive `m/84'/0'/0'` and compare the **fingerprint,
`zpub` and first receive address** to the device's **Watch‑only** export.
They must match exactly.
Build a PSBT for the wallet, sign it on the device, and check the returned
`PSBT_IN_PARTIAL_SIG` is a **canonical low‑s ECDSA** signature over the
**BIP143** sighash for that input's pubkey. Any PSBT library
(`python-bitcoinlib`, `bitcoinjs`, Bitcoin Core `analyzepsbt`) can finalise
and verify it.
Watch the serial line during **Watch‑only** and during signing: only
`zpub`/fingerprint/descriptor/signature bytes appear — never the words or
entropy. Grep the firmware: the only writer of the seed words is
`btcDisplaySeed` (OLED), never `SerialUSB`.
**Reference validation (2026‑06‑30).** An independent pure‑Python BIP84 verifier
reproduced the device's exact `zpub` + first address from the 12 words, and a
fund‑free PSBT test produced a signature **byte‑for‑byte equal** to the
independently predicted RFC 6979 low‑s signature over the BIP143 sighash,
verifying as valid canonical ECDSA.
## Source map
| Concern | Where |
| ------------------------------------------------ | ----------------------------------------------------- |
| Entropy, RNG override, storage, derivation, PSBT | `ZerokeyOS/zerokey-bitcoin.cpp` |
| secp256k1 / BIP32/39 / PSBT | `ZerokeyOS/libraries/uBitcoin` |
| AES master key, page encrypt/decrypt | `ZerokeyOS/zerokey-security.cpp` |
| EEPROM page I/O, wallet slot address | `ZerokeyOS/zerokey-eeprom.cpp`, `zerokey-memorymap.h` |
| Hold‑to‑sign wiring | `ZerokeyOS/zerokey-io.cpp` |
Create the wallet, export watch‑only, sign a PSBT.
The master key that protects the stored seed.
# Flashing Protocol (WebTool)
Source: https://docs.zerokeyusb.com/firmware/bootloader/flashing-protocol
The WebTool now acts as a simple loader that transfers the pre-signed binary to the device and manages the reboot.
## WebTool: A Secure Loader
The **WebTool** (the browser-based flasher) no longer calculates security features; it only acts as a transfer interface.
### 📥 Input Files
The WebTool should only receive **binary files (`*.bin`) that have already been pre-signed** by the Offline Signer Tool.
* The file contains the application code **PLUS** the 28-byte Security Footer.
### 🔄 Transfer Protocol
The flashing process follows the traditional USB CDC protocol, treating the signed file as a single complete *payload*:
1. **Start:** The WebTool sends `HELLO` and then `ERASE APP` to clear the application space.
2. **Writing:** The WebTool sends the entire **signed file** in *chunks* using the command `WRITE addr len crc32` and the binary *payload*.
3. **Finalization:** The `DONE` command is sent.
4. **Activation:** The Bootloader receives the data, writes it to Flash (including the footer in its final location), and reboots.
### ⏱️ Timeout Management
To ensure the flashing process does not fail prematurely if unauthorized firmware is loaded, the WebTool waits for an extended period:
* **Increased Delay:** The final waiting time after sending `DONE` has been extended to **20 seconds**.
* **Purpose:** This time covers the 15-second delay imposed by the Bootloader if the authenticity check fails, ensuring the USB connection is not cut before the device can reboot or enter waiting mode.
***
# Offline Signer Tool
Source: https://docs.zerokeyusb.com/firmware/bootloader/signer-tool
Details on how the firmware is securely signed before distribution to ensure authenticity.
## Secure Signing Architecture
To protect the **BLAKE2s Secret Key** (`ZK_SECRET_KEY`) and keep the WebTool public, ZeroKeyUSB uses an **Offline Signing** process.
### 🔑 The Secret: Signing Key
* **Residence:** The 32-byte secret key exists only within the private **Offline Signer Tool** and the device's Bootloader itself.
* **Function:** The key is used to calculate the **BLAKE2s MAC** of the firmware.
* **Security:** Since the WebTool is public, this approach ensures that **no user or attacker can extract the signing key** to create their own official firmware.
### ✍️ Signing Process (Offline)
1. **Input:** The firmware binary (`firmware.bin`) ready for release.
2. **Calculation:** The tool calculates the **CRC32** and the **BLAKE2s MAC** (16 bytes) of the file.
3. **Footer Creation:** It assembles the **Security Footer** structure with the Magic Number, code length, CRC32, and MAC.
4. **Concatenation:** The footer is **concatenated** to the end of the firmware binary.
5. **Output:** A single **pre-signed binary file** (`firmware_signed_footer.bin`) is produced, ready to be uploaded by the public WebTool.
### 📦 Reproducibility and Transparency
Although the signing key is secret, the firmware remains **Open Source and auditable**. The process ensures that:
* Only the development team can create a binary that the Bootloader accepts as official (skipping the 15-second delay).
* The principle is maintained that **there are no remote signing or update mechanisms**.
***
# Integrity and Authenticity Verification
Source: https://docs.zerokeyusb.com/firmware/bootloader/verification
The application verification process using hardware CRC32 and BLAKE2s MAC, including the penalty logic.
## Boot-Up Trust Chain
The ZeroKeyUSB Bootloader executes a fast, cryptographic verification process before yielding control to the application firmware. This process ensures that the firmware **has not been altered (Integrity)** and **originates from an official source (Authenticity)**.
### ⚡ Fast Integrity Check (Hardware CRC32)
Verification is performed using the **DSU (Data Scrambling Unit)** hardware of the SAMD21 microcontroller for CRC32 calculation. This allows scanning the entire Flash memory at maximum bus speed:
* **Cumulative CRC32:** The CRC32 is calculated efficiently chunk-by-chunk.
* **Speed:** Minimizes boot time, ensuring the full verification takes only a few milliseconds.
### 🔐 Cryptographic Authentication (BLAKE2s MAC)
To ensure the firmware was signed by the secret key, the **BLAKE2s-128 MAC (Message Authentication Code) algorithm** is used.
1. **MAC in the Footer:** The final application firmware ends with a 28-byte **Security Footer**, which contains the final CRC32 and the pre-calculated BLAKE2s MAC.
2. **Recalculation:** The Bootloader recalculates the MAC over the entire application code using the embedded **secret key** (`ZK_SECRET_KEY`).
3. **Approval:** If the calculated MAC matches the MAC in the footer, authentication is successful.
### 🛡️ Sanity and Range Checks
Before cryptographic verification, pointer checks are executed to prevent redirection attacks:
* **Stack Pointer (SP):** The initial address of the Stack Pointer is verified to be within the valid **SRAM** range.
* **Reset Handler:** The application's start function address is checked to be within the **Flash** region reserved for firmware.
### 🚨 Penalty for Unofficial Software (15 Seconds)
For cases where the firmware has been altered or comes from an unsigned source, the Bootloader enforces a strict penalty policy:
* **Verification Failure:** If the CRC32 or BLAKE2s MAC does not match, a **15,000 millisecond delay** (`PENALTY_DELAY_MS`) is applied using the *SysTick Timer*.
* **Effect:** This delay discourages the use of unauthorized firmware and prevents fast reboot loops, offering a time window for the user to enter the flashing Bootloader mode.
***
# Browser link
Source: https://docs.zerokeyusb.com/firmware/browser-link
How the host FIND / ZK PING / TIME serial commands and the Chrome extension drive the on-device search — navigation only, never typing.
The **browser link** lets a host — the Chrome/Edge
[extension](/getting-started/browser-extension) — drive the device's alphabet
search over the USB CDC serial port. It is **navigation only**: the host can
suggest *where to look*, but a credential is always typed by the device over USB
HID after a physical press.
## Serial commands
Parsed by `handleIncomingHostRequests()` in `zerokey-io.cpp`. Full protocol table
in [USB utilities](/firmware/usb-utilities).
| Command | Reply | Purpose |
| --------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `ZK PING` | `ZK PONG ON` / `OFF` | Identity probe + link state (lets the extension grey its icon). Always answered. |
| `FIND ` | `OK FIND ` / `ERR OFF` / `ERR BUSY` / `ERR EMPTY` | Jump the on-device alphabet search to ``. |
| `TIME ` | `OK TIME` / `ERR OFF` | Set the device clock (UTC Unix seconds) so TOTP codes stay accurate. |
## The Tools toggle
`Menu → Tools → Chrome: On/Off` gates the link. The flag is persisted in EEPROM
(`0x000D`) and read at boot into `chromeLinkEnabled`; it defaults **On** (blank
EEPROM reads as enabled). When off, `FIND` and `TIME` are ignored with `ERR OFF`
and nothing beyond the identity ping runs — minimal attack surface for anyone
who doesn't use the extension.
## Security model
* **Navigation only.** There is deliberately no serial command that types or
reveals a credential. Typing always requires a physical press on the device.
* `FIND` is accepted only while the vault is **unlocked and browsing** the
credential list (`MAIN_SITE/USER/PASS/2FA` or the search) — never during PIN
entry, an edit, the menu or TOTP, so it cannot clobber in-progress input.
* The worst a malicious page or host can do is move the on-device search cursor.
* `TIME` only sets the clock (affects TOTP), which is not secret; it is still
gated behind the toggle.
## Extension architecture
* **Web Serial** (`navigator.serial`) — the extension opens the CDC port after a
one-time, **per-origin** user grant (done on a normal tab, not the popup, which
the OS port chooser would dismiss). It identifies the device with `ZK PING`, so
it does not depend on a specific USB VID/PID.
* **Main-domain letter** — it uses the first letter of the registrable domain,
ignoring subdomains (`ss.revolut.com` → `R`), with a small list of two-level
suffixes (`co.uk`, `com.br`, …).
* **Clock sync** — it pushes `TIME ` on every use so TOTP stays accurate
without opening the separate [time-sync tool](/firmware/totp/web-time-sync-tool).
* **Field focus** via `chrome.scripting`: heuristics (`autocomplete=username`,
`type=email`, name/id containing user/email/login, or the text input in a form
that has a password field), injected into all frames and deferred so the caret
lands after the popup closes and the page regains focus.
## Limitations
* Field detection misses some single-page apps, shadow DOM and cross-origin
iframes.
* Split username/password flows (some Google/Microsoft logins) don't fit the
device's *user → TAB → password* output.
* Live icon greying without opening the popup would need an MV3 offscreen
document holding the serial connection; the current build updates the badge on
each popup run.
# Display System
Source: https://docs.zerokeyusb.com/firmware/display
How the OLED UI is rendered, animated, and kept clear while you browse your credentials.
## 128×32 pixels with purpose
ZeroKeyUSB uses a **white OLED panel** (SSD1306 controller, I²C at `0x3C`) with a 128×32 pixel resolution.\
The firmware keeps the interface intentionally minimal: large typography, clear layout, and smooth transitions that remain legible even in low light.
***
## Screen hierarchy
```mermaid theme={null}
graph TD
SPLASH["Splash Screen ZeroKeyUSB logo"]
SETUP["Setup Wizard 10 scrollable pages"]
PIN["PIN Entry digit selector + dots"]
MAIN["Main Screen Site / User / Pass / 2FA"]
EDIT["Editor 3 keyboard pages"]
MENU["Menu scrollable list"]
TOTP["TOTP View 6-digit code + countdown"]
TEXT["Text Page confirmations, info"]
SPLASH --> SETUP
SPLASH --> PIN
SETUP --> PIN
PIN --> MAIN
MAIN --> EDIT
MAIN --> MENU
MAIN --> TOTP
MENU --> TEXT
style PIN fill:#fef3c7,stroke:#d97706,color:#000
style MAIN fill:#dbeafe,stroke:#2563eb,color:#000
style EDIT fill:#dcfce7,stroke:#16a34a,color:#000
```
***
## Rendering pipeline
```mermaid theme={null}
flowchart LR
A["Compose frame in RAM buffer (512 bytes)"] --> B["Full-frame I²C transfer to SSD1306"]
B --> C["Display updates ~30 fps"]
style A fill:#dbeafe,stroke:#2563eb,color:#000
style B fill:#fef3c7,stroke:#d97706,color:#000
```
1. **Frame buffer build** — the application composes the entire screen in a 512-byte RAM buffer using `Adafruit_SSD1306` drawing functions: `setCursor()`, `print()`, `drawRect()`, `fillRect()`, `drawBitmap()`.
2. **Full-frame transfer** — `display.display()` sends all 512 bytes to the OLED via I²C in a single burst.
3. **Refresh pacing** — the cooperative main loop redraws only when screen state changes, avoiding unnecessary I²C traffic.
***
## Screen types
### PIN entry screen
* Shows the current digit selector (0–9) with Up/Down navigation.
* Entered digits are shown as filled dots (●) for security.
* PIN length is displayed as a count indicator.
### Main credential screen
* **Four lines** showing the current slot:
* Line 0: Slot index indicator
* Line 1: Site name (scrolls if > \~20 chars)
* Line 2: Username
* Line 3: Password (masked by default)
* A context indicator (`SITE`, `USER`, `PASS`, `2FA`) appears at the top.
### Editor screen
* **Three keyboard pages** selectable with Up/Down:
* Page 1 (`EDIT_KB1`): `A-Z`, brackets, symbols
* Page 2 (`EDIT_KB2`): `a-z`, punctuation
* Page 3 (`EDIT_KB3`): `0-9`, space, special characters
* Left/Right controls: cursor position (◀ / ▶), random character, backspace
* Selected character highlighted with inverted colors
### Menu screen
* Scrollable list with inverted highlight on selected item.
* When items exceed 4 rows, a **scrollbar with thumb** appears on the right edge (3 px wide).
* Thumb position updates proportionally to scroll position.
### Setup wizard pages
* 10 pages of scrollable text with Up/Down navigation.
* A step indicator (`1/9`, `2/9`, etc.) and footer hints appear on screen.
* Pages with > 4 lines show a scrollbar thumb.
### TOTP code screen
* Large **2× text size** for the 6-digit code.
* Countdown in seconds: `"Expires in: XXs"`.
* Refreshes automatically every second.
* Touch any pad to return to credentials.
***
## Typography & assets
| Asset | Format | Size | Usage |
| ---------------- | --------------------- | -------- | ------------------------------------------- |
| **Default font** | Adafruit GFX built-in | 6×8 px | Menus, labels, info text |
| **Text size 2** | 2× scaled | 12×16 px | TOTP codes, large prompts |
| **Icons** | PROGMEM bitmaps | 16×16 px | Menu items (backup, settings, danger, info) |
| **Device SVGs** | Vector in `/images/` | Various | Documentation touch pad illustrations |
All fonts and icons are stored in **Flash (PROGMEM)** — no runtime loading from EEPROM.
***
## Scrolling
Two types of scrolling are implemented:
### Auto-scroll (credential names)
* Names longer than the display width (\~20 chars) scroll **horizontally** at a steady pace.
* Managed by `refreshMainScrollIfNeeded()` in the main loop.
* Scrolling pauses briefly at each end before reversing.
### Vertical scroll (menus and wizard)
* Menu items and wizard pages scroll vertically with Up/Down.
* `menuScrollTop` tracks the first visible row.
* `ensureMenuSelectedVisible()` keeps the highlighted item in view.
* A filled scrollbar thumb proportional to content length appears at the right edge.
***
## Visual feedback
| Feedback type | Implementation |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Selection highlight** | Inverted colors (black text on white background) |
| **Long-press progress** | Two white pens fill along the screen edge via `drawLongPressProgress()`, with a short black lead ahead of each pen so the advance is easy to follow |
| **Activity spinner** | Frame-based animation via `renderActivityScreen()` |
| **Progress bar** | `renderProgress()` with title, subtitle, done/total |
| **Typing indicator** | `renderTypingActivity()` shows characters typed vs. total |
| **Context indicator** | Top bar shows current context (`MENU`, `SITE`, `TOTP`, `SETUP`) |
***
## Inactivity blanking
After **1 minute with no touch input**, the firmware powers the OLED off
(`SSD1306_DISPLAYOFF`) from `handleButtonChecker()`. This is a **display-only**
sleep:
* The vault is **not** locked and `programPosition` does not change — waking
lands on the exact same screen.
* **Any touch** wakes the panel (`SSD1306_DISPLAYON`); that first touch is
consumed, so it only wakes and does not also navigate.
* A host `FIND` from the [browser extension](/getting-started/browser-extension)
also wakes the panel so the jump is visible.
The MCU keeps running while blanked (timers, TOTP steps, serial). Automatic
periodic redraws (e.g. the TOTP countdown) do **not** count as activity — only
touch and host commands do.
***
## Display security
* Sensitive fields (passwords, TOTP codes) are **displayed briefly and cleared** — the frame buffer is overwritten on the next screen transition.
* The display **blanks after 1 minute of inactivity but does not lock** (see above); locking the vault still requires power removal (USB disconnection).
* While typing to host, `renderTypingActivity()` shows progress without displaying the credential content.
* No decrypted credential is ever stored in the OLED controller's GDDRAM beyond the current frame.
The OLED sits behind the sealed epoxy encapsulation, providing excellent contrast and resistance to scratches, dust, and moisture.
# EEPROM Management
Source: https://docs.zerokeyusb.com/firmware/eeprom-management
Memory map, page layout, and read/write operations for the M24C64-WMN6TP credential storage.
ZeroKeyUSB stores every secret inside an external **ST M24C64-WMN6TP** EEPROM (64 Kbit = 8 KB). The firmware manages reads and writes carefully, respecting page boundaries and keeping all credential data encrypted at rest.
***
## Memory map
The EEPROM is organized into a **configuration zone** (first \~220 bytes) and a **credential zone** (remainder):
```mermaid theme={null}
block-beta
columns 1
block:config["Configuration Zone (0x0000–0x00FF)"]
A["0x0000: Config flag (1B)"]
B["0x0001: Screen mode (1B)"]
C["0x0002: Failed attempts (1B)"]
D["0x0010–0x001F: AES IV (16B)"]
E["0x0020–0x0023: legacy / reserved (4B)"]
F["0x0024: Provision flag (1B)"]
G["0x0028–0x0037: legacy / reserved (16B)"]
I["0x003E: Keyboard layout (1B)"]
J["0x0040–0x0047: Last TOTP epoch (8B)"]
H["0x0048–0x0067: PIN hash (32B)"]
K["0x0068–0x00E3: TOTP meta (124B)"]
end
block:creds["Credential Zone (0x0100–0x1FFF)"]
L["61 credential slots × 128 bytes each"]
end
style config fill:#dbeafe,stroke:#2563eb
style creds fill:#dcfce7,stroke:#16a34a
```
| Address | Size | Content | Source in code |
| --------------- | ------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------- |
| `0x0000` | 1 B | Config wizard flag (`0x42` = done) | `zerokey-setup.cpp` |
| `0x0001` | 1 B | Screen orientation (0 = normal, 1 = inverted) | `EEPROM_SCREEN_MODE_ADDR` |
| `0x0002` | 1 B | Failed-attempts counter (persistent backoff, re-applied at boot) | `FAILED_ATTEMPTS_ADDR` |
| `0x0010–0x001F` | 16 B | AES-CBC Initialization Vector | `EEPROM_IV_ADDR` |
| `0x0020–0x0023` | 4 B | *Reserved.* Legacy PIN-attempt threshold from the removed Counter0 lockout; no longer read or written. | — |
| `0x0024` | 1 B | Provisioning flag (`0xA5` = provisioned) | `EEPROM_PROVISION_FLAG` |
| `0x0028–0x0037` | 16 B | *Reserved.* Held the AES master key in older firmware; the key now lives in ATECC slot 8 and never touches EEPROM. | — |
| `0x003E` | 1 B | Keyboard layout selector (0–8) | `EEPROM_LAYOUT_ADDR` |
| `0x0040–0x0047` | 8 B | Last TOTP epoch (big-endian) | `EEPROM_LAST_TOTP_EPOCH_ADDR` |
| `0x0048–0x0067` | 32 B | PIN hash: SHA-256(PIN ∥ serial) | `EEPROM_PIN_HASH` |
| `0x0068–0x00E3` | 124 B | TOTP metadata: 2 B × 61 slots (algorithm + secret\_len) | `CONFIG_TOTP_META_START` |
| `0x0100–0x1F7F` | 7 808 B | 61 credential slots (128 B each = 4 pages × 32 B) | `EEPROM_CREDENTIAL_BASE` |
| `0x1F80–0x1FFF` | 128 B | Bitcoin wallet — AES-encrypted seed page ([audit](/firmware/bitcoin-signer)) | `BITCOIN_WALLET_ADDR` |
***
## Credential slot structure
Each of the **61 credential slots** occupies **4 consecutive 32-byte EEPROM pages** (128 bytes total):
```mermaid theme={null}
graph LR
subgraph Slot["Credential Slot N (128 bytes)"]
P0["Page 0 Site name 32 B ciphertext"]
P1["Page 1 Username 32 B ciphertext"]
P2["Page 2 Password 32 B ciphertext"]
P3["Page 3 TOTP secret 32 B ciphertext"]
end
P0 --- P1 --- P2 --- P3
style P0 fill:#dbeafe,stroke:#2563eb,color:#000
style P1 fill:#dbeafe,stroke:#2563eb,color:#000
style P2 fill:#dbeafe,stroke:#2563eb,color:#000
style P3 fill:#fef3c7,stroke:#d97706,color:#000
```
Each 32-byte page contains:
* **16 bytes of plaintext** (padded with `0xFF`) encrypted as **two AES-128 CBC blocks**
* The encryption uses the device-wide IV plus the AES master key that lives inside ATECC slot 8 — the cipher rounds run on the chip, never on the MCU.
There is no separate `status` byte or CRC per page. Empty slots are written as encrypted `0xFF` blanks during `silentEraseAll()`.
***
## Page address calculation
```
credentialPageAddress(slotIndex, pageIndex) =
(FIRST_CREDENTIAL_PAGE + slotIndex × 4 + pageIndex) × 32
```
Where:
* `FIRST_CREDENTIAL_PAGE` is computed from `CONFIG_TOTP_META_END` rounded up to the next 32-byte boundary.
* `slotIndex` ranges from 0 to 61.
* `pageIndex` ranges from 0 (site) to 3 (TOTP).
***
## Write sequence
The `writeEepromPage()` function writes 32 bytes at a page-aligned address:
1. Check EEPROM presence via `Wire.beginTransmission()` + ACK test.
2. Send 2-byte address (MSB first) followed by 32 data bytes.
3. Wait 10 ms for the EEPROM internal write cycle.
4. Return `true` if `Wire.endTransmission()` reported no error.
For security-related writes that cross page boundaries (e.g., 16-byte IV, 32-byte PIN hash), `eepromWriteRaw()` in `zerokey-security.cpp` splits the data at 32-byte page boundaries to avoid the M24C64's address wrap-around behavior.
***
## Read sequence
`readEepromPage()` reads exactly 32 bytes:
1. Send 2-byte address via I²C write.
2. Issue `Wire.requestFrom(eepromAddress, 32)`.
3. Read all available bytes into the output buffer.
4. Zero-fill any bytes not received (partial read = error).
***
## TOTP metadata
Each credential slot has a 2-byte TOTP metadata entry in the config zone:
| Byte | Content |
| ---- | --------------------------------------------------------------------- |
| 0 | Algorithm code: `0` = none, `1` = SHA-1, `2` = SHA-256, `3` = SHA-512 |
| 1 | Secret length in raw bytes (before Base32 encoding) |
Metadata is read/written independently of credential pages to allow quick TOTP detection without decrypting the entire slot.
***
## Wear characteristics
* The M24C64-WMN6TP supports **>1 million write cycles per page** (datasheet guarantee).
* Credential pages are only rewritten when the user edits a field or imports data.
* Config zone pages (IV, PIN hash, threshold) are written during provisioning and PIN changes — infrequent events.
* The TOTP epoch at `0x0040` is updated each time the user syncs time or generates a code — this is the most-written location.
* Because credentials are typically static, expected EEPROM lifespan exceeds decades of normal use.
***
## Troubleshooting
| Symptom | Cause | Fix |
| ----------------------------- | ------------------------------------------- | ------------------------------------------------- |
| `EEPROM not found` | I²C connection broken | Check solder joints; verify pull-ups on SDA/SCL |
| `EEPROM write 0xNNN` | Write failed at address | Re-try; if persistent, EEPROM may be damaged |
| Garbled credentials | IV or AES key changed without re-encryption | Factory reset + re-provision; restore from backup |
| Slot shows blank after import | TOTP secret parsing failed | Check Base32 encoding; verify algorithm support |
No decrypted credential ever touches persistent memory without explicit user action. Plaintext exists only in SRAM during the active session.
# Touch Input Handling
Source: https://docs.zerokeyusb.com/firmware/io-touch-handling
How the capacitive keys are scanned, debounced, and mapped to menu navigation.
ZeroKeyUSB replaces mechanical buttons with **five copper touch pads** connected to a dedicated **TS06 capacitive controller**. The firmware polls this controller over I²C and translates touches into navigation events.
***
## Hardware overview
| Component | Detail |
| ------------------- | ----------------------------------------------------------------------- |
| **Controller** | TS06 — 6-channel capacitive touch IC |
| **I²C address** | `0xD2 >> 1 = 0x69` |
| **Pads used** | 5 of 6 channels: Left, Right, Up, Down, Center |
| **Status register** | `0x25` — bitmask of currently touched channels |
| **Sensitivity** | Set to `0x3F` (minimum) on channels 0–2 at boot to avoid false triggers |
The controller handles baseline calibration internally and reports which channels are active via the status register.
***
## Touch initialization
At startup (`zerokey-setup.cpp`), the firmware:
1. Probes the TS06 at `0x69` with up to **5 retries** (20 ms apart).
2. If detected, writes minimum sensitivity (`0x3F`) to registers `0x00–0x02`.
3. Configures operation mode via registers `0x05–0x06`.
4. Sets `ts06_ok = true` — if the controller isn't found, touch is disabled and the serial log shows `"TS06 not found on I2C"`.
***
## Polling cycle
```mermaid theme={null}
flowchart TD
A["Read status register 0x25"] --> B{"Any channel active?"}
B -->|No| C["Clear all press timers"]
B -->|Yes| D{"Same channel as locked?"}
D -->|No| E["Ignore — lockout active"]
D -->|Yes / None locked| F{"Press duration?"}
F -->|"< 80 ms"| G["Still debouncing"]
F -->|"80–800 ms"| H["Short press event"]
F -->|"> 800 ms"| I["Long press event"]
H --> J["Dispatch to current screen"]
I --> J
C --> A
G --> A
style A fill:#dbeafe,stroke:#2563eb,color:#000
style H fill:#bbf7d0,stroke:#16a34a,color:#000
style I fill:#fef3c7,stroke:#d97706,color:#000
```
The `handleButtonChecker()` method runs in the main loop:
1. Reads `STATUS_REGISTER` (0x25) via `readRegister()` over I²C.
2. For each channel bit:
* Records `pressStartTime` when a channel first becomes active.
* Applies a **80 ms debounce** (`DEBOUNCE_MS`) — releases shorter than this are ignored.
* Enforces a **150 ms channel lockout** (`CHANNEL_LOCKOUT_MS`) — touching a different pad while one is active is ignored.
3. On release:
* If held > `LONG_PRESS_THRESHOLD` (800 ms) → dispatches long-press handler.
* Otherwise → dispatches short-press handler.
***
## Gesture mapping
| Gesture | Trigger | Main screen | Editor | Menu |
| ----------- | --------- | ----------------------- | ---------------------------------- | ----------------------------------- |
| Tap Left | \< 800 ms | Previous slot | Move cursor left | Go back / Exit submenu |
| Tap Right | \< 800 ms | Next slot | Move cursor right / Enter keyboard | Enter submenu / Exit to credentials |
| Tap Up | \< 800 ms | Cycle to Site view | Change character | Navigate up |
| Tap Down | \< 800 ms | Cycle to 2FA view | Switch keyboard page | Navigate down |
| Tap Center | \< 800 ms | Type credential to host | Insert character | Select / Confirm |
| Hold Left | ≥ 800 ms | Jump 10 slots back | — | — |
| Hold Right | ≥ 800 ms | Jump 10 slots forward | — | — |
| Hold Center | ≥ 800 ms | Enter edit mode | Save and exit editor | Authorize import/export |
***
## Long-press visual feedback
When a long press is in progress, `drawLongPressProgress()` renders a filling progress bar on the OLED screen. This gives the user visual confirmation that they should keep holding. Releasing before 800 ms cancels the action.
***
## PIN screen controls
On the PIN entry screen, the controls change:
| Pad | Action |
| --------------------- | -------------------------------------------------- |
| **Up / Down** | Change the current digit (0–9) |
| **Right** | Add the current digit to the PIN (up to 16 digits) |
| **Left** | Delete the last entered digit |
| **Center** | Submit the PIN for verification |
| **Long-press Center** | Type the device serial number |
***
## Error handling
* If the TS06 is not detected at boot, `ts06_ok` is set to `false` and the status register read returns `0x00` — effectively disabling touch input.
* The firmware does not show a touch error screen; instead, the serial log reports the issue for debugging.
* Touch is silently disabled during lockout delays (`waitFromEeprom()`) and during long-running operations like credential erasure.
The TS06 operates independently of EEPROM or ATECC608A operations. Since all share the same I²C bus at 100 kHz, touch polling is interleaved with other I²C traffic in the cooperative main loop.
# Menu System
Source: https://docs.zerokeyusb.com/firmware/menu
Explore the ZeroKeyUSB menu structure, setup wizard, and how to use every feature safely.
## Simple control, powerful features
ZeroKeyUSB has no buttons, no apps, and no hidden menus — only **five golden touch points** that control everything.\
The menu becomes accessible **after entering your Master PIN** by scrolling past the last credential slot.
***
## Menu structure
```mermaid theme={null}
graph TD
ROOT["Main Menu"]
ROOT --> TOOLS["Tools"]
ROOT --> SETTINGS["Settings"]
ROOT --> DANGER["Danger Zone"]
ROOT --> INFO["Info"]
TOOLS --> IMP["Import"]
TOOLS --> EXP["Export"]
TOOLS --> BTC["Bitcoin"]
TOOLS --> CHR["Chrome: On/Off"]
SETTINGS --> ROT["Rotate Screen"]
SETTINGS --> KB["Keyboard: XX-XX"]
SETTINGS --> UILANG["UI Language"]
SETTINGS --> READER["Reader: On/Off"]
SETTINGS --> PWD["Pwd: XX"]
DANGER --> FACTORY["Factory Reset"]
DANGER --> BOOT["Bootloader Mode"]
INFO --> SW["SW: x.x.x"]
INFO --> SN["SN: XXXXXXXX"]
style ROOT fill:#dbeafe,stroke:#2563eb,color:#000
style DANGER fill:#fee2e2,stroke:#dc2626,color:#000
```
***
## Navigating the menu
| Gesture | Action |
| ------------ | --------------------------------------------- |
| **Up ↑** | Move selection up (wraps to bottom) |
| **Down ↓** | Move selection down (wraps to top) |
| **Center ●** | Select / Execute the highlighted item |
| **Left ←** | Go back to parent menu or exit to credentials |
| **Right →** | Exit menu, jump to credential slot 0 |
When a menu has more items than fit on the 4-row display, a **scrollbar with thumb** appears on the right edge. The selection stays visible as you scroll.
***
### 🧰 Tools
This submenu was called **Backup** in older firmware; it was renamed **Tools** when the Bitcoin wallet was added.
| Item | Action |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Import** | Receives credentials from the host via USB serial (CDC). Device shows "Waiting for data from the web app." |
| **Export** | Sends all 61 credential slots as plaintext CSV over USB serial. Requires long-press Center authorization. |
| **Bitcoin** | Airgapped Bitcoin wallet: create wallet, show the 12-word seed (screen-only) and export a watch-only `zpub`. See [Bitcoin signer](/firmware/bitcoin-signer). |
| **Chrome: On/Off** | Enables the host `FIND` command used by the [browser extension](/getting-started/browser-extension) to jump the on-device search. Default **On**; when Off the firmware ignores the command. Saved in EEPROM (`0x000D`). |
The export/import flow shows an authorization prompt before transferring any data:
```mermaid theme={null}
sequenceDiagram
participant Host
participant Device
Host->>Device: "EXPORT" or "IMPORT"
Device-->>Host: "AWAIT_AUTH EXPORT"
Device->>Device: Show "Hold center to authorize"
alt User holds Center
Device->>Host: Stream CSV data (export) or receive CSV data (import)
Device-->>Device: "Export/Import complete"
else User releases
Device-->>Device: Cancel, return to menu
end
```
Export sends **plaintext credentials** over USB serial. Only perform this on a trusted computer.
***
### ⚙️ Settings
| Item | Action |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Rotate Screen** | Flips the display 180° for left/right-handed use. Also inverts touch controls. Saved in EEPROM. |
| **Keyboard: XX-XX** | Cycles through 9 keyboard layouts (EN-US → DA-DK → DE-DE → ES-ES → FR-FR → HU-HU → IT-IT → PT-PT → SV-SE → EN-US). Saved in EEPROM. |
| **UI Language** | Cycles the on-screen interface language (English ↔ Spanish). Saved in EEPROM. |
| **Reader: On/Off** | Toggles the persistent HID [screen-reader mode](/firmware/screen-reader) — types the screen over USB so a unit with a dead display is still usable. Saved in EEPROM. |
| **Pwd: XX** | Cycles the format used by `Rand` when generating a password: `Symbols` → `Numeric` → `a-z 0-9` → `Aa-z 0-9` → `Words` → `Words+Num`. All formats draw from the ATECC608A TRNG and cap at 16 characters. Saved in EEPROM (`0x0004`). See [Edit a credential](/getting-started/edit-credential). |
***
### ⏱️ TOTP
TOTP is accessed from the credential view, not the main menu. When viewing a credential, scroll **Down past Password** to the **2FA** field:
* If no TOTP secret exists for that slot → shows "No TOTP secret" for 2 seconds.
* If time is not synced → shows "Time not set — Request host time" and sends `REQTIME` over serial.
* If ready → displays a **6-digit code** with a 30-second countdown timer. Refreshes automatically each period. Touch any pad to return.
***
### ⚠️ Danger Zone
Every action in this section shows a **confirmation page** that requires pressing Center to proceed or Left to cancel:
| Item | Effect | Reversible? |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- |
| **Factory Reset** | Runs `eraseAll()` (3-second countdown, encrypted blanks over all 61 slots + clears TOTP metadata) and resets the provisioning flag to `0x00`, so the next boot starts the setup wizard. | ❌ No |
| **Bootloader Mode** | Sets double-reset magic word (`0xF01669EF` at `0x20007FFC`), then issues `NVIC_SystemReset()`. Device reboots into USB DFU bootloader for firmware flashing. | ✅ Yes (reflash) |
***
### ℹ️ Info
Read-only submenu showing:
* **SW: x.x.x** — firmware version from `zerokeyInfo::getSoftwareVersion()`
* **SN: XXXXXXXX** — hardware serial from the SAMD21 unique ID registers
***
## Setup wizard
The setup wizard runs on first boot (or after factory reset). It consists of **10 internal pages** across **9 visible steps**:
```mermaid theme={null}
graph LR
W1["1. Welcome"] --> W2["2. Navigation"]
W2 --> W3["3. Rotate Screen"]
W3 --> W4["4. Keyboard Layout"]
W4 --> W5["5. Create PIN"]
W5 --> W6["6. Confirm PIN"]
W6 --> W7["7. Unlock Info"]
W7 --> W8["8. Accounts Info"]
W8 --> W9["9. Generate IV"]
W9 --> W10["10. Ready!"]
style W5 fill:#fef3c7,stroke:#d97706,color:#000
style W6 fill:#fef3c7,stroke:#d97706,color:#000
style W9 fill:#fee2e2,stroke:#dc2626,color:#000
```
Pages with more than 4 lines of text are **vertically scrollable** using Up/Down. A scrollbar with thumb appears on the right edge.
Each wizard page supports:
* **Right** → advance to next page
* **Left** → go back to previous page
* **Center** → action (toggle orientation, change layout, start PIN entry)
* **Up/Down** → scroll content
***
## Design philosophy
The menu system is intentionally minimalist:
* No deep submenus — every option is **two taps away** from the main menu.
* All destructive actions require explicit Center confirmation on a dedicated page.
* Layout and gestures remain consistent across firmware versions.
* Menu items dynamically update their labels (e.g., keyboard layout shows current selection).
ZeroKeyUSB requires no drivers or software installation.\
It's recognized as a standard USB keyboard on any operating system.
# Screen-reader / No-screen mode (technical / audit)
Source: https://docs.zerokeyusb.com/firmware/screen-reader
How the HID screen-reader ("broken screen") mode is implemented — the activation gesture, echo-line mechanics, exactly what it emits and what it never emits — so you can audit its data exposure.
This page documents the **implementation** of the HID screen‑reader so its data
exposure can be audited. For the user recovery guide see
[Broken / No‑screen mode](/getting-started/recovery-no-screen).
The mode makes the device **type the current on‑screen state over USB HID
(keyboard)**, one line at a time, so a unit with a dead OLED can still be
unlocked and operated blind. Implementation lives in
**`ZerokeyOS/zerokey-utils.cpp`** (emitter), **`zerokey-io.cpp`** (gesture) and
**`zerokey-menu.cpp`** (persistent toggle).
## State & activation
* **Runtime flag** `screenReaderMode` (`zerokey-globals.cpp`, default `false`).
* **Persistent flag** in EEPROM at `EEPROM_SCREEN_READER_ADDR = 0x0003`
(`1` = boot straight into reader mode). `initScreenReaderMode()` reads and
applies it at boot; **Settings → "Reader: On/Off"**
(`setScreenReaderPersistent`) writes it and flips the runtime flag so the
change takes effect immediately.
* **Session gesture:** hold **Center for 10 s** (`SCREEN_READER_HOLD_MS = 10000`)
**while the PIN prompt is shown** (`PIN_SCREEN`/`EDITPIN`). A border animation
fills over the full 10 s and toggles `screenReaderMode` exactly on completion.
The gesture is deliberately restricted to the **PIN screen** so it is reachable
**before unlocking** — the whole point is to rescue a device whose screen died.
Releasing before 10 s does nothing.
## What it emits (echo‑line mechanics)
The device keeps **one logical line** on the host. `announceCurrentScreen()`:
* caches the text in `g_lastEcho` and the length in `g_hidEchoLen`;
* on a state change, **backspaces** the previous echo and types the new one
(unchanged states are **not** retyped → no flicker);
* sets `g_suppressNextAnnounce` to skip the one automatic announce that would
otherwise double up right after a real credential was typed.
HID output uses a fixed cadence (`hidTapKey`): press, `12 ms`, `releaseAll`,
`18 ms`; `\n`→`KEY_RETURN`, `\t`→`KEY_TAB`.
| Screen | Emitted line |
| ----------------- | ----------------------------------------------------------------------------- |
| PIN entry | `PIN 125 >7` — digits entered, then the selected digit (`>OK` = confirm tick) |
| Credential (site) | `3: google.com` |
| Credential fields | `3: user`, `3: password`, `3: 2FA` |
| Menu | `Menu: ` |
| Confirm page | ` Hold=OK Left=No` |
**Blind PIN entry:** Up/Down change the selected digit (watch `>n`), Right adds
it, Left deletes, then select `>OK` and Right/Center to unlock — the typed line
mirrors what the OLED would show.
**Revealing a value:** pressing **Center** on a credential wipes the echo
(`typeCredential` backspaces `g_hidEchoLen`) and types the **real value**
(user/password) exactly as normal HID typing would — so a password can be read
blind into a focused text box.
## Data‑exposure boundary (the audit‑relevant part)
The reader types **PIN digits** and, on an explicit Center, **passwords** into
whatever field is focused. Use it only into a **private** text field you control
and clear that field afterwards.
Two properties matter for an audit:
1. **No new egress channel.** The reader only ever emits (a) the single status
line the OLED would show, or (b) — on an explicit Center — the *same* value
`typeCredential` already types during normal use. It exposes nothing that a
sighted user couldn't already get over HID.
2. **The Bitcoin seed is never typed.** The seed viewer (`btcDisplaySeed`) draws
to the OLED only and has **no HID path**; the reader has no branch that emits
seed words or entropy. A broken‑screen unit therefore still **cannot** leak the
Bitcoin seed over USB. (See [Bitcoin signer](/firmware/bitcoin-signer).)
## How to audit it yourself
Grep for `announceCurrentScreen` and `hidTypeText`/`hidTapKey` in
`zerokey-utils.cpp`. Confirm every call site corresponds to a screen the OLED
already shows, and that none of them reads the `ZKBW` wallet page or the seed
words.
In `zerokey-io.cpp`, verify the 10 s toggle is gated on
`programPosition == PIN_SCREEN || EDITPIN` and `SCREEN_READER_HOLD_MS`.
Verify the persistent flag is a single byte at `EEPROM_SCREEN_READER_ADDR
(0x0003)` and that it only toggles the same runtime behaviour.
## Source map
| Concern | Where |
| ----------------------------------------- | ----------------------------------- |
| Emitter, echo line, credential reveal | `ZerokeyOS/zerokey-utils.cpp` |
| 10 s activation gesture | `ZerokeyOS/zerokey-io.cpp` |
| Persistent "Reader: On/Off" toggle | `ZerokeyOS/zerokey-menu.cpp` |
| Runtime & persistent flag, EEPROM address | `ZerokeyOS/zerokey-globals.{h,cpp}` |
Activate and use the mode with a dead screen.
Why the seed is never exposed, even here.
# AES-128 Encryption
Source: https://docs.zerokeyusb.com/firmware/security/aes-128-encryption
How ZeroKeyUSB chains the ATECC608A's hardware AES engine into CBC mode to protect every credential stored on the device.
ZeroKeyUSB encrypts each credential block using **AES-128 in CBC mode**. Single-block ECB is done **on the ATECC608A** using a key that never leaves the chip; the SAMD21 MCU wraps the chip calls in CBC chaining and handles I/O to the EEPROM.
***
## Key material
The AES master key is a **16-byte random value generated by the ATECC608A TRNG** at first boot, written to **slot 8** of the chip, and then locked in by the data-zone lock.
| Property | Detail |
| --------------------- | --------------------------------------------------------------------------------------------------- |
| **Source** | ATECC608A hardware TRNG (`random()` command, mode 0x00 — refreshes the DRBG seed before output) |
| **Size** | 16 bytes (128 bits) |
| **Storage** | ATECC608A slot 8 (`IsSecret=1`, `KeyType=6`/AES, `WriteConfig=Never`) |
| **Visibility** | The chip never exposes the slot contents over I²C once configured this way |
| **Generation moment** | Single shot, during the first boot of new firmware on a virgin chip, inside `provisionAesAndLock()` |
| **Mutability** | None after the data zone is locked. The key persists for the device's lifetime. |
Because `IsSecret=1` is enforced before the data zone is locked, even an attacker with physical I²C access cannot read the AES key back from the chip — the `Read` command refuses to return it.
***
## Why the chip and not the MCU?
The earlier firmware ran software AES on the SAMD21 with the key stored in EEPROM, because the `MAHDA-T` variant of the ATECC608A ships with the hardware AES command disabled. Enabling it requires:
1. Writing the `AES_Enable` bit (byte 13, bit 0) of the Config Zone.
2. Configuring slot 8 with `KeyType=6` (AES).
3. Locking the Config Zone so those settings take effect.
The firmware now does all three the first time it boots. The trade-off: AES blocks now cross the I²C bus, which is slower (\~10 ms per block vs \~0.1 ms in software). For a credential read that decrypts 3×32 bytes that's ≈60 ms — imperceptible to the user.
***
## CBC chaining implementation
The `cbcEncrypt32` / `cbcDecrypt32` functions in `zerokey-security.cpp` process each 32-byte credential field as two 16-byte blocks chained against the device IV:
```mermaid theme={null}
flowchart LR
subgraph Encryption
IV["IV (16B) EEPROM 0x0010"] --> XOR0["⊕"]
P0["Plain Block 0"] --> XOR0
XOR0 --> AES0["ATECC608A AES ECB encrypt slot 8 key"]
AES0 --> C0["Cipher Block 0"]
C0 --> XOR1["⊕"]
P1["Plain Block 1"] --> XOR1
XOR1 --> AES1["ATECC608A AES ECB encrypt slot 8 key"]
AES1 --> C1["Cipher Block 1"]
end
style IV fill:#fef3c7,stroke:#d97706,color:#000
style AES0 fill:#fef3c7,stroke:#d97706,color:#000
style AES1 fill:#fef3c7,stroke:#d97706,color:#000
style C0 fill:#dcfce7,stroke:#16a34a,color:#000
style C1 fill:#dcfce7,stroke:#16a34a,color:#000
```
### Encryption
```
prev = IV
for each 16-byte block b (0, 1):
x = plain[b] XOR prev
cipher[b] = ATECC608A.aesEncryptBlock(slot=8, mode=0x00, in=x)
prev = cipher[b]
```
### Decryption
```
prev = IV
for each 16-byte block b (0, 1):
dec = ATECC608A.aesDecryptBlock(slot=8, mode=0x01, in=cipher[b])
plain[b] = dec XOR prev
prev = cipher[b]
```
The MCU only sees plaintext blocks (input to encrypt, output of decrypt) and ciphertext blocks (output of encrypt, input to decrypt). It never sees the AES key.
***
## Wire format of each AES call
For every 16-byte block:
| Field | Value | Purpose |
| -------------- | -------------------------------------------------- | ------------------------------------------- |
| Wake token | drive SDA low 60 µs | Pull the chip out of sleep |
| Opcode | `0x51` (`AES`) | Command identifier |
| Param1 (Mode) | `0x00` = encrypt block 0, `0x01` = decrypt block 0 | `bit 0` operation, bits 6–7 sub-key index |
| Param2 (KeyID) | `0x0008` | Slot 8 |
| Data | 16 bytes | Plaintext (encrypt) or ciphertext (decrypt) |
| CRC | 2 bytes | Custom CRC-16 (poly `0x8005`, init `0`) |
| Response | 16 bytes + status | The encrypted or decrypted block |
| Sleep token | `0x01` | Return chip to low-power state |
Each call takes \~10 ms including I²C overhead.
***
## Padding
Each credential field (site, username, password) is **up to 32 bytes** in RAM. Before encryption:
1. Trailing **space characters (`0x20`)** are replaced with `0xFF` from the end inward.
2. The field fills the 32-byte page buffer directly; any unused tail is `0xFF`.
On decryption, `bufferToString()` strips `0xFF` bytes and reads until `\0` or `0xFF`.
***
## Per-operation flow
### `lock()` — encrypt and write credentials
1. Replace trailing spaces in `currentSite`, `currentUser`, `currentPass` with `0xFF`.
2. Load IV from EEPROM (`loadIVfromEEPROM()`).
3. For each of the 3 fields:
* Copy up to 32 bytes into the 32-byte buffer, padding any unused tail with `0xFF`.
* Call `cbcEncrypt32(iv, plain, encrypted)` — two ATECC AES calls under the hood.
* Write the 32-byte ciphertext to the correct EEPROM page.
### `unlock()` — decrypt and load credentials
1. Load IV from EEPROM.
2. Self-heal check: if slot 0 page 0 is raw `0xFF`, call `silentEraseAll()`.
3. For each of the 3 fields:
* Read the 32-byte ciphertext from EEPROM.
* Call `cbcDecrypt32(iv, encrypted, decrypted)` — two ATECC AES calls.
* Copy the 32 bytes into `currentSite` / `currentUser` / `currentPass`.
***
## Error reporting
When an AES round-trip fails, the firmware preserves the chip's response code and displays it on the OLED instead of a generic error. The format is `AES E RC SS` plus a second line `LC= LV= KT=` showing the chip's lock and key-type state at the moment of failure:
| Code | Meaning |
| -------- | ------------------------------------------------------------------------------- |
| `AES E1` | `silentEraseAll()` could not encrypt a blank slot |
| `AES E2` | `eraseAll()` could not encrypt a blank slot |
| `AES E3` | `lock()` could not encrypt a credential field (`f0`/`f1`/`f2` = site/user/pass) |
| `AES E4` | `unlock()` could not decrypt a credential field |
`RC` is the driver-level code (`-1` wake, `-2` I²C, `-3` CRC, `-4` chip status error, `-5` timeout). `SS` is the raw status byte from the chip (`0x0F` execution error, `0x03` parse, `0x07` self-test, …). The combination tells you exactly why the chip rejected the call. Locked + `KT=1` (instead of `6`) means the chip is permanently misconfigured for AES; that's the only failure mode that can't be cleared by a reboot.
***
## Security considerations
| Consideration | Status |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Key entropy** | 128 bits from the chip's hardware TRNG — not brute-forceable |
| **PIN ≠ key** | Changing or forgetting the PIN does not affect the AES key or existing ciphertext |
| **Key at rest** | Lives inside ATECC608A slot 8 with `IsSecret=1`. The slot is not readable via the `Read` command after the data zone is locked. |
| **Key in transit** | Never crosses the I²C bus. The MCU sends plaintext / ciphertext blocks; the chip uses its internal copy of the key. |
| **Physical attack via I²C** | An attacker who exposes I²C can replay AES calls but cannot extract the key. They could still observe plaintext blocks the MCU is feeding the chip — physical encapsulation remains essential. |
| **Factory reset** | `eraseAll()` overwrites all credential pages with encrypted blanks. The AES key itself is permanent (slot 8 is locked-Never). |
| **No key escrow** | There is no backup copy of the AES master key anywhere. Chip failure = permanent loss of all credentials. Keep an exported backup. |
The AES key cannot be rotated or recovered once the data zone is locked. If the secure element fails, every credential encrypted under it becomes unreadable. Use the USB-CDC backup command on a trusted host before relying on the device long-term.
# Security System
Source: https://docs.zerokeyusb.com/firmware/security/index
How ZeroKeyUSB protects your credentials through a hardware secure element, AES-128 CBC encryption (key on chip, ECB blocks in hardware), and offline-only operation.
## Offline by design
ZeroKeyUSB does not rely on the Internet, cloud storage, or companion apps.
Everything — from random number generation to PIN verification — happens **inside the device**, powered directly through USB.
Your passwords **never leave the hardware** and **cannot be accessed remotely**, even by the manufacturer.
***
## Two cooperating chips
Security is split across two pieces of silicon so neither one alone can leak the vault:
```mermaid theme={null}
graph LR
subgraph MCU["SAMD21E18A (MCU)"]
CBC["CBC chaining\n(XOR + block dispatch)"]
SHA["SHA-256\nPIN hashing"]
USB["USB HID + CDC"]
end
subgraph SE["ATECC608A (Secure Element)"]
TRNG["Hardware TRNG\nkey + IV generation"]
AESHW["AES-128 ECB\nhardware engine\n(slot 8 key)"]
KEY["AES key (16B)\nslot 8, IsSecret=1"]
SER["Chip Serial\n9-byte unique salt"]
end
subgraph MEM["M24C64 EEPROM"]
IV["IV (16B)\n@ 0x0010"]
HASH["PIN hash (32B)\n@ 0x0048"]
CRED["61 encrypted\ncredential slots"]
end
TRNG -->|"generates at provisioning"| KEY
KEY -->|"internal, never leaves chip"| AESHW
TRNG -->|"generates"| IV
SER -->|"salt"| SHA
SHA -->|"stores"| HASH
IV -->|"loaded into RAM"| CBC
CBC -->|"per-block AES via I²C"| AESHW
CBC -->|"reads/writes"| CRED
style MCU fill:#dbeafe,stroke:#2563eb
style SE fill:#fef3c7,stroke:#d97706
style MEM fill:#dcfce7,stroke:#16a34a
```
| Chip | Role |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **SAMD21E18A (MCU)** | Runs the application firmware, manages the CBC chaining logic, drives the OLED, USB HID, touch, and I²C bus. Single-block AES is delegated to the secure element. |
| **ATECC608A-MAHDA-T (secure element)** | Generates true random numbers (TRNG), holds a unique 9-byte chip serial used as PIN salt, and — once provisioned — performs every AES-128 ECB block in dedicated hardware using a key stored in slot 8 that **never leaves the chip**. |
The MCU and ATECC608A share an I²C bus at address `0x60`. The MCU does not have a usable copy of the AES key: it sends 16-byte plaintext blocks to the chip and receives 16-byte ciphertext back. The chip generated the key itself at provisioning time using its TRNG; the byte sequence never crossed the I²C bus.
> **Crypto model — Camino A (AES key + AES engine on chip).**
> The firmware enables the hardware AES command, configures slot 8 as an AES key holder (`IsSecret=1`, `KeyType=6`), writes a 16-byte TRNG-generated key to it, and locks both Config and Data zones. From that point on, encryption and decryption are single-block ECB calls to the chip, chained on the MCU into CBC.
***
## Encryption architecture
All sensitive data is stored in the external **EEPROM M24C64-WMN6TP**, encrypted using **AES-128 in CBC mode**. Each ECB block is computed by the ATECC608A hardware AES engine; the MCU handles only the CBC XOR chaining.
| Element | Source / Location |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cipher** | AES-128 CBC — single-block ECB delegated to the ATECC608A `AES` command (opcode `0x51`). Per-block XOR chaining is done on the MCU around the chip calls so the key never has to be loaded into MCU SRAM. |
| **AES master key** | 16 random bytes produced by the ATECC608A TRNG at first boot and written to **slot 8** of the chip. `IsSecret=1` means it can never be read out via the I²C bus. |
| **IV (Initialization Vector)** | 16 random bytes produced by the ATECC608A TRNG at provisioning. Stored in EEPROM at `0x0010–0x001F`. |
| **PIN binding** | The AES master key is **not** derived from the PIN. The PIN is verified separately via an EEPROM-stored hash and gates access to the unlock routine. |
| **Credential layout** | Each slot holds up to four 32-byte encrypted EEPROM pages: site (p0), username (p1), password (p2), TOTP secret / note (p3). Each page holds **up to 32 bytes** of plaintext (site/user/password up to 32 chars); any unused tail is padded with `0xFF`. |
### CBC chaining detail
`cbcEncrypt32` / `cbcDecrypt32` process each 32-byte credential in two 16-byte blocks. For each block:
1. The plaintext block is XORed with the previous ciphertext (or with the device IV for the first block).
2. The XOR result is sent to the ATECC608A via the AES command (`mode=0x00` for encrypt, `0x01` for decrypt, key from slot 8, key block 0). The chip returns the 16-byte ciphertext.
3. The resulting ciphertext becomes `prev` for the next block.
Decryption is symmetric: the chip returns plaintext, the MCU XORs it with the previous ciphertext to recover the original block.
**Why split the work this way?** The ATECC608A's `AES` command exposes only single-block ECB. CBC is the *chaining policy* layered on top — implementing it on the MCU keeps every byte of the key inside the secure element while giving us the diffusion benefits of CBC on the credential data.
***
## EEPROM security map
| Address | Size | Content |
| --------------- | ----- | -------------------------------------------------------------------------------------------------------------------- |
| `0x0000` | 1 B | Configuration wizard flag (`0x42` = done) |
| `0x0001` | 1 B | Screen mode / orientation |
| `0x0002` | 1 B | Soft failed-attempts counter (UX backoff) |
| `0x0010–0x001F` | 16 B | AES-CBC Initialization Vector (TRNG-generated) |
| `0x0020–0x0023` | 4 B | *Reserved* — legacy PIN-attempt threshold from the removed Counter0 lockout. No longer read or written. |
| `0x0024` | 1 B | Provisioning flag (`0xA5` = provisioned) |
| `0x0028–0x0037` | 16 B | *Reserved* — legacy AES master slot from the software-AES build. Not used since the key was moved into ATECC slot 8. |
| `0x003E` | 1 B | Keyboard layout selector |
| `0x0040–0x0047` | 8 B | Last TOTP epoch (persisted across power cycles) |
| `0x0048–0x0067` | 32 B | PIN hash = SHA-256(pinArray\[16] ∥ chip\_serial\[9]) |
| `0x0068+` | 124 B | TOTP metadata (algorithm + secret length, 2 B × 61 slots) |
| `0x0100+` | — | Credential pages (4 × 32 B × 61 slots = 7808 B max) |
> **Note on `0x0028`.** Older units (compiled before the AES move) used this region to hold the 16-byte AES master in plaintext. New units do not touch it; the bytes remain at whatever the EEPROM had previously. Treat the address as reserved.
***
## The Master PIN
The PIN authorises an unlock cycle; it is **never used directly as an encryption key**.
```mermaid theme={null}
flowchart TD
A["User enters PIN\n4-16 digits"] --> E["SHA-256(PIN ∥ serial)"]
E --> F{"Hash matches\nEEPROM @ 0x0048?"}
F -->|"Yes (constant-time)"| G["✅ Unlock\nClear fail counter"]
F -->|"No"| H["❌ Deny\nIncrement fail counter\nExponential delay (persists)"]
style G fill:#bbf7d0,stroke:#16a34a,color:#000
style H fill:#fef3c7,stroke:#d97706,color:#000
```
The verification flow:
1. On boot, before the PIN screen accepts input, the firmware replays the accumulated backoff for the stored failed-attempt count (`waitFromEeprom()`).
2. The user enters up to **16 digits** on the capacitive pads.
3. `derivePinKey()` computes `SHA-256(pinArray[16] ∥ chip_serial[9])` to produce the 32-byte PIN hash.
4. It reads the stored 32-byte hash from EEPROM (`0x0048`) and runs a **constant-time compare** (`diff |= stored[i] ^ derived[i]`).
5. **On match:** the failed-attempt counter is cleared and the unlock proceeds.
6. **On mismatch:** the counter increments and the device enforces an exponential backoff delay before the next attempt — and again at the next boot.
The backoff counter lives in EEPROM (`0x0002`) and is re-applied on every power-up, so an attacker cannot skip the delay by cutting power. The vault is **never wiped** by wrong PINs. Note that this protects only guesses made *through the device*: an attacker who reads the PIN hash off the I²C bus can crack it offline with no delay, which is why the epoxy encapsulation (blocking bus access) and a long PIN matter.
### Exponential backoff — delay schedule
Stored at EEPROM `0x0002`; re-applied at boot; reset on correct PIN.
| Failed attempts | Wait time |
| --------------- | ------------------------------------ |
| 1 | 5 s |
| 2 | 10 s |
| 3 | 20 s |
| 4 | 40 s |
| 5 | 80 s |
| … | doubles up to **2 560 s (≈ 43 min)** |
Formula: `wait = BASE_SECONDS (5) × 2^(min(attempts,10)−1)`, capped at `MAX_WAIT_SECONDS` (2 560).
### No destructive lockout
An earlier design used the ATECC608A's monotonic `Counter0` to wipe the vault after 50 wrong PINs. That mechanism was **removed**: `verifySignature()` no longer increments Counter0, reads any threshold, or calls `eraseAll()` on failed attempts. The persistent backoff above is the sole automatic brute-force defence; `eraseAll()` runs only on a user-initiated factory reset.
***
## ATECC608A slot map
Established by the device itself the first time it boots, then **locked permanently** (Config and Data zones both irreversibly closed):
| Slot | Size used | Content | SlotConfig / KeyConfig |
| ----- | --------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **8** | 16 B | AES-128 master key — generated on chip by the TRNG at first boot, used by every credential encrypt/decrypt | `IsSecret=1` / `WriteConfig=Never` / `KeyType=6 (AES)`. The key cannot be read out over I²C, nor rewritten once the data zone is locked. |
| **9** | 32 B | `SHA-256(PIN_padded ∥ device_serial)` — the PIN key | `IsSecret=0` / `WriteConfig=Always` so the app can rewrite the slot when the user changes their PIN. Readable over I²C. |
### Provisioning sequence (first boot only)
`zerokeyAtecc.provisionAesAndLock()` runs once, before the setup wizard:
1. **Read** Config Zone blocks 0, 1 and 3 to learn the chip's current factory values.
2. **Set the AES\_Enable bit** (byte 13, bit 0) using a 32-byte block write that preserves every factory bit we didn't intend to change. Re-read and verify the bit took effect; abort without locking if not.
3. **Set SlotConfig\[8]** — `IsSecret=1` (bit 7 of byte 36) and `WriteConfig=Never` (high nibble of byte 37 = `0x4`), preserving the rest of the byte. Re-read and verify.
4. **Set KeyConfig\[8].KeyType = 6 (AES)** — bits 2..4 of byte 112, preserving every other bit. Re-read and verify.
5. **Lock the Config zone.** Irreversible.
6. **Generate** a 16-byte random key via the chip's TRNG and write it to slot 8 in the clear (still allowed while the data zone is open).
7. **Lock the Data zone.** Irreversible.
Every write is followed by a read-back. If any verify fails, the function returns a numbered `PROV E` error with the chip's raw status byte attached and **does not proceed to lock the zone**, so a misbehaving chip cannot brick itself silently.
> **Why bit-level writes?** The MAHDA-T parts ship with several "reserved" bits in byte 13 (`AES_Enable`) factory-set. A naive write that clears them is rejected by the chip with a parse error (`SS=0x03`). The provisioning code reads each byte first, OR-masks only the bits it actually needs to flip, and writes the whole 32-byte block back.
> **Known trade-off (Slot 9 readable):** Slot 9 is not locked as secret because the MAHDA-T SKU rejects clear writes to IsSecret slots 0–7. The PIN hash lives there with `IsSecret=0`, so an attacker with physical I²C access can read the 32-byte hash and attempt an offline SHA-256(PIN∥serial) brute force. The persistent backoff limits online attempts but does nothing against offline cracking — the epoxy encapsulation (blocking bus access) is what stands in the way there. Short PINs are vulnerable to this attack — use the maximum 16 digits.
***
## Initialization Vector
The IV is generated **once** during provisioning by the ATECC608A's TRNG and stored in EEPROM at `0x0010`. Two sanity guards protect it:
* A read returning all-`0x00` or all-`0xFF` is treated as uninitialised and triggers regeneration from the TRNG.
* If EEPROM read fails at unlock time, the firmware attempts TRNG regeneration and re-stores the IV.
**Single device-wide IV:** all credential pages are chained against the same IV. This keeps the layout simple and auditable. The threat model leans on TRNG quality and the secrecy of the AES key, not on per-record nonces.
**Regeneration consequence:** if the IV is lost or regenerated without re-encrypting credentials, existing slots will decrypt to garbage (the ciphertext was produced under the old IV). The firmware's self-healing routine (`silentEraseAll`) is called automatically on first unlock if slot 0 page 0 is still raw `0xFF` (EEPROM default), and can be called again manually via `generateAndStoreIV()`.
***
## Self-healing initialisation
On the first unlock after provisioning, `ZerokeySecurity::unlock()` checks whether credential slot 0, page 0 is still at the EEPROM factory default (`0xFF` across all 32 bytes). If so, it calls `silentEraseAll()`:
1. Loads the device IV from EEPROM.
2. For each of the 61 credential slots × 4 pages: encrypts a 32-byte `0xFF` blank under AES-128 CBC and writes it to EEPROM.
3. Clears TOTP metadata for every slot.
This ensures fresh units always have consistent, properly encrypted blank entries before any credential is written.
***
## Data segmentation
Each credential slot occupies 4 consecutive EEPROM pages (128 bytes total):
| Page | Content (plaintext, up to 32 B; unused tail `0xFF`) | EEPROM bytes |
| ---- | --------------------------------------------------- | --------------- |
| 0 | Site / domain | 32 B ciphertext |
| 1 | Username | 32 B ciphertext |
| 2 | Password | 32 B ciphertext |
| 3 | TOTP secret | 32 B ciphertext |
Splitting fields keeps recognisable plaintext patterns out of the ciphertext stream and limits the blast radius of a corrupt EEPROM page. Padding bytes are `0xFF`; trailing `0x20` (space) chars are replaced with `0xFF` before encryption to avoid pattern leakage.
***
## Tamper protection
* The PCB is **encapsulated in epoxy resin**; opening the device destroys the board and the chip connections.
* **No wireless interfaces** (no Wi-Fi, no Bluetooth, no NFC).
* The **bootloader region is BOOTPROT-locked** in hardware fuses (`BOOTPROT = 7`, protecting the first 16 KB) — application firmware cannot rewrite or relocate the bootloader.
* The bootloader will only jump to **ECDSA P-256-signed firmware**; an unsigned or tampered image falls into USB-CDC recovery mode instead of executing.
* All decrypted credentials live in **temporary RAM buffers** (`currentSite`, `currentUser`, `currentPass`) that are populated at unlock and overwritten at the next lock or power cycle.
* **Write-protect pin** (`EEPROM_WP_PIN = PA01`) can be driven high by firmware to hardware-lock EEPROM writes.
***
## Backup and restore
Credentials can be exported and imported over the USB CDC serial interface:
* **Export (`backupAllCredentials`):** decrypts all 61 slots in-device and sends them as comma-delimited plaintext lines over SerialUSB. The host receives credentials in clear — **ensure the USB connection is trusted**.
* **Import (`loadAllbackupCredentials`):** receives records from the host, re-encrypts them under the current device IV/master, and writes them to EEPROM. TOTP secrets are parsed and stored in page 3 of each slot.
> **Security note:** backup transmits decrypted credentials in plaintext over USB. Only perform backup/restore on a trusted, air-gapped host.
***
## Known limitations and trade-offs
| Limitation | Impact | Mitigation |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| AES key cannot be regenerated after provisioning | If the chip ever fails, the credentials encrypted under that key are unrecoverable | `WriteConfig=Never` is the cost of `IsSecret=1` plus locked data zone; pick the maximum-security trade-off and accept it. Keep an exported backup. |
| Slot 9 PIN hash readable over I²C | Offline SHA-256 brute-force if I²C accessible | Short PINs (\< 6 digits) are vulnerable; use max-length PINs |
| Single device-wide IV | Same IV for all slots; no per-record nonces | IV entropy from ATECC TRNG; IV loss requires re-initialisation |
| PIN comparison in software (not CheckMac); hash readable over I²C | Offline SHA-256 brute-force if the I²C bus is reached; side-channel timing | Constant-time compare; persistent on-device backoff; epoxy encapsulation blocks bus access; use a long PIN |
| Backup is plaintext over USB | Physical host can capture credentials | Document clearly; use only on trusted machines |
| Each AES block is a chip round-trip over I²C | Slower than software AES — credentials decrypt in \~30 ms instead of \~3 ms | Acceptable for a hand-held password manager; the chip is the security boundary |
***
## Transparency, not dependence
ZeroKeyUSB's firmware is fully open-source and available for public **audit and verification**. Anyone can review:
* How the ATECC608A is driven, including the bit-level provisioning routine that enables the chip's AES engine and locks both zones (`zerokey-atecc.cpp`).
* How CBC chaining wraps the chip's single-block AES command (`zerokey-security.cpp`).
* How the IV is generated, validated, and refreshed (`zerokey-security.cpp::generateAndStoreIV`).
* How the bootloader hashes and verifies the application (`bootloader/src/main.c`).
There are **no remote update mechanisms**: re-flashing requires physical access via SWD pogo pins or the local USB-CDC bootloader, and any new image must be signed by the offline ECDSA key.
***
## Dive deeper
How the ATECC608A's hardware AES engine encrypts every credential block, with CBC chaining wrapped around it on the MCU.
The persistent backoff, the EEPROM-stored SHA-256 hash, and the constant-time compare that gates unlock.
How the ATECC608A TRNG seeds the device-wide IV and how regeneration is handled.
ZeroKeyUSB pairs an MCU with a hardened secure element. The chip provides the *entropy* (TRNG), the *identity* (chip serial used as PIN salt), **and** the *cipher* itself — every AES block is computed inside the secure element using a key the MCU has never seen. The MCU's role is to chain those blocks into CBC, drive the UI, and shuttle plaintext / ciphertext to and from the EEPROM.
# Initialization Vector Generation
Source: https://docs.zerokeyusb.com/firmware/security/iv-generation
How ZeroKeyUSB creates the AES-CBC IV using the ATECC608A TRNG, validates it, and handles regeneration.
The Initialization Vector (IV) ensures that identical plaintext blocks produce different ciphertext under AES-128 CBC. ZeroKeyUSB generates the IV **once at provisioning** using the ATECC608A hardware TRNG, stores it in EEPROM, and validates it on every unlock.
***
## Generation procedure
Called as part of `storeSignature()` (the PIN setup routine):
1. `generateAndStoreIV()` calls `fillIVFromAtecc(iv)`.
2. `fillIVFromAtecc()` calls `zerokeyAtecc.random(buf)` — the ATECC608A `RANDOM` command with mode `0x00` (updates the internal DRBG seed before generating).
3. The first **16 bytes** of the 32-byte TRNG output are copied into `iv[16]`.
4. `ivIsValid()` checks the result: rejects all-`0x00` or all-`0xFF` (statistically impossible with a working TRNG, but guards against chip faults).
5. The IV is written to EEPROM at `0x0010–0x001F` via `eepromWriteRaw()`.
6. After IV storage, `eraseAll()` is called to reset all credential pages to encrypted blanks under the new IV.
The entire process runs on-device with no host involvement.
***
## EEPROM layout
| Address | Content |
| -------- | ---------- |
| `0x0010` | IV byte 0 |
| `0x0011` | IV byte 1 |
| … | … |
| `0x001F` | IV byte 15 |
***
## Validation on unlock
`loadIVfromEEPROM()` is called before every encrypt or decrypt operation:
1. Reads 16 bytes from `0x0010`.
2. If the I²C read fails → attempts TRNG regeneration and EEPROM re-write.
3. If `ivIsValid()` returns false (all-zero or all-FF) → regenerates from ATECC TRNG and stores.
4. If regeneration also fails → returns `false`; caller shows an error screen.
***
## Regeneration triggers
The IV is regenerated automatically when:
* The EEPROM read fails (I²C error).
* The stored value is all-`0x00` or all-`0xFF` (blank/corrupt EEPROM).
* The user generates a new PIN (the full `storeSignature()` flow re-generates the IV).
**Consequence of regeneration:** all credential pages were encrypted under the old IV. Regenerating the IV without also re-writing the ciphertext pages makes existing credentials unreadable. The `generateAndStoreIV()` function therefore calls `eraseAll()` immediately after storing the new IV to bring the EEPROM to a consistent (encrypted-blank) state.
***
## Why a single device-wide IV?
* Keeps the layout simple, deterministic, and fully auditable.
* CBC mode with a fixed IV across all slots does not weaken confidentiality as long as the AES key is secret and the IV itself was generated randomly — the key varies per device.
* Per-record IVs would require 16 additional bytes per credential slot and complicate the EEPROM layout significantly.
* The primary confidentiality guarantee comes from the 128-bit randomly generated AES master key, not from IV uniqueness across records.
The IV never leaves the device. Ciphertext extracted from one ZeroKeyUSB cannot be decrypted with another unit even if the PIN and device serial are known, because the AES master key is device-unique and lives inside the ATECC608A — there is no way to copy it out and use it elsewhere.
***
## Tamper detection
* `ivIsValid()` rejects obviously corrupt values (all-zero, all-FF).
* If the IV is flipped bit-by-bit in EEPROM, the next AES-CBC decryption will produce garbage for the first block of every slot (IV affects only the XOR input for block 0; subsequent blocks are self-synchronising in CBC decryption).
* There is no CRC or MAC stored alongside the IV in the current implementation. An attacker who can write arbitrary bytes to EEPROM address `0x0010` can force IV regeneration (and thus data loss) by corrupting those bytes.
***
## ATECC608A TRNG properties
* The `RANDOM` command with mode `0x00` updates the chip's internal DRBG seed from hardware entropy before returning 32 random bytes.
* The DRBG is designed to FIPS 140-2 requirements with a maximum seed life before forced re-seeding.
* Output is validated locally (`ivIsValid`) to catch pathological failures.
# PIN Verification
Source: https://docs.zerokeyusb.com/firmware/security/pin-verification
Unlock flow, SHA-256 hash comparison, and the persistent exponential backoff that rate-limits the Master PIN.
ZeroKeyUSB uses a Master PIN (1–16 digits) to authenticate the user. The verification process pairs a **software constant-time hash comparison** with a **persistent exponential backoff** that is re-applied on every boot, making brute-force attempts impractical without ever destroying stored data.
***
## How the PIN is stored
The PIN is **never stored in plaintext**. At PIN setup time (`storeSignature()`):
```mermaid theme={null}
flowchart LR
PIN["PIN digits pinArray[16]"] --> CONCAT["Concatenate"]
SERIAL["Chip serial 9 bytes from ATECC"] --> CONCAT
CONCAT --> SHA["SHA-256"]
SHA --> HASH["32-byte hash"]
HASH --> EEP["EEPROM @ 0x0048"]
HASH --> SLOT["ATECC Slot 9 (for future CheckMac)"]
style SHA fill:#dbeafe,stroke:#2563eb,color:#000
style EEP fill:#dcfce7,stroke:#16a34a,color:#000
style SLOT fill:#fef3c7,stroke:#d97706,color:#000
```
1. The user's PIN digits are read from `pinArray[16]` (each byte holds a digit value 0–9).
2. `derivePinKey()` computes: `SHA-256(pinArray[16] ∥ chip_serial[9])`.
* `chip_serial` is the 9-byte unique serial read from the ATECC608A Config Zone.
3. The resulting 32-byte hash is written to EEPROM at `0x0048–0x0067`.
4. The same 32-byte hash is also written to **ATECC slot 9** (for potential future CheckMac use).
The chip serial acts as a hardware salt: the same numeric PIN on a different device produces a completely different 32-byte hash.
***
## Unlock sequence
Each unlock attempt executes the following steps in `verifySignature()`:
```
On boot, before the PIN screen accepts any input:
waitFromEeprom() // replay the accumulated backoff for the stored fail count
Then each unlock attempt runs verifySignature():
1. derivePinKey(pinArray, derived):
serial = ATECC608A.readSerial()
derived = SHA-256(pinArray[16] || serial[9])
2. Read stored hash from EEPROM [0x0048] → stored[32]
3. diff = 0; for i in 0..31: diff |= stored[i] ^ derived[i] // constant-time
4. If diff == 0:
writeFailedAttemptsCounter(0) // clear the backoff
→ ACCESS GRANTED
5. Else:
incrementFailedAttemptsCounter()
waitFromEeprom() // exponential backoff
→ ACCESS DENIED
```
There is **no `Counter0` increment, threshold read, or automatic wipe** in this path. An earlier design used the ATECC608A's monotonic Counter0 to wipe the vault after 50 wrong PINs; that was removed. The live defence is the persistent backoff described below.
***
## Persistent rate-limiting — the real brute-force defence
There is **no automatic wipe** after a number of failed attempts; the vault is never destroyed by wrong PINs. Instead, every guess is slowed by an exponential backoff whose counter lives in EEPROM (`0x0002`) and therefore survives power loss.
The load-bearing detail is *when* the delay is applied. On every boot, `readConfigurationFlag()` calls `waitFromEeprom()` **before the PIN screen accepts any input**. So an attacker cannot skip the penalty by cutting power mid-countdown: after each failed guess the accumulated delay is re-imposed at the next power-up. Once the counter passes \~10 failures every further attempt costs ≈ 43 minutes, so online brute force is impractical (a 4-digit PIN would take on the order of a year) — all without ever destroying the user's data.
| Event | Failed-attempt counter (EEPROM `0x0002`) |
| ----------- | ---------------------------------------------------- |
| Wrong PIN | +1, then enforce the backoff delay |
| Correct PIN | reset to 0 |
| Power cycle | the delay for the stored count is re-applied at boot |
`eraseAll()` still exists, but it is only ever triggered **manually** by the user (factory reset / forgotten PIN) — never automatically by wrong PINs.
> **Offline caveat.** The rate limit only applies to guesses made through the device. The PIN hash is readable over I²C (EEPROM `0x0048` and ATECC slot 9 with `IsSecret=0`), so an attacker who physically reaches the I²C bus can copy the hash and the chip serial and crack the PIN offline with no delay. What stops that is the **epoxy encapsulation** blocking bus access — plus using a long PIN. It is not the backoff.
***
## Exponential backoff — delay schedule
Stored at EEPROM `0x0002` and re-applied at boot; reset only on a correct PIN:
| Failed attempts | Wait time |
| --------------- | ------------------ |
| 0 | none |
| 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) |
Formula: `wait = 5 × 2^(min(attempts, 10) − 1)` seconds, capped at 2 560 s.
During the delay the OLED shows a progress bar and countdown. The device does not accept new input until the timer expires.
***
## Secure input handling
* Digits are buffered in `pinArray[16]` in SRAM and cleared after verification.
* Touch events are ignored during the lockout wait (`waitFromEeprom()`).
* SerialUSB **cannot** inject PIN digits — only physical capacitive-touch input is accepted.
* The PIN comparison uses a constant-time XOR accumulator (`diff |= stored[i] ^ derived[i]`) to avoid timing side-channels.
***
## Changing the PIN
Initiated via **Menu → Change PIN** → `storeSignature()`:
1. 3-second on-screen countdown (allows safe abort).
2. `derivePinKey(pinArray, derived)` computes the new hash.
3. New 32-byte hash written to ATECC slot 9.
4. New 32-byte hash written to EEPROM `0x0048`.
5. The failed-attempts counter (EEPROM `0x0002`) is cleared.
6. ATECC ping confirms the chip is still alive. The AES key in slot 8 is **not touched** by PIN setup — it is provisioned once at first boot and is irrevocable.
7. IV is loaded or generated.
8. Setup config flag is written (`0x42`).
9. All credential slots are silently re-initialised with encrypted blanks.
Changing the PIN does **not** change the AES master key or re-encrypt existing credentials. The AES key lives inside ATECC slot 8 and is generated once per device; it cannot be rotated. Existing ciphertext is decryptable with the same chip as long as it is not destroyed.
***
## Forgotten PIN
ZeroKeyUSB has no PIN recovery mechanism. The only option is a **factory reset** (`eraseAll()`), which:
1. Shows a 3-second countdown.
2. Loads the device IV.
3. Overwrites all 61 credential slots × 4 pages with encrypted blanks.
4. Clears TOTP metadata.
After reset the device halts with a "LOCKED — reflash" error. The bootloader must be used to flash new firmware and re-provision the device from scratch.
Previously stored credentials are unrecoverable unless you have a plaintext backup exported before the reset.
Choose a PIN you can remember but others cannot guess. A PIN of 4 digits or fewer is vulnerable to offline SHA-256 dictionary attacks if an adversary gains I²C access to the device.
# Epoch Synchronization
Source: https://docs.zerokeyusb.com/firmware/totp/epoch-synchronization
Keep ZeroKeyUSB’s internal clock aligned so TOTP codes stay valid.
ZeroKeyUSB does not contain a real-time clock. Instead, it keeps track of time using the SAMD21 millisecond counter plus a stored Unix epoch. To maintain accuracy, the device occasionally needs the host to send the current time.
***
## When synchronization is required
* First boot or after a factory reset
* When the OLED displays `REQTIME`
* If login services report “invalid code” despite entering it immediately
* After long periods without power (several weeks)
The firmware triggers a sync request once drift exceeds ±90 seconds.
***
## Sync workflow
1. Unlock ZeroKeyUSB.
2. Connect to the serial interface via the web manager or CLI.
3. The device sends `REQTIME` to signal it needs the current epoch.
4. The host responds with `SETTIME `, for example `SETTIME 1706227200`.
5. ZeroKeyUSB stores the value in EEPROM (little-endian 64-bit) and resets its internal counters.
The entire exchange is local; no network connection is required.
***
## Checking drift manually
Run the CLI status command:
```bash theme={null}
zerokeyusb-cli status
```
Look for a line like `Clock drift: +18s`. If the value approaches ±60s, perform a new synchronization.
***
## Troubleshooting
| Symptom | Fix |
| ------------------------------------------ | ------------------------------------------------------------------------ |
| `REQTIME` persists after sending `SETTIME` | Ensure the epoch is in seconds (not milliseconds). |
| Codes always off by 30 s | Host clock likely misconfigured; verify OS time sync. |
| CLI cannot open port | Close other serial programs (e.g., Arduino IDE) that might be connected. |
Proper time alignment guarantees your TOTP codes match the server’s expectations.
# TOTP Module
Source: https://docs.zerokeyusb.com/firmware/totp/index
Generate offline 2FA codes alongside your stored passwords.
TOTP stands for **Time-based One-Time Password** — the same standard used by Google Authenticator or Authy. ZeroKeyUSB calculates each 6-digit code **offline**, using the encrypted secret stored in EEPROM and a locally maintained Unix time value.
***
## How it works
* Secrets are imported as **Base32 strings** and encrypted with AES-128 before being written to EEPROM.
* The firmware keeps an 8-byte Unix epoch counter in plaintext (for simplicity) and increments it using the SAMD21 millisecond timer.
* Every 30 seconds the device computes `Truncate(HMAC-SHA1(secret, epoch / 30))` and shows the result on the OLED.
Because the algorithm follows RFC 6238, the codes match any mainstream authenticator application while staying isolated from the Internet.
***
## Adding a TOTP secret
1. Unlock the device and open the credential you want to protect.
2. Use the **local web manager** or CLI to paste the `otpauth://` URI provided by the service.
3. The tool extracts the `secret=` parameter and sends it once over the secure serial channel.
4. ZeroKeyUSB encrypts the secret, stores it in the TOTP page, and flags the slot as 2FA-enabled.
Secrets are never shown in plain text after they are stored.
***
## Viewing codes
* Credentials with a TOTP secret show a `2FA → Touch to view` prompt beneath the password.
* Tapping the center pad reveals the current 6-digit code and a countdown ring that refreshes each second.
* The screen auto-hides after 15 seconds of inactivity to keep codes private.
If the device needs the current epoch, it displays `REQTIME` and waits for the host to send the time once.
***
## Keep it accurate
Understand how ZeroKeyUSB tracks Unix time and how to resync when drift occurs.
Step-by-step guide for using the browser-based utility to keep the TOTP clock aligned.
***
## Supported algorithms
| Algorithm | Status | Typical use |
| ----------- | ------------- | -------------------------------------------------- |
| **SHA-1** | ✅ Implemented | Most consumer services (Google, Microsoft, GitHub) |
| **SHA-256** | ⏳ Planned | High-security deployments |
| **SHA-512** | ⏳ Planned | Enterprise authenticator suites |
Future firmware releases can extend the hash options without changing hardware.
***
## Text notes instead of a code
The same per-credential field can hold a **plaintext note** — a recovery code,
a hint, anything worth jotting down — instead of a TOTP secret. You choose per
credential in the webtool: a selector marks the field as **2FA code** or **Note**.
* A note is **hidden when empty**, exactly like the 2FA field.
* When present it is **shown on the device** (scrolling if long) with a document
icon, so you can read it.
* It is **never typed over USB and never editable on-device** — notes are set
only from the webtool.
* Limit: **32 characters** (the field's storage capacity).
The field's type lives in its metadata byte (a `NOTE` marker), so the data needs
no special marker on-device; backups round-trip the type via a `note:` prefix, so
importing needs no manual re-marking.
***
## Best practices
* Resync time after long storage or travel across time zones.
* Keep an offline backup of your credentials before performing a factory reset.
* Treat printed or exported TOTP secrets as highly sensitive material.
With TOTP handled directly by the hardware key, your password and second factor stay together yet remain offline.
# Web Time Sync Tool
Source: https://docs.zerokeyusb.com/firmware/totp/web-time-sync-tool
Use the browser-based helper to send accurate Unix time to ZeroKeyUSB.
The ZeroKeyUSB firmware repository includes a lightweight web application that runs locally in your browser. It connects to the device over WebUSB and pushes the current epoch so TOTP codes stay in sync.
***
## Requirements
* Chromium-based browser (Chrome, Edge, Brave) with WebUSB enabled
* ZeroKeyUSB unlocked and connected via USB-C
* Local copy of the **`tools/web-time-sync`** directory served via `npm run dev`
No Internet access is necessary once the page is loaded.
***
## Launching the tool
```bash theme={null}
cd tools/web-time-sync
npm install
npm run dev
```
Open the printed local URL (typically `http://localhost:5173`) in your browser. You should see the ZeroKeyUSB logo and a “Connect” button.
***
## Sending the time
1. Click **Connect** and select your ZeroKeyUSB from the device list (`ZeroKeyUSB CDC`).
2. The page displays the current Unix epoch and a countdown to the next 30-second boundary.
3. Press **Sync now** when the device requests time (shows `REQTIME`).
4. The tool sends `SETTIME ` automatically and confirms success with a toast notification.
If the device was already in sync, it responds with `OK` and no changes are made.
***
## Safety features
* The tool only communicates with USB devices whose vendor/product IDs match ZeroKeyUSB.
* All commands are visible in the on-screen console for auditability.
* No data leaves the browser tab; telemetry and analytics are disabled.
Close the tab when finished. Leaving WebUSB connections open can prevent other applications from accessing the serial port.
***
## Troubleshooting
| Issue | Resolution |
| ------------------------- | ------------------------------------------------------------------------------- |
| Browser cannot access USB | Make sure you are using a Chromium browser and have granted device permissions. |
| `Failed to send epoch` | The device might be locked; unlock it and try again. |
| Tool closes unexpectedly | Check the terminal running `npm run dev` for errors and restart the dev server. |
The web time sync tool offers a user-friendly way to keep your hardware authenticator aligned without installing heavyweight software.
# USB Utilities
Source: https://docs.zerokeyusb.com/firmware/usb-utilities
Keyboard emulation, serial commands, and backup/restore over the composite USB interface.
## Dual USB personality
ZeroKeyUSB operates as a **composite USB Full-Speed device** exposing two interfaces simultaneously:
```mermaid theme={null}
graph LR
USB["USB-C Connector"] --> COMP["Composite USB Device"]
COMP --> HID["HID Keyboard Class 0x03"]
COMP --> CDC["CDC Serial 115200 bps"]
HID --> TYPE["Types credentials to host"]
CDC --> CMD["Backup, restore, time sync"]
style USB fill:#dbeafe,stroke:#2563eb,color:#000
style HID fill:#bbf7d0,stroke:#16a34a,color:#000
style CDC fill:#fef3c7,stroke:#d97706,color:#000
```
Both interfaces remain active after boot, but CDC commands that modify data require PIN unlock + on-device authorization.
***
## Keyboard output engine
The firmware supports **9 keyboard layouts** stored as compiled keyboard maps:
| Code | Layout |
| ------- | ------------------------------ |
| `EN-US` | United States QWERTY (default) |
| `DA-DK` | Danish |
| `DE-DE` | German |
| `ES-ES` | Spanish |
| `FR-FR` | French |
| `HU-HU` | Hungarian |
| `IT-IT` | Italian |
| `PT-PT` | Portuguese |
| `SV-SE` | Swedish |
The active layout is stored in EEPROM at `0x003E` and can be changed from **Settings → Keyboard** or during the setup wizard.
### Typing sequence
When you tap **Center** on the credential main screen, ZeroKeyUSB types:
```mermaid theme={null}
sequenceDiagram
participant User
participant Device
participant Host as Host Computer
User->>Device: Tap Center on credential
Device->>Host: Type username (char by char)
Device->>Host: Send TAB key
Device->>Host: Type password (char by char)
Note over Device,Host: Each character sent as USB HID keypress + release
```
The typing engine in `zerokey-utils.cpp` converts each ASCII character to the appropriate HID keycode using the selected keyboard layout library.
***
## Serial command protocol
The CDC channel communicates at **115200 bps** using simple ASCII lines. Commands are processed by `handleIncomingHostRequests()` in `zerokey-io.cpp`.
| Command | Direction | Pre-condition | Description |
| --------------- | ------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EXPORT` or `R` | Host → Device | PIN unlocked | Initiates credential export. Device shows authorization prompt. |
| `IMPORT` | Host → Device | PIN unlocked | Initiates credential import. Device shows authorization prompt. |
| `` | Host → Device | Device showing `REQTIME` | Sends Unix epoch timestamp for TOTP synchronization. |
| `ZK PING` | Host → Device | — (always) | Identity probe. Replies `ZK PONG ON` / `ZK PONG OFF`. Used by the [browser extension](/getting-started/browser-extension) to detect the device and read the link state. |
| `FIND ` | Host → Device | `Chrome: On`, unlocked, browsing | Jumps the alphabet search to ``. Replies `OK FIND `, or `ERR OFF` / `ERR BUSY` / `ERR EMPTY`. Navigation only — never types or reveals a credential. |
| `TIME ` | Host → Device | `Chrome: On` | Sets the device clock from the host (UTC Unix seconds) — the same value the [time-sync tool](/firmware/totp/web-time-sync-tool) sends via `REQTIME`, but pushed instead of requested. Replies `OK TIME`. The [browser extension](/getting-started/browser-extension) sends it on every use so TOTP codes stay accurate. |
`FIND` only *navigates* the on-device search. There is deliberately no serial
command that types or reveals a credential; typing always requires a physical
press. `FIND` is ignored unless **Tools → Chrome** is on and the vault is
unlocked and on the credentials list.
### Export data format
Each credential is sent as a CSV line:
```
slotIndex,siteName,userName,password[,totpSecret]
```
* The TOTP field is optional and only included if the slot has a 2FA secret.
* The first line sent is the total number of slots (`61`).
* Example: `0,github.com,alice,MyP@ss123,JBSWY3DPEHPK3PXP`
### Import data format
Same CSV format. The host sends:
1. The total number of records (integer).
2. One line per record: `slotIndex,site,user,pass[,totpSecret]`.
The device encrypts each field with AES-128 CBC and writes to the corresponding EEPROM slot.
***
## Time synchronization
TOTP codes require accurate time. Since ZeroKeyUSB has **no hardware RTC**, time is tracked using `millis()` drift from a synced epoch.
```mermaid theme={null}
sequenceDiagram
participant Device
participant Host
Device->>Host: "REQTIME" (via SerialUSB)
Note over Device: Shows "Time not set" on OLED
Host->>Device: "1714328400" (Unix epoch)
Device->>Device: syncTotpEpoch(epoch)
Device->>Device: Save to EEPROM 0x0040
Note over Device: TOTP codes now available
```
* The epoch value must be between `946684800` (2000-01-01) and `4102444800` (2099-12-31).
* The saved epoch persists across power cycles in EEPROM at `0x0040–0x0047`.
* On each boot, the last saved epoch is loaded and `millis()` tracking resumes from that point.
* Drift accumulates over time; long sessions or frequent unplugging may require re-sync.
***
## Bootloader entry
From **Menu → Danger Zone → Bootloader Mode**, the firmware:
1. Writes `0xF01669EF` to SRAM address `0x20007FFC` (the double-reset magic word).
2. Calls `NVIC_SystemReset()`.
3. The bootloader sees the magic word and stays in USB-CDC DFU mode for firmware flashing.
This allows firmware updates without physical access to SWD pogo pins.
***
## Serial security model
* **Before PIN unlock:** `EXPORT` and `IMPORT` commands are rejected with `ERR LOCKED`.
* **After PIN unlock:** commands are accepted but require **on-device authorization** (long-press Center) before any data transfer begins.
* **During transfer:** the device shows a progress screen and rejects new commands with `ERR BUSY`.
* **No hidden commands:** the serial protocol has no debug, dump, or diagnostic commands in production firmware.
All serial communication is plaintext. There is no encryption layer on the CDC channel. Treat the USB connection as a direct wire to the device's internals.
# Bitcoin Wallet
Source: https://docs.zerokeyusb.com/getting-started/bitcoin
Create an airgapped Bitcoin wallet on the device, export a watch-only key to your phone, and sign transactions (PSBT) — the private key never leaves the device.
ZeroKeyUSB can work as an **airgapped Bitcoin wallet**. It creates a standard
12-word Bitcoin wallet **on the device**; the seed is shown only on the screen
and **never leaves over USB**. You watch balances and build payments in a normal
wallet app (phone or desktop), and only the **signing** happens on ZeroKeyUSB.
Find the Bitcoin menu under **Tools → Bitcoin**. Mainnet only.
| Menu item | What it does |
| ----------------- | -------------------------------------------------------------------------- |
| **Show seed** | Re-displays the 12 words on the OLED (screen only) |
| **Watch-only** | Sends your **public** account key to a wallet app so it can watch balances |
| **Create wallet** | Generates a new 12-word seed (overwrites any existing one) |
***
## 1 · Create the wallet
If a wallet already exists, the device asks you to **hold Center** to confirm the overwrite (this **destroys** the old seed). On a fresh device it starts straight away.
The device shows the words in pages of three. Advance by tapping. **Write them on paper only** — never photograph them or type them into a computer.
Anyone with these 12 words controls the funds. The device keeps the seed encrypted; the paper is your only backup.
**Create wallet** generates a brand-new random seed and overwrites the previous one. Coins on the old wallet are lost unless you backed up its 12 words.
You can re-check the words any time with **Show seed**.
***
## 2 · Set up a watch-only wallet (phone/desktop)
To see balances and build payments you import the **public** account key into a normal wallet (BlueWallet, Nunchuk, Sparrow, Bitcoin Core…). This key can only *watch* your wallet — it can never spend.
Open `bitcoin.html` in Chrome or Edge (Web Serial), **Connect device**, and unlock the device with your PIN.
Press **Get watch-only**. The page shows a **QR code** with your public account key.
Scan the **descriptor QR** (or the `zpub` QR) into your mobile wallet. It can now show balances and create transactions, but it cannot spend — it has no private key.
You can also trigger the export from the device itself: **Tools → Bitcoin → Watch-only** prints the same data over the serial connection.
***
## 3 · Sign a transaction (PSBT)
The flow is the standard airgapped PSBT flow: build the spend on your watch-only wallet, sign it on ZeroKeyUSB, then broadcast from the wallet.
In your watch-only wallet, create the transaction and **export the unsigned PSBT** (base64).
In `bitcoin.html`, paste the PSBT and press **Send to device**.
The OLED shows the destination, the amount being sent and the fee. **Verify these on the device screen**, not in the browser.
Hold **Center** to sign (a progress border fills around the screen). Tap any other button to cancel. The device returns the **signed PSBT** to the webtool.
Copy the signed PSBT back into your wallet, finalise and broadcast.
Always confirm the **address and amount on the device's own screen** before holding to sign. A compromised computer can show you one thing in the browser while sending another — the device screen is the trusted display.
If the PSBT has no inputs belonging to this wallet, the device replies **"No inputs for us"** and signs nothing — that is expected when the PSBT is for a different wallet.
***
## Your keys stay safe
* The 12-word seed **never leaves the device** — it is shown only on the screen and kept encrypted with your PIN.
* Only **public** data and **signatures** ever go over USB — never the seed.
* Every signature needs a deliberate **Center hold** after you check the address and amount **on the device's own screen**. It never signs blindly.
Want the technical details — how the wallet is derived, where the randomness comes from, and how to verify it yourself? See **[Bitcoin signer](/firmware/bitcoin-signer)** in the Software section.
Entropy, derivation, storage and signing — verifiable against the source.
Where to find Tools → Bitcoin.
Recover access if the display fails.
# Browser extension
Source: https://docs.zerokeyusb.com/getting-started/browser-extension
Install the ZeroKeyUSB Web Link and fill logins with one click.
The optional **ZeroKeyUSB Web Link** extension for Chrome/Edge speeds up logins:
click the toolbar icon on a login page and the device jumps to the matching
site and focuses the login field — you just pick the site on the device and it
types the credential.
The extension never sees your password. It only tells the device which letter to
look for and focuses a field; the credential is typed by the device after you
physically pick it. How it works under the hood: [Browser link](/firmware/browser-link).
## Install
Open `chrome://extensions`, turn on **Developer mode**, choose **Load unpacked**
and select the `chrome-extension/` folder.
Click the toolbar icon → **Connect ZeroKeyUSB…**, then pick the device's port on
the page that opens. It is remembered afterwards.
The toolbar icon shows a **green dot** when the device is linked and a **grey
dot** when it isn't.
## Using it
Go to a website where you have an account, and make sure the ZeroKeyUSB is
plugged in and **unlocked**.
The device jumps to the site's first letter and the page's login field gets
focused.
Choose the matching site on the device and press — it types your username and
password.
## Turning it off
Don't want it? On the device, set **Menu → Tools → `Chrome: Off`** and it will
ignore the extension entirely. It ships **On**.
It works on most login pages. Sites that split the username and password across
separate screens (some Google/Microsoft flows) may not fill correctly.
# Create a Backup
Source: https://docs.zerokeyusb.com/getting-started/creating-backups
Export all your credentials as CSV over USB serial. Screen by screen, from the menu to the saved file.
A backup protects you against loss or damage of the device. ZeroKeyUSB makes the process **deliberate**: no credential leaves the device until you physically press the authorization button.
The backup is emitted as **plaintext** over USB serial. Encrypt it right after capturing it (GPG, age, 7z with password…) and store it offline.
***
## Before you start
| You need | For |
| ------------------------------ | ---------------------------------------------------------------- |
| The device unlocked | Export only works with the PIN already entered |
| A serial tool | Web manager, `screen`, `minicom`, PuTTY or similar at 115200 bps |
| A safe place to store the file | Encrypted disk, locked SD card, etc. |
***
## Step 1 — Open the menu
From the first credential, press **Left**. (You can also get there by pressing **Right** from the last credential → "Add New" → Right.) See [Navigate the menu](/getting-started/menu-navigation) if in doubt.
*Press **Left** while on credential 1.*
***
## Step 2 — Select "Tools"
On current firmware this menu is labelled **Tools** (it was **Backup** when these screenshots were taken, and it now also contains the Bitcoin wallet). The position and steps are identical — Export/Import live inside it.
In the root menu, **Tools** is selected by default. Press **Center** to enter the submenu.
*Press **Center**.*
***
## Step 3 — Select "Export"
Inside Backup you have `Import`, `Export` and `< Back`. Press **Down** once to highlight `Export`, then **Center**.
*Press **Down** to reach `Export`, then **Center** to activate.*
***
## Step 4 — Physical authorization
After pressing Center, the authorization screen appears. This is a **security measure**: the device doesn't export until you hold Center for more than 800 ms (you'll see the large button halo).
*Before continuing, open your serial terminal on the host and connect to the device (115200 bps), or open the [web manager](https://zerokeyusb.com/manager). When it's ready to receive, **hold Center for \~1 second**.*
Changed your mind? **Release before 800 ms** or press **Left** — the operation is cancelled and you return to the menu.
***
## Step 5 — Send progress
After authorizing, the device decrypts each credential (one by one) and sends it as a CSV line over USB. The screen shows the progress.
*You don't have to press anything. The device walks through all 61 slots and on completion shows "Export complete" for 1 second before returning to the menu.*
***
## Step 6 — Capture the CSV on the host
While the device sends, on your serial terminal you'll see lines appear:
```csv theme={null}
61
0,Google,user1,contrasena1,JBSWY3DPEHPK3PXP
1,Apple,user2,password,
2,Netflix,user3,Atalaya,
3,github,user4,no me acuerdo,JBSWY3DPEHPK3PXP;algo=SHA256
4,Amazon,comprador99,123456,
...
```
| Field | Meaning |
| ------------ | --------------------------------------------------------- |
| Line 0 | Total number of slots (always `61`) |
| `slotIndex` | 0–61 |
| `site` | Site (≤32 chars) |
| `username` | Username (≤32 chars) |
| `password` | Password |
| `totpSecret` | Optional — Base32 (with optional `;algo=SHA256`) or empty |
Save the whole output to a file (`zerokeyusb-2026-05-27.csv`, for example). The web manager has a "Save" button that does this for you.
***
## Step 7 — Encrypt immediately
The CSV you just saved contains **all** your passwords in cleartext. Encrypt it as soon as you've captured it and delete the unencrypted version.
```bash theme={null}
# Option A: GPG (symmetric, AES-256)
gpg -c --cipher-algo AES256 zerokeyusb-2026-05-27.csv
shred -u zerokeyusb-2026-05-27.csv
# Option B: age with passphrase
age -p -o zerokeyusb-2026-05-27.csv.age zerokeyusb-2026-05-27.csv
shred -u zerokeyusb-2026-05-27.csv
# Option C: 7z with a strong password (Windows)
7z a -p -mhe=on zerokeyusb-2026-05-27.7z zerokeyusb-2026-05-27.csv
del zerokeyusb-2026-05-27.csv
```
***
## When to back up
| When | Why |
| --------------------------------------------- | -------------------------------- |
| After adding or importing several credentials | So you don't lose new work |
| Before factory reset or firmware reflashing | The reset is irreversible |
| Before any **Danger** submenu action | Same |
| Periodically | As part of your security routine |
***
## Best practices
| Practice | Why |
| ---------------------------------------- | ---------------------------------------------- |
| Encrypt right away | Plain CSV is a huge risk |
| Store in ≥ 2 separate physical locations | Protects against single-disk/cloud failure |
| Test a restore now and then | Guarantees the file is usable when you need it |
| Shred old backups | Reduces exposure window |
| Name with date (`YYYY-MM-DD`) | So you know which is most recent |
***
## Next steps
Restore from a backup or migrate from another manager.
Learn the rest of the menu options.
# Edit a Credential
Source: https://docs.zerokeyusb.com/getting-started/edit-credential
How to open the editor, move the cursor, use the three keyboard pages, generate random passwords and save.
This guide details every operation in the on-device editor. If you just want to create your first credential, start with the [Create your first credential](/getting-started/first-credential) guide.
***
## Step 1 — Select the credential
From the main screen, navigate to the credential you want to edit using **Left/Right**. If you have many, hold **Left/Right** to jump 10 slots at a time.
*When the right credential is on screen (you'll see its number at the top left — here `4`), move on to the next step.*
***
## Step 2 — Select the field and open the editor
The active field shows as an inverted white block on the left (globe = site, silhouette = user, padlock = password, key = 2FA). Switch between fields with **Down/Up**:
* **Site** ↓ **User** ↓ **Pass** ↓ **2FA** (if the credential has TOTP)
When you're on the right field, **hold Center for \~1 second** to enter the editor.
*A **short** Center press types the field to the host (not what you want). A **long** press (\~800 ms, until you see the large halo) opens the editor.*
***
## Step 3 — Editor anatomy
The editor looks like this:
| Element | Function |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Row 1 | Shows the current field content (**up to 32 chars**, scrolls horizontally past 16) with an inverted cursor at the insertion point |
| Row 2 — `<` `>` | Move the **field** cursor (insertion position) |
| Row 2 — KB1 | Uppercase keyboard: `A B C D E F G H I J K L M N O P` |
| Row 3 — `Rand` | Fills the field with strong random characters from the hardware TRNG (format set in Settings) |
| Row 3 — KB2 | Lowercase keyboard: `a b c d e f g h i j k l m n o p` |
| Row 4 — `Save` | Saves changes and returns to the main view |
| Row 4 — KB3 | Numbers/symbols: `0 1 2 3 4 5 6 7 8 9 - + ! @ #` |
***
## Step 4 — Move the cursor across the keyboard
When you enter the editor, focus starts on KB1 (row 2). **Left/Right** move the keyboard cursor within the current row.
*Press **Right** to advance the keyboard cursor, **Left** to go back. At the end/start, the cursor jumps to the control in the adjacent row (`Rand`, `Save`, `<`, `>`).*
***
## Step 5 — Insert a character
With the desired character under the keyboard cursor, press **Center**. It gets inserted at the field cursor's current position, which then advances automatically.
*The `L` is inserted at position 5 of the field and the field cursor advances to position 6. If you wanted to replace instead of insert, read the next step.*
***
## Step 6 — Move the **field** cursor (not the keyboard)
Sometimes you want to edit characters in the middle of the field, not just append to the end. For that, use the `<` and `>` symbols on row 2.
*Reach the `<` or `>` symbol with **Left** (from KB1) and press **Center**. The **field** cursor moves one position left or right. When it's on the position you want to edit, go back to KB1/KB2/KB3 and press **Center** on the new letter — it overwrites the existing one.*
Keyboard insertions **overwrite** the character at the current position, they don't push existing content. So "deleting" a character is simply moving onto it and entering a space (KB3, first character).
***
## Step 7 — Generate a random password
For password fields, instead of typing letter by letter, use **Rand**. It generates a strong random password (**up to 32 characters**).
The **format** follows what you picked in **Settings → `Pwd:`** (see [Menu](/firmware/menu)). Six formats are available:
| Setting label | Format | Example |
| ------------------- | ----------------------------------------------------------------- | ---------------------------------- |
| `Symbols` (default) | 32 chars from the full printable set (letters, digits, symbols) | `t7#Kp!2r$Qm9^Za5Wd8&Bv3Ln6@Xj1Qz` |
| `Numeric` | 32 digits | `48201937560428173905641820937584` |
| `a-z 0-9` | 32 lowercase letters + digits | `k3m9xq1z7r4p2n8wj5c6vb0hd1ft9gs2` |
| `Aa-z 0-9` | 32 mixed-case letters + digits | `Kp3Mx9Qz1Rt4Nb8yWd2Fc7Hj5Lv0Gs6R` |
| `Words` | Capitalised BIP39 words joined by a random separator (`-_.!@#*+`) | `Ocean-Cargo-Mint-River-Sun` |
| `Words+Num` | Same, plus a 2-digit group after the first word | `River_28_Mango_Ocean_Tiger` |
The word formats use a built-in word list and fit as many words as they can under 32 characters. `Rand` on the **site** or **user** fields always uses the full `Symbols` set regardless of this setting.
*Navigate to `Rand` with **Down** from row 2. Press **Center**: the field fills up. If you don't like it, press **Center** again to regenerate.*
***
## Step 8 — Save
When you're done, navigate to `Save` (bottom-left corner) and press **Center**. The changes are encrypted with AES-128 CBC and written to EEPROM.
*The editor closes and you return to the main view. The credential now has the changes.*
Leaving the editor without pressing **Save** **discards** the changes. There is no confirmation: the moment you change screen via any path other than `Save`, what you typed is lost.
***
## Shortcut table
| Action | How |
| ------------------------ | ------------------------------------------------------------ |
| Enter the editor | Long-press **Center** on a field |
| Move keyboard cursor | **Left/Right** |
| Change row/keyboard | **Up/Down** |
| Insert character | **Center** on the letter |
| Move **field** cursor | **Center** on `<` or `>` (row 2) |
| "Delete" a character | Move the cursor there and insert space (KB3 first character) |
| Generate random password | **Center** on `Rand` |
| Save and exit | **Center** on `Save` |
| Discard changes | Unplug USB before pressing Save |
***
## Next steps
The TOTP field is imported over USB-CDC only, not editable on-device.
Save your current state before editing many credentials.
# Create Your First Credential
Source: https://docs.zerokeyusb.com/getting-started/first-credential
From unlocking the device with your PIN to saving your first password using 'Add New' and the on-device editor.
This guide assumes you have already finished the [initial tutorial](/getting-started/setup-wizard). If not, do it first.
***
## Step 1 — Unlock with the PIN
When you plug the device in, the PIN numpad appears. For each digit: **Up/Down** to the correct value, **Right** to confirm.
*When you finish the last digit, the cursor jumps to the tick (✓). Press **Center** on the tick to validate.*
***
## Step 2 — "Add New" appears automatically
If this is the first time (no credentials yet) or you have navigated to the end of your list, the **Add New** screen appears — a centered button.
*Press **Center** to create an empty credential in the first available slot.*
If you already have credentials, you can also reach this screen from the main view by pressing **Right** repeatedly until you pass the last one.
***
## Step 3 — Freshly created credential
The device creates a slot with placeholder values (`nuevo`/`usuario`/`clave`) and takes you directly to its main view. On the left you'll see the slot number (`1`).
*To start filling in the site, **press and hold Center for \~1 second** (you'll see the large halo appear). When you release, you enter the editor.*
***
## Step 4 — Editor: the first letter
The editor occupies the whole screen. It has 4 rows:
1. **Top:** the field being edited (16 slots).
2. **Row 2:** `< >` controls to move the cursor + the **KB1** keyboard (uppercase `A-P`).
3. **Row 3:** `Rand` (random fill) + **KB2** (lowercase `a-p`).
4. **Row 4:** `Save` + **KB3** (numbers and symbols).
Let's start by typing the first letter. We are in `KB1` with the cursor over `G`.
*Use **Right/Left** to move the cursor across the keyboard and **Center** to insert the selected character into the field. The field cursor (where insertion happens) advances automatically.*
***
## Step 5 — Switch keyboard page
To access lowercase (KB2) or numbers/symbols (KB3), use **Down** from KB1.
*Press **Down** to jump from KB1 to KB2 (and again to reach KB3). **Up** goes back. Keep inserting letters with **Center**.*
To move the cursor **within the text field** (not within the keyboard), use the `<` and `>` symbols in the second row — reachable when you're in KB1 with the keyboard cursor on its first position and you press **Left** one more time.
***
## Step 6 — Save the credential
When you finish typing, navigate to **Save** (row 4, left column) using **Down** from `Rand` or **Left** from KB3, and press **Center**.
*With `Save` highlighted in white, press **Center** to save and return to the main credential view. What you wrote is encrypted and saved before exiting.*
***
## Step 7 — Move to the User field
Back on the main view you'll see the site (`github`) and the rest of the fields still empty. Use **Down** to switch from **Site** to the **User** field.
*Press **Down**. The icon switches from site (globe) to user (silhouette), confirming the next edit applies to the user field. Then **hold Center** to enter the editor.*
***
## Step 8 — Generate a random password with Rand
Repeat the steps for the User field. Then navigate to **Pass** (another Down press) and enter the editor. But this time, instead of typing it manually, use the **Rand** option to have the device generate a strong 12-character password.
*With `Rand` highlighted, press **Center**. The `Pass` field is instantly filled with a strong random password. Then move down to `Save` and press **Center** to save.*
The generated password stays in the field. If you don't like it, press **Center** on `Rand` again to regenerate.
***
## Step 9 — Done
You're back on the main view. The credential already has its site, user and password saved securely. If you press **Center** on the Site field now, ZeroKeyUSB will type `username` + `TAB` + `password` + `ENTER` to the host, as if you were typing on a normal keyboard.
*With your cursor on the login page's username field, short-press **Center**. The device writes user + TAB + password + ENTER automatically.*
***
## Next steps
Learn how to modify existing fields and use editor shortcuts.
Add TOTP codes to your credentials for two-factor logins.
Export your freshly created credentials to a safe file.
Discover Tools, Settings, Danger and Info from the credential list.
# Import Credentials
Source: https://docs.zerokeyusb.com/getting-started/importing-credentials
Restore from a backup or migrate from another password manager. Screen by screen, from the menu to imported data.
Import loads credentials (site, username, password and optionally a TOTP secret) from a CSV file over USB serial. It happens **after unlocking** the device and requires **physical authorization** via the Center button.
Import **overwrites** the destination slots without asking. If you already have credentials, **create a backup** ([guide](/getting-started/creating-backups)) before continuing.
***
## Before you start
| You need | How to get it |
| -------------------------------- | ------------------------------------------------------------------------------------------------------ |
| The device unlocked with PIN | Plug it in and enter your PIN |
| A CSV file with your credentials | A previous export, or an export from 1Password / Bitwarden / Keepass adapted to the format (see below) |
| A serial tool | [Web manager](https://zerokeyusb.com/manager) (recommended), `screen`, `minicom`, PuTTY at 115200 bps |
***
## Step 1 — Open the menu
While on the first credential, press **Left**.
*Press **Left** while on credential 1.*
***
## Step 2 — Enter the Tools submenu
On current firmware this menu is labelled **Tools** (it was **Backup** when these screenshots were taken, and it now also contains the Bitcoin wallet). The position and steps are identical — Export/Import live inside it.
In the root menu, **Tools** is selected by default. Press **Center**.
*Press **Center**.*
***
## Step 3 — Select "Import"
Inside Backup, `Import` is at the top. If it's not highlighted, press **Up** to reach it. Then **Center**.
*With `Import` highlighted, press **Center**.*
***
## Step 4 — Physical authorization
Just like in export, an authorization screen asks for a long Center press before enabling the import channel.
*Before continuing, prepare your CSV file on the host. When ready, **hold Center for \~1 second**. The device sends `REQUEST_SAVE` over USB serial waiting for the data.*
**Don't press Center short by mistake** — a short press won't authorize anything (it just moves to the next menu screen if there is one).
***
## Step 5 — Send the CSV from the host
With the device in "waiting for data" mode, send over USB serial:
1. A line with the **total number of records** to import (example: `5`).
2. One CSV line per credential in this format:
```csv theme={null}
slotIndex,site,username,password[,totpSecret]
```
Full example:
```csv theme={null}
5
0,github.com,alice,MyP@ss123,JBSWY3DPEHPK3PXP
1,gmail.com,bob@gmail.com,correct horse battery staple
2,bank.com,12345678X,s3cur3P@ss,JBSWY3DPEHPK3PXP;algo=SHA256
3,aws-prod,admin,A!7zQ#mYpL2v
4,banca,12345678X,Pin-only2FA
```
Each line is processed like this:
```
Host → device: "0,github.com,alice,MyP@ss123,JBSWY3DPEHPK3PXP"
Device: encrypts it and stores it in slot 0
Device → host: "Record 1 stored correctly."
```
The [web manager](https://zerokeyusb.com/manager) has a file picker that sends the lines in the right order and shows progress. If you don't want to fight with the terminal, use it.
***
## Step 6 — Progress
During import you'll see the slot being written and overall progress on screen.
*When done, the device shows "Import complete" for 1 second and returns to the menu. Press **Left** to exit to the credential list and verify they've been added.*
***
## Step 7 — Verify
Press **Left** repeatedly to exit the menu to the credential list. Navigate with **Left/Right** and check that the imported data appears.
*Use **Right** to walk through the newly imported credentials. If everything looks good, consider making a [new backup](/getting-started/creating-backups) now.*
***
## CSV format
| Field | Description | Limit |
| ------------ | ---------------------- | ------------------- |
| `slotIndex` | Target slot | 0–61 |
| `site` | Site or service | 32 characters |
| `username` | Username | 32 characters |
| `password` | Password | Any printable ASCII |
| `totpSecret` | (optional) TOTP secret | See below |
### Accepted TOTP secret formats
| Format | Example |
| --------------------- | ---------------------------------------------------------------------- |
| Bare Base32 | `JBSWY3DPEHPK3PXP` |
| Base32 + algorithm | `JBSWY3DPEHPK3PXP;algo=SHA256` |
| Full `otpauth://` URI | `otpauth://totp/GitHub:alice?secret=JBSWY3DPEHPK3PXP&algorithm=SHA256` |
Default algorithm if not specified: **SHA-1**.
***
## Validation and errors
| Case | Behaviour |
| ----------------------------- | ---------------------------------------- |
| `slotIndex` outside 0–61 | Reject the line: `"Index out of range"` |
| Invalid Base32 | Reject the TOTP secret: `"TOTP invalid"` |
| Line with fewer than 3 fields | Skipped with an error log |
| Empty line | Terminates the import early |
***
## Migrating from other managers
To migrate from 1Password, Bitwarden, Keepass and others:
1. Export to CSV in the source manager.
2. Adapt the CSV to the ZeroKeyUSB format (a simple Python script — trim each field to 32 chars and reorder columns).
3. Import following this guide.
There are conversion script templates in the [tools repo](https://github.com/Depbit-lab/zerokeyusb-tools) — `tools/convert_.py`.
***
## Next steps
Export the freshly imported data before doing more work.
If you imported TOTP secrets, see how to view and type them.
# Navigate the Menu
Source: https://docs.zerokeyusb.com/getting-started/menu-navigation
How to access the main menu from the credential list and move through Tools, Settings, Danger and Info.
The main menu holds the actions that are **not** credentials: tools (backups and the Bitcoin wallet), screen/layout settings, dangerous options (factory reset) and firmware info. This guide shows you how to get there and how to move inside it.
***
## Route A — From the first credential
When you're on credential 1, **Left** opens the menu directly.
*Press **Left** while on credential number 1.*
***
## Route B — From the last credential (via "Add New")
If you're near the end of your list, another way is **Right** repeatedly: after the last credential the "Add New" screen appears, and another **Right** jumps to the menu.
*From "Add New", press **Right** to reach the menu (without creating anything). If instead you want to create a credential, press **Center**.*
***
## Jump 10 at a time
Hold **Left** or **Right** (long-press) while browsing credentials to jump **10 used slots** at once in that direction, instead of one. A short tap still moves one slot. Handy when you have many credentials.
***
## Menu screen
Once inside, you see four options in the root menu. The inverted `MENU` bar occupies the leftmost 10 pixels as a visual marker.
| Item | What it does |
| ------------ | -------------------------------------------------------------------------------------- |
| **Tools** | `Export` / `Import` credentials over USB, plus the **Bitcoin** wallet |
| **Settings** | Rotate screen, UI language, keyboard layout, screen-reader mode and re-launch tutorial |
| **Danger** | Factory reset (wipes **everything**) and bootloader mode (for reflashing) |
| **Info** | Firmware version, serial number and status |
*Move between items with **Up/Down**. To enter a submenu, press **Center**. To go back to the parent menu, press **Left**.*
***
## Enter a submenu
For example, inside **Settings**:
| Item | Action |
| --------------- | --------------------------------------------------------------------------------------------------------- |
| Rotate screen | Rotates the display 180° (controls flip too) |
| Language: XX | Cycles UI language (Spanish ↔ English) |
| Keyboard: XX-XX | Cycles the USB keyboard layout |
| Reader: On/Off | Persists the [screen-reader mode](/getting-started/recovery-no-screen) across reboots |
| Pwd: XX | Chooses the random-password format used by `Rand` (Symbols, Numeric, a-z 0-9, Aa-z 0-9, Words, Words+Num) |
| Tutorial | Re-launches the first-boot wizard |
| `< Back` | Returns to the parent menu (equivalent to pressing Left) |
*Pick an item with **Up/Down** and press **Center** to run it. **Left** returns to the root menu.*
***
## Leaving the menu
From the root menu, pressing **Left** takes you to the "Add New" screen (jumping one credential back), and another **Left** to the last credential. Pressing **Right** takes you directly to the first credential.
| Action in root menu | Result |
| ------------------- | ------------------------------------------ |
| **Left** | "Add New" → another LEFT → last credential |
| **Right** | First credential |
| **Up/Down** | Navigate items |
| **Center** | Enter submenu / run item |
Navigation is **circular**: credentials → "Add New" → menu → first credential → … No dead ends.
***
## Submenus — quick reference
### Tools
| Item | Action |
| ------- | ----------------------------------------------------------------------------------------------------------------------- |
| Export | Send all credentials over USB-CDC (see [guide](/getting-started/creating-backups)) |
| Import | Receive credentials over USB-CDC (see [guide](/getting-started/importing-credentials)) |
| Bitcoin | On-device Bitcoin wallet: create, view seed, watch-only export and PSBT signing (see [guide](/getting-started/bitcoin)) |
### Danger
Actions in the **Danger** submenu are irreversible. Factory reset, bootloader and the credential Import/Export now require **holding Center** (\~1.5 s) to confirm — a single tap will not trigger them, and **Left** cancels. A progress border fills around the screen while you hold.
| Item | Action |
| ------------- | ----------------------------------------------------------------------------------------- |
| Factory reset | Wipes **all** credentials and resets the PIN. The device returns to the initial tutorial. |
| Bootloader | Restarts the device in flash mode to upload new firmware. |
### Info
Shows firmware version (`v1.0`), device serial number and integrity check. Informational only — items are non-interactive except `< Back`.
***
## Next steps
Create a wallet, export watch-only and sign transactions.
Read the screen over USB if the display fails.
First use of the Tools → Export submenu.
Restore from a backup or migrate from another manager.
# Broken / No Screen Mode
Source: https://docs.zerokeyusb.com/getting-started/recovery-no-screen
If the OLED fails, ZeroKeyUSB can type what would be on screen over USB so you can still enter your PIN and use the device.
If the display breaks, the device is not bricked. A **screen-reader mode** makes ZeroKeyUSB type the current screen contents over **USB HID (keyboard)**, one line at a time, into a focused text field on your computer or phone. You can still enter your PIN, browse credentials and type passwords — blind.
The host types into whatever text field is focused. Open a notes app / empty text box **before** activating, so the output goes somewhere harmless.
***
## Activate it
Plug the device in and reach the PIN prompt (the normal boot screen).
Press and **hold the Center button for \~10 s**. A border fills around the screen over the full 10 s; when it completes, the mode toggles. Release before 10 s and nothing happens.
The device starts typing the current state into your focused text field, e.g. `ZK reader ON` then the PIN line.
Hold Center for 10 s again to turn it off.
To make it **permanent** (useful when the screen is genuinely dead), enable it in **Settings → Reader: On/Off**. That stores the choice in EEPROM and the device boots straight into screen-reader mode. The 10 s gesture only toggles it for the current session.
***
## What it types
The device keeps **one line** on screen, erasing and rewriting it as you navigate:
| Screen | Types |
| ------------------------------ | ------------------------------------------------------------------------------------------------------ |
| PIN entry | `PIN 125 >7` — the digits entered so far, then the currently selected digit (`>OK` = the confirm tick) |
| Credential (site) | `3: google.com` |
| Credential (user / pass / 2FA) | `3: user`, `3: password`, `3: 2FA` |
| Menu | `Menu: ` |
| Confirm page | ` Hold=OK Left=No` |
Entering the PIN blind: use **Up/Down** to change the selected digit (watch `>n` update), **Right** to add it, **Left** to delete, and select the tick (`>OK`) then **Right/Center** to unlock — exactly as on screen, but reading the typed line instead of the OLED.
When you press **Center** on a credential, the announcement line is erased and the **real value** (user / password) is typed — so with a dead screen you can still read a password by pressing Center in a text box.
***
## Notes & safety
This types your screen contents — **including PIN digits and, on Center, passwords** — into the focused field. Use it for recovery, into a private text field you control, and clear that field afterwards.
* It works at the PIN screen even before unlocking, so a broken-screen device can still be unlocked.
* The Bitcoin **seed** is never typed by this mode — it is screen-only by design.
* Typing is deliberately paced, so each line takes a moment to appear.
Exactly what it emits over USB — and what it never does.
Where the Reader setting lives.
Other recovery options.
# Initial Tutorial
Source: https://docs.zerokeyusb.com/getting-started/setup-wizard
Screen-by-screen walkthrough of the wizard that appears the first time you plug your ZeroKeyUSB in. Orientation, keyboard layout and master PIN.
The first time you plug your ZeroKeyUSB into a USB-C port, a 9-page setup wizard starts automatically (in Spanish by default — the localized strings appear below). This guide walks you from the welcome screen to a device ready to store credentials.
***
## Step 1 — Welcome splash
When you plug the cable in, the screen lights up showing the logo for a couple of seconds. This confirms the device booted correctly and passed its firmware security check.
*Wait about 2 seconds. The wizard appears automatically — you don't have to press anything.*
***
## Step 2 — Page 1: Welcome
First page of the wizard. The inverted top bar shows the title and a page indicator `<1/9>`. The body briefly describes what the device is and what to do.
*Press **Right** to advance. If the body has more text than fits on screen, use **Up/Down** to scroll before continuing.*
***
## Step 3 — Page 2: Navigation
Explains what each of the 5 pads does. This is the only page that describes the controls explicitly.
*Read the page and press **Right** to move to the next one.*
***
## Step 4 — Page 3: Rotate screen
If the golden dots end up on your left instead of on the right, flip the device physically — or rotate the screen in software with **Center**. The orientation is saved on the device and persists across power cycles.
*Press **Center** to toggle the orientation (you will see `NORMAL ↔ ROTADA` on screen). When the controls feel right, press **Right** to continue.*
***
## Step 5 — Page 4: Keyboard layout
ZeroKeyUSB emulates a USB keyboard. So that passwords with special characters (`@`, `!`, `#`, etc.) type correctly on your computer, the device needs to know **your** keyboard layout.
*Press **Center** to cycle through EN-US, ES-ES, FR-FR, DE-DE, IT-IT, PT-PT, DA-DK, SV-SE, HU-HU. When yours appears, press **Right**.*
You can change this later from **Menu → Settings → Keyboard**.
***
## Step 6 — Page 5: Master PIN (introduction)
This page explains the PIN rules before asking for one. Pick between 4 and 16 digits (0–9). It is the only thing standing between an attacker and your credentials.
*Press **Center** to move on to the PIN entry screen.*
**There is no PIN recovery.** If you forget it, the only option is a factory reset that wipes **all** your credentials. Memorize the PIN — or write it down somewhere physically safe.
***
## Step 7 — Enter the PIN
The numpad screen. Each digit is entered with **Up/Down** (to choose 0–9) and **Right** (to confirm and advance to the next digit). The small arrows on the sides of the active digit remind you of the available moves.
*For each digit of your PIN: **Up/Down** to the correct number, then **Right** to confirm it. When you finish the last digit, the cursor jumps to the tick (✓) symbol. Press **Center** on the tick to save the PIN.*
To erase the last digit if you make a mistake, press **Left**.
***
## Step 8 — Page 6: PIN saved
After entering the PIN, the wizard shows it back to you **once** so you can memorize it. This is your last chance to see it in cleartext.
*Write the PIN down or memorize it. When you have it, press **Right**.*
***
## Step 9 — Page 7: Unlocking
Explains that from now on, every time you plug the USB in, you will have to enter the PIN. And that repeated failures trigger an exponential wait.
*Read the page and press **Right** to continue.*
**Exponential backoff:** first failure = 5 s wait, second = 10 s, third = 20 s… up to \~43 min max. The counter resets when you enter the correct PIN. Nothing is wiped automatically.
***
## Step 10 — Page 8: Accounts
A preview of the main screen — how credentials are navigated once you are unlocked.
*Press **Right**.*
***
## Step 11 — Page 9: All set
Last page. Reminds you that you can reach the main menu by pressing **Left** on the first credential (or **Right** on the last, going through "Add New" first).
*Press **Center** to exit the wizard and reach the PIN screen. That's it — the device is set up.*
***
## Step 12 — Ready to use
After pressing Center, the device takes you to the PIN numpad to enter the PIN you just created (the **same** one you will see every time you plug the device in from now on). When you enter the PIN correctly, you will reach the main screen — but since there are no credentials yet, the **Add New** screen appears directly.
*Press **Center** to create your first credential — or follow the dedicated guide at [Create your first credential](/getting-started/first-credential).*
***
## Next steps
Save your first password step by step with the on-device editor.
Learn the Tools, Settings, Danger and Info options.
# 2FA Codes (TOTP)
Source: https://docs.zerokeyusb.com/getting-started/totp-codes
How to enter date/time the first time, view the 6-digit code with countdown and have it typed automatically to your computer.
ZeroKeyUSB can generate **TOTP codes (RFC 6238)** fully offline — the same ones you'd see in Google Authenticator, without a phone. This guide covers the usage flow. To add a TOTP secret to a credential, see [Importing credentials](/getting-started/importing-credentials).
**Before you start:** the credential must have a TOTP secret loaded (in the `2FA` field). If you see `2FA --` on screen, it doesn't have one. Load the secret from the web manager or over USB-CDC first.
***
## Step 1 — Navigate to the 2FA field
From the credential's main screen, press **Down** until you reach the 2FA field. The icon switches from padlock (Pass) to key (2FA). If the credential has a secret loaded, you'll see `2FA (OK)`. Otherwise, `2FA --`.
*With the 2FA field selected (inverted key icon), press **Center** (short) to start calculating the code.*
***
## Step 2 — Enter the date (first time per session)
The ATECC608A has no RTC. It needs date and time **just once** per session (between power cycles). The device asks for the date first in `DD/MM/YY` format.
| Button | Action |
| -------------- | ------------------------------------------------- |
| **Up/Down** | Increase/decrease the current digit (0–9) |
| **Left/Right** | Move the cursor between digits (skipping the `/`) |
| **Center** | Confirm the date and move to the time |
*Set today's date and press **Center**.*
***
## Step 3 — Enter the time
Same flow, now in `HH:MM` (24h local time) format.
*Same buttons as the date. When done, press **Center** to compute the code.*
The time can be your exact local time — the device does not apply a timezone. If the service expects UTC and you live elsewhere, adjust manually.
***
## Step 4 — View the TOTP code
After entering date and time, the ATECC608A computes HMAC-SHA1 of secret + epoch/30 and shows the resulting 6 digits. The bottom bar empties as the 30-second window approaches its end.
*With your cursor on the 2FA input field of your app, short-press **Center**. The device types the 6 code digits to the host as if from a normal keyboard.*
If the countdown reaches 0 before you confirm, the code regenerates automatically — you don't have to re-enter the time.
***
## Step 5 — Subsequent codes in the same session
While the device stays plugged in, you won't have to re-enter date and time. The next time you enter a 2FA field (in any credential), you jump straight to step 4 with a code computed using the current time.
*Short-press **Center** to type the code to the host, or **Left** to return to the credential's main view without typing anything.*
***
## Quick reference
| Screen | Useful buttons |
| ---------------- | ----------------------------------------------------------------- |
| `2FA (OK)` field | **Center** → enter TOTP flow |
| `2FA --` field | (no secret loaded, nothing to do) |
| Enter date | **Up/Down**: digit · **Left/Right**: cursor · **Center**: confirm |
| Enter time | Same as date |
| View code | **Center**: type to host · **Left**: exit |
***
## Next steps
How to load the Base32 secret from your service into a credential.
If you don't have credentials with TOTP yet, start by creating one.
# ATECC608A Secure Element
Source: https://docs.zerokeyusb.com/hardware/atecc608a
Role, slot configuration, commands, and security properties of the ATECC608A in ZeroKeyUSB.
The **Microchip ATECC608A** (SKU: MAHDA-T) is the hardware secure element at the heart of ZeroKeyUSB's security architecture. It provides the entropy, identity, rate-limiting, **and the AES cipher itself** — every credential block is encrypted and decrypted inside this chip.
***
## Why a secure element?
The SAMD21 MCU alone cannot provide:
* **True random numbers** — MCUs generate pseudo-random numbers from software seeds; quality is hard to verify.
* **Tamper-resistant monotonic counters** — software counters can be reset by erasing EEPROM or reflashing firmware.
* **Device-unique identity** — a chip serial baked into the die at manufacture provides an unforgeable hardware salt.
* **A key store that resists I²C inspection** — once the data zone is locked with `IsSecret=1`, the AES master key cannot be read back, even by code running on the MCU.
The ATECC608A fills all four roles. AES on this chip used to be considered unreachable on `MAHDA-T` parts; the current firmware enables it during a one-shot first-boot provisioning routine.
***
## Hardware connection
| Signal | SAMD21 pin | ATECC608A pin |
| ------ | ---------- | ------------- |
| SDA | PA08 | SDA |
| SCL | PA09 | SCL |
| GND | GND | GND |
| VCC | 3.3 V | VCC |
I²C address: **`0x60`**\
Bus speed: **100 kHz** (set at startup and matched by the bootloader)
***
## SKU note — MAHDA-T
The `MAHDA-T` variant ships with:
* Hardware AES command **disabled at the factory** (`AES_Enable` byte 13 has bit 0 cleared). Bits 6 and 7 of the same byte are factory-set; any write that tries to clear them is rejected by the chip with `SS=0x03` (parse error).
* Several other bytes in the first 16 of the Config Zone are factory-locked (SN, RevNum). A 4-byte write that straddles those bytes is rejected wholesale.
* Standard TRNG, Counter, CheckMac, and ReadSerial commands enabled.
* Factory-default slot configuration: every slot is unlocked, readable and writable until provisioning locks the zones.
The firmware works around these quirks during the first-boot provisioning:
1. Read the affected blocks into RAM.
2. OR-mask only the bits that need to flip (set `AES_Enable` bit 0, set `SlotConfig[8].IsSecret`, set `SlotConfig[8].WriteConfig=Never`, set `KeyConfig[8].KeyType=AES`).
3. Write the whole 32-byte block back so the chip ignores the read-only bytes inside it.
4. Re-read and verify each modification took effect before locking.
Once provisioning completes successfully, the chip runs AES blocks for the rest of the device's life.
***
## Slot map
Established by the device itself at first boot and locked permanently:
| Slot | Size used | Purpose | SlotConfig / KeyConfig |
| ----- | ------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| **8** | 16 B (first AES sub-key) | AES-128 master key — generated by the chip's TRNG, never leaves the chip | `IsSecret=1`, `WriteConfig=Never`, `KeyType=6 (AES)`. The chip refuses to return the slot data via `Read`. |
| **9** | 32 B | PIN key: `SHA-256(pinArray[16] ∥ chip_serial[9])` | `IsSecret=0`, `WriteConfig=Always`. The app rewrites the slot when the user changes their PIN. Readable over I²C. |
Counter0 exists on the chip but the firmware does **not** use it for PIN rate-limiting (see the note at the end of this page); brute-force throttling is done with a persistent EEPROM backoff instead.
> **Slot 9 security note:** Because `MAHDA-T` rejects clear writes to IsSecret slots 0–7, the PIN key is stored in slot 9 which keeps `IsSecret=0`. This means the 32-byte PIN hash is **readable** over I²C by anyone with physical access. An adversary could read the hash and attempt offline SHA-256(PIN∥serial) dictionary attacks. Short PINs (\< 6 digits) are particularly vulnerable to this approach.
> **Slot 8 trade-off:** Setting `WriteConfig=Never` means the AES key cannot be regenerated after the data zone is locked. If the chip fails, every credential encrypted under that key is unrecoverable. The trade is `IsSecret=1` (key cannot be read out over I²C). Export a USB-CDC backup before relying on the device for anything important.
***
## Commands used
### `RANDOM` (opcode `0x1B`)
* Mode `0x00`: refreshes the internal DRBG seed from hardware entropy before generating 32 random bytes.
* Used to generate the AES master key (16 B) inside the chip and the IV (16 B) at provisioning. The key never leaves the chip — the firmware never sees its bytes; only the IV is copied to EEPROM.
### `AES` (opcode `0x51`)
* Mode `0x00`: encrypt one 16-byte block using the key in slot 8.
* Mode `0x01`: decrypt one 16-byte block using the key in slot 8.
* Param2 = `0x0008` (slot 8). The chip looks up `KeyConfig[8].KeyType`, confirms it is `6` (AES), uses the slot's 16-byte sub-key, and runs a hardware AES round on the input.
* Called once per 16-byte block by `cbcEncrypt32` / `cbcDecrypt32` in `zerokey-security.cpp`. CBC chaining is layered around these calls on the MCU.
### `LOCK` (opcode `0x17`)
* Mode `0x80`: lock the Config zone (CRC check skipped).
* Mode `0x81`: lock the Data + OTP zone.
* Used during provisioning. Once executed the chosen zone cannot be modified ever again.
### `WRITE` (opcode `0x12`)
* 32-byte clear write to Config zone (`p1=0x80`) — used by `provisionAesAndLock()` to set `AES_Enable`, `SlotConfig[8]`, and `KeyConfig[8]`.
* 32-byte clear write to Data zone (`p1=0x82`) — used to populate slot 8 with the freshly-generated AES key and to (re)write the PIN HMAC in slot 9.
* Each write is followed by a read-back verify so the firmware bails out without locking if the chip silently rejected the change.
### `INFO` (opcode `0x30`)
* Mode `0x00`: returns 4-byte revision word.
* Used as a liveness check (`ping()`) to detect an unprovisioned or missing chip at boot.
### `READ` (opcode `0x02`)
* 32-byte block read from Config Zone.
* Used by `readSerial()` to extract the 9-byte chip serial (bytes 0–3 and 8–12 of Config Zone block 0).
* Used by `readConfigBlock()` (provisioning only) and `readLockStatus()`.
### `COUNTER` (opcode `0x24`)
* Mode `0x00` (read): returns current Counter0 value.
* Mode `0x01` (increment): atomically increments Counter0 and returns the new value.
* Hardware monotonic — no software path can decrement it. **Not used by the current PIN verify path** (the removed Counter0 lockout used it); available for future firmware.
### `CHECKMAC` (opcode `0x28`) — defined but not used in primary verify path
* Computes `SHA-256(slot_key ∥ ClientChallenge ∥ OtherData)` inside the chip and compares it to a host-computed response.
* Implemented in `checkMacAgainstPin()` for future use. Current `verifySignature()` uses a direct EEPROM hash comparison instead.
***
## Wake / sleep protocol
The ATECC608A uses a non-standard I²C wake sequence:
1. Drive SDA low for > 60 µs. Achieved by addressing `0x00` at 100 kHz (ignored NACK expected).
2. Wait ≥ 1.5 ms (t\_WHI).
3. Read 4-byte wake response: expect `[0x04, 0x11, CRC_lo, CRC_hi]`.
4. Validate CRC-16 (poly `0x8005`, init `0x0000`, no reflection) over first 2 bytes.
All commands follow the pattern: `wake()` → `execute()` → `sleep()`. The chip returns to low-power sleep after every operation.
***
## PIN key derivation
```
derivePinKey(pin_bytes[16], out[32]):
serial[9] = ATECC608A.readSerial()
buf[25] = pin_bytes[16] || serial[9]
out[32] = SHA-256(buf)
```
* `pin_bytes` are the raw digit values from `pinArray[16]` (each byte = 0–9 from touch input).
* `serial` is the 9-byte device-unique identifier (irreversible, factory-programmed).
* The same formula is used by the **provisioning kit** when writing slot 9, ensuring the app and chip agree.
Because `serial` is unique per chip, the same PIN on two different ZeroKeyUSB devices produces completely different 32-byte keys.
***
## PIN rate-limiting (no Counter0 lockout)
An earlier design used Counter0 as a destructive hard limit: a threshold of `cur_counter + 50` in EEPROM `0x0020`, and a wipe (`eraseAll()`) once the counter crossed it after 50 wrong PINs. **That mechanism was removed.** `verifySignature()` no longer increments Counter0 or reads any threshold, and wrong PINs never wipe the vault.
The live defence is a **persistent exponential backoff**: a failed-attempt counter in EEPROM `0x0002` grows the delay up to ≈ 43 minutes, and `waitFromEeprom()` is called on every boot before the PIN screen accepts input, so the penalty cannot be skipped by power-cycling. `eraseAll()` still exists but runs only on a user-initiated factory reset. See [PIN Verification](/firmware/security/pin-verification) for the full flow.
The `COUNTER` command and Counter0 remain available on the chip (documented above) and could be re-enabled by future firmware, but are not part of the current verify path.
***
## CRC protocol
All ATECC608A command packets use a custom CRC-16:
* Polynomial: `0x8005`
* Initial value: `0x0000`
* No input/output reflection
* No final XOR
The CRC covers the packet bytes from `count` through the last data byte, excluding the CRC bytes themselves. Response CRC covers from byte 0 (`count`) through the last data byte.
***
## Lock status
`getLockStatus()` reads Config Zone block 2 (bytes 64–95):
* Byte 86 (`LockValue`): `0x55` = Data+OTP zone unlocked; any other value = locked.
* Byte 87 (`LockConfig`): `0x55` = Config zone unlocked; any other value = locked.
A fully provisioned device has both zones locked. The provisioning routine locks the Config zone first (after writing `AES_Enable`, `SlotConfig[8]`, `KeyConfig[8]`), then writes the random AES key to slot 8, and finally locks the Data zone.
If the firmware boots a chip with both zones locked but `KeyConfig[8].KeyType ≠ 6`, it stops with `CHIP BRICKED KT=` on the OLED rather than letting later AES calls fail with opaque status errors. That state means an earlier firmware version locked the chip with an invalid AES key configuration; the chip is physically unrecoverable.
# Display: SSD1306 OLED
Source: https://docs.zerokeyusb.com/hardware/display-ssd1306
128×32 monochrome display wiring, power budget, and firmware usage.
ZeroKeyUSB uses a **0.91" SSD1306-based OLED module** to present menus, credentials, and status icons. The display is bright, low-power, and readable from multiple angles — ideal for a quick glance during logins.
***
## Electrical characteristics
| Parameter | Value |
| --------------- | --------------------------- |
| Resolution | 128 × 32 pixels |
| Interface | I²C (address `0x3C`) |
| Supply voltage | 3.3 V |
| Typical current | 10–12 mA at full brightness |
| Controller | Solomon Systech SSD1306 |
The module connects directly to the SAMD21’s SERCOM3 I²C bus, shared with the external EEPROM. Pull-up resistors (4.7 kΩ) are located on the PCB, so the breakout resistors should be disabled when assembling.
***
## Pin assignments
| OLED pin | Signal | Notes |
| -------- | -------- | ---------------------------------------------- |
| VCC | 3V3 | Powered from the MCU regulator |
| GND | GND | Common ground |
| SCL | PA23 | Shared I²C clock |
| SDA | PA22 | Shared I²C data |
| RES | PA14 | Controlled by firmware during init |
| DC | Tied low | Command/data handled automatically in I²C mode |
| CS | Tied low | Not used in I²C mode |
The firmware toggles the **RES** line during startup to ensure a clean boot sequence even if power is unstable.
***
## Frame buffer strategy
* The SSD1306 expects data in **pages of 8 vertical pixels**.
* Firmware maintains a 512-byte buffer in SRAM (`128 × 32 / 8`).
* Updates use **partial writes** to minimize I²C traffic when only a few characters change.
* A simple double-buffer diff tracks dirty regions so the screen refresh stays under 5 ms.
Animations such as smooth scrolling for long passwords rely on timer interrupts that shift the buffer between refreshes.
***
## Brightness control
* Default contrast value: `0x7F` (50%).
* Menu option allows dimming down to `0x20` for dark environments.
* After 60 seconds of inactivity the firmware sends `DISPLAY OFF` while keeping data in RAM.
* Any touch input or USB activity turns the screen back on instantly.
This approach balances legibility and OLED lifespan.
***
## Troubleshooting
| Symptom | Possible cause | Fix |
| ------------------------------ | -------------------------------- | ---------------------------------------------------- |
| No image, backlight off | RES pin held low | Check solder joint or ensure the boot logo finished. |
| Display flashes or shows noise | I²C conflict with EEPROM | Inspect pull-up resistors and cable length. |
| Ghosting / burn-in | Static content at max brightness | Lower contrast or enable auto-dim in settings. |
If the OLED ever needs replacement, any SSD1306 I²C module with the same pin order can be swapped in without firmware changes.
# EEPROM Memory Map
Source: https://docs.zerokeyusb.com/hardware/eeprom
How ZeroKeyUSB stores encrypted credentials and why its memory architecture is designed for maximum safety.
## Secure memory, not just storage
ZeroKeyUSB uses an industrial-grade **ST M24C64-WMN6TP EEPROM**, a 64-kilobit non-volatile memory chip (8 KB total).\
It was selected not for capacity, but for **reliability and long-term data integrity** — critical for a device expected to safeguard your credentials for years.
All information inside this chip is **encrypted by the MCU before being written**.\
Even if the memory were physically removed, it would reveal only **ciphertext blocks** — never readable data.
***
## Key characteristics
| Specification | Description |
| --------------------- | ------------------------ |
| **Chip model** | ST M24C64-WMN6TP |
| **Capacity** | 64 Kbit (8 192 bytes) |
| **Interface** | I²C, 2-byte addressing |
| **Endurance** | > 1 000 000 write cycles |
| **Data retention** | > 40 years |
| **Operating voltage** | 1.8 V – 5.5 V |
| **Page size** | 32 bytes |
All inter-chip communication uses I²C for memory access and USB HID for host interaction.\
The I²C bus itself is not encrypted — instead, **data is encrypted in firmware before transmission**, ensuring confidentiality even if the bus were intercepted.
***
## Internal structure overview
ZeroKeyUSB’s EEPROM is divided into isolated regions.\
Each serves a dedicated security function and is accessed exclusively through firmware routines.
| Address range | Size | Purpose |
| --------------- | ------- | ------------------------------------------------------------ |
| `0x0000–0x0001` | 2 B | Configuration flags / setup marker |
| `0x0002` | 1 B | **Failed-attempts counter** (persistent across power cycles) |
| `0x0005–0x000C` | 8 B | PIN verification signature |
| `0x0010–0x001F` | 16 B | AES Initialization Vector (IV) |
| `0x0020–0x03DF` | ≈ 960 B | System & TOTP metadata (including 2 bytes per slot status) |
| `0x03E0–0x03EF` | 8 B | Last TOTP epoch (Unix time, 64-bit) |
| `0x0400–0x1FFF` | ≈ 7 KB | Encrypted credential storage (user data) |
Each credential occupies **three 32-byte encrypted pages** (96 B total):
1. Site / service name
2. Username or email
3. Password
An optional fourth page is used for the **TOTP secret** when 2FA is enabled.
***
## Data segmentation
Storing each field in a separate encrypted page offers key advantages:
* 🔐 **Independent encryption:** Every field (site, user, password, TOTP) is encrypted separately.
* 🧩 **No pattern correlation:** Even identical credentials produce different ciphertext.
* 💥 **Corruption isolation:** If a page fails, others remain intact.
* ⚡ **Efficient writes:** Editing one field only rewrites that page, prolonging EEPROM life.
***
## Security metadata
### 🔑 Initialization Vector (IV)
A unique 16-byte value generated from analog noise on a floating pin during first startup.\
It ensures that even identical data encrypted twice produces different ciphertext.
### 🧩 PIN signature block
An 8-byte cryptographic fingerprint stored at address `0x0005`.\
It lets ZeroKeyUSB verify the correct Master PIN without storing the PIN itself.
### 🕒 Failed-attempts counter
Stored at `0x0002`, this byte tracks consecutive failed PIN entries.\
If a user enters an incorrect PIN multiple times, the firmware applies exponential delays before the next attempt.\
Because the counter is stored in EEPROM, lockout timers persist even after power cycling or unplugging the device.
### ⏱️ Last TOTP epoch
A 64-bit Unix timestamp representing the last synchronized time.\
It allows offline TOTP generation without re-syncing on every use.
***
## Credential layout example
| Page | Content | Encrypted? | Size |
| ------------------ | ---------------------- | ---------- | ------------ |
| 0 | Site / domain | ✅ | 32 B |
| 1 | Username | ✅ | 32 B |
| 2 | Password | ✅ | 32 B |
| 3 | TOTP secret (optional) | ✅ | 32 B |
| — | — | — | — |
| **Total per slot** | — | — | **96–128 B** |
Up to **64 credentials** fit securely within the 8 KB memory, depending on TOTP usage.
***
## Data integrity and error handling
Every EEPROM write is acknowledged at the I²C level to confirm success.\
If a write fails or times out, the firmware retries automatically.\
Persistent errors trigger an on-screen message (`EEPROM Error`) and abort the operation safely.
ZeroKeyUSB never stores plaintext or partial records — credentials are either **fully encrypted** or **not written at all**.
***
## Why it matters
Typical password managers depend on OS storage and software encryption.\
ZeroKeyUSB keeps everything in hardware, with:
* A dedicated EEPROM rated for **40 + years of retention**.
* Encryption and IV generation handled by the **SAMD21 microcontroller**.
* No wireless interfaces and no Internet connectivity to exploit.
Even with physical access to the memory chip, the contents cannot be decrypted without the correct PIN-derived key and IV.
***
Transparency builds trust: the memory map is public so that anyone can verify firmware behavior, yet all regions remain encrypted and locked during normal operation.
# MCU: Microchip SAMD21
Source: https://docs.zerokeyusb.com/hardware/mcu-samd21
Microcontroller responsibilities, clock setup, and peripheral usage inside ZeroKeyUSB.
The **Microchip ATSAMD21G18** is the core of ZeroKeyUSB. It combines a 32-bit ARM Cortex-M0+ CPU, built-in USB controller, and enough peripherals to coordinate the display, touch inputs, and external EEPROM.
***
## Key specifications
| Feature | Value |
| ------- | ---------------------------------------------------- |
| CPU | ARM Cortex-M0+ @ 48 MHz |
| Flash | 256 KB (firmware occupies \~60 KB) |
| SRAM | 32 KB |
| USB | Full-speed device with HID + CDC composite support |
| GPIO | 38 general-purpose pins |
| Timers | 9 (TC/TCC) used for PWM, debouncing, and TOTP timing |
| ADC | 12-bit, used for IV entropy sampling |
The firmware runs from internal flash and executes entirely from zero-wait-state memory, keeping latency low even while updating the OLED display.
***
## Clock configuration
1. Internal **8 MHz oscillator** feeds the Digital Frequency Locked Loop (DFLL).
2. DFLL multiplies to **48 MHz** for the CPU and synchronous peripherals.
3. The **generic clock controller** divides the 48 MHz clock for:
* 1 MHz I²C (SERCOM3) used by EEPROM and OLED
* 2 MHz SERCOM1 for the touch controller SPI
* 32 kHz reference for millisecond timing (via `SysTick`)
This configuration balances performance with low noise for the touch sensor.
***
## Peripheral mapping
| Peripheral | SERCOM | Function |
| ---------- | ------ | ------------------------------------------ |
| SERCOM0 | USART | CDC serial channel (TX/RX on USB pads) |
| SERCOM1 | SPI | Touch controller (TS06) |
| SERCOM2 | I²C | Reserved/debug headers |
| SERCOM3 | I²C | EEPROM (M24C64-W) + OLED display (SSD1306) |
| SERCOM4 | Unused | Available for future expansions |
| SERCOM5 | USB | Native USB full-speed interface |
The `PORT` multiplexer assigns each SERCOM to specific pins; see the KiCad design for exact pad numbers.
***
## Memory layout
* **Bootloader (8 KB)** – UF2-compatible loader for factory flashing and community updates.
* **Application (240 KB max)** – ZeroKeyUSB firmware; currently uses \<30% of available flash.
* **EEPROM emulation** is unused; all persistent data lives in the external M24C64-W.
* **SRAM buffers**:
* 512 bytes for OLED frame buffer
* 128 bytes for USB HID reports
* 96 bytes scratch space for AES blocks
The linker script reserves stack space for nested menu rendering and cryptographic routines.
***
## Power and sleep
* The MCU runs in **active mode** while connected; consumption stays below 25 mA for the whole board.
* After 60 seconds of inactivity the firmware dims the OLED and places the CPU into **Standby** while keeping USB active.
* Touch or USB activity wakes the chip in under 3 ms.
This behavior ensures responsive interaction without exceeding USB current limits.
***
## Firmware responsibilities
* Authenticating the Master PIN using AES routines.
* Orchestrating the menu, display, and touch interactions.
* Managing EEPROM read/write operations with wear-level tracking.
* Generating USB HID reports and processing CDC commands.
* Calculating TOTP codes using integer arithmetic (no floating point required).
The SAMD21 provides enough headroom to add features such as multiple keyboard layouts or additional security checks without hardware changes.
# Hardware Overview
Source: https://docs.zerokeyusb.com/hardware/overview
Inside ZeroKeyUSB — industrial-grade components designed for offline security and long-term reliability.
## Built for trust
ZeroKeyUSB is a self-contained, hardware-based password manager.\
Engineered with a single goal: **protect your credentials without ever connecting to the Internet**.
Each unit is assembled, tested, and encapsulated in **industrial-grade epoxy resin** to prevent external tampering — making it waterproof, dust-proof, and maintenance-free.
***
## System architecture
```mermaid theme={null}
graph TB
USBC["USB-C Connector Power + Data"]
subgraph PCB["ZeroKeyUSB PCB"]
MCU["SAMD21E18A ARM Cortex-M0+ 48 MHz / 256 KB Flash"]
ATECC["ATECC608A MAHDA-T Secure Element"]
EEPROM["M24C64-WMN6TP 64 Kbit EEPROM"]
OLED["SSD1306 128×32 OLED"]
TS06["TS06 6-ch Touch IC"]
PADS["5 Gold Touch Pads"]
end
USBC -->|"USB FS"| MCU
MCU -->|"I²C 0x60"| ATECC
MCU -->|"I²C 0x50"| EEPROM
MCU -->|"I²C 0x3C"| OLED
MCU -->|"I²C 0x69"| TS06
TS06 --- PADS
style MCU fill:#dbeafe,stroke:#2563eb,color:#000
style ATECC fill:#fef3c7,stroke:#d97706,color:#000
style EEPROM fill:#dcfce7,stroke:#16a34a,color:#000
style OLED fill:#e0e7ff,stroke:#4f46e5,color:#000
style TS06 fill:#fce7f3,stroke:#db2777,color:#000
```
***
## Component list
| Component | Model | I²C Addr | Purpose |
| -------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| **MCU** | Microchip SAMD21E18A | — | ARM Cortex-M0+ @ 48 MHz. Runs firmware, AES-128 CBC encryption, USB HID keyboard + CDC serial. |
| **Secure Element** | Microchip ATECC608A (MAHDA-T) | `0x60` | Hardware TRNG for key/IV generation, hardware AES-128 engine, 9-byte chip serial as PIN salt. |
| **EEPROM** | ST M24C64-WMN6TP | `0x50` | 64 Kbit (8 KB) non-volatile storage. Holds encrypted credentials, AES key, IV, PIN hash, TOTP metadata. >1M write cycles. |
| **Display** | SSD1306 OLED | `0x3C` | 128×32 pixel monochrome white OLED. Shows credentials, menus, PIN entry, TOTP codes, progress bars. |
| **Touch Controller** | TS06 | `0x69` | 6-channel capacitive touch IC (5 channels used). Gold-plated PCB pads for Up/Down/Left/Right/Center. |
| **USB** | USB-C connector | — | USB Full-Speed. Powers the device (\~20 mA) and provides HID keyboard + CDC serial interfaces. |
| **Write Protect** | GPIO PA01 | — | EEPROM write-protect pin. Can be driven high to hardware-lock EEPROM writes. |
***
## Why these components
### 🧠 SAMD21E18A microcontroller
The ARM Cortex-M0+ processor balances performance, size, and power efficiency:
* **256 KB Flash** — room for firmware, fonts, 9 keyboard layouts, and PROGMEM icon bitmaps.
* **32 KB SRAM** — enough for display buffer, credential cache, and TOTP workspace without dynamic allocation.
* **Native USB** — hardware USB Full-Speed peripheral eliminates the need for external USB bridge chips.
* **Hardware DSU** — Data Scrambling Unit provides hardware CRC32 for fast boot-time firmware integrity checks.
* **BOOTPROT fuse** — `BOOTPROT=7` locks the first 16 KB of Flash, preventing application code from modifying the bootloader.
### 🔐 ATECC608A secure element
The ATECC608A provides four capabilities that software alone cannot guarantee:
| Capability | Why it matters |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Hardware TRNG** | Generates the AES master key (16 B, inside the chip) and IV (16 B) with true hardware entropy — not pseudo-random. |
| **AES-128 engine** | Every credential block is encrypted and decrypted by the chip's hardware AES. The key lives in slot 8 with `IsSecret=1` and never crosses the I²C bus. |
| **Locked config & data zones** | At first boot the chip permanently locks both zones (irreversible), sealing the AES key in slot 8 and the AES-enable configuration so they can never be altered or read out. (PIN brute-force throttling is handled separately by a persistent EEPROM backoff, not by the chip.) |
| **Chip serial (9 B)** | Factory-programmed unique identifier used as salt in PIN hashing: `SHA-256(PIN ∥ serial)`. Same PIN on a different device produces a completely different hash. |
> The MAHDA-T SKU ships with the hardware AES command disabled. The first-boot provisioning routine enables it, configures slot 8 as an AES key holder, generates the key with the on-chip TRNG, and irreversibly locks both Config and Data zones.
### 💾 M24C64-WMN6TP EEPROM
* **8 KB** of non-volatile storage organized in 32-byte pages.
* **>1 million write cycles per page** — decades of normal use.
* All credential data is **AES-128 CBC encrypted before writing** — the I²C bus only sees ciphertext.
* Page boundary awareness: the firmware splits writes that cross 32-byte page boundaries to avoid the M24C64's address wrap-around behavior.
### 🖐️ TS06 touch controller
* **Sealed, six-channel capacitive touch IC** (five channels actively used).
* Internal baseline calibration — no analog tuning required.
* Minimum sensitivity (`0x3F`) set at boot to prevent false triggers through the epoxy encapsulation.
* 80 ms debounce, 800 ms long-press threshold, 150 ms channel lockout — all handled in firmware.
### 💡 SSD1306 OLED
* **128×32 pixels**, white-on-black, high contrast.
* Driven via I²C at address `0x3C`.
* Full-frame refresh (\~512 bytes per frame) through `Adafruit_SSD1306` library.
* Excellent visibility in both daylight and darkness.
* Protected behind the sealed epoxy window.
### ⚡ USB-C connection
* Draws approximately **20 mA** — similar to a wireless mouse.
* **No battery** — fully powered from the host USB port.
* **No wireless** — no Wi-Fi, Bluetooth, or NFC hardware exists on the PCB.
* Works with Windows, macOS, Linux, Android, and iPadOS.
***
## I²C bus
All peripherals share a single I²C bus at **100 kHz**:
| Device | Address | Role |
| ------------- | ------- | ------------------ |
| SSD1306 OLED | `0x3C` | Display |
| M24C64 EEPROM | `0x50` | Credential storage |
| ATECC608A | `0x60` | Secure element |
| TS06 | `0x69` | Touch controller |
SDA and SCL are on **PA08** and **PA09** respectively. External pull-up resistors are present on the PCB.
***
## Physical design
* **Encapsulated in epoxy resin** — prevents corrosion, dust, moisture ingress, and physical tampering.
* **No wireless interfaces** — eliminates remote attack surfaces entirely.
* **No external screws or seams** — the device cannot be non-destructively opened.
* **Gold-plated touch pads** — durable, corrosion-resistant, and visible through the resin.
***
## Transparency, not exposure
ZeroKeyUSB is **fully open source**. The firmware and hardware schematics are publicly available on\
[GitHub → Depbit-lab/zerokeyusb](https://github.com/Depbit-lab/zerokeyusb).\
Anyone can verify exactly what code runs on their device.
Firmware updates require **physical access** — either via SWD pogo pins or the USB bootloader with signed firmware. There is no remote update mechanism.
ZeroKeyUSB is a sealed product — opening or reprogramming the device voids the warranty and destroys the epoxy encapsulation.
# Touch Sensor
Source: https://docs.zerokeyusb.com/hardware/touch-sensor
The five golden points that replace buttons — a durable, intuitive interface for seamless control.
## Designed for durability
ZeroKeyUSB has **no moving parts**.\
Instead of fragile buttons or switches, it uses a **six-channel capacitive touch controller (TS06)** that detects precise finger contact through the device’s resin surface.
The result: a **sealed, wear-proof interface** that remains perfectly responsive even after years of daily use.
***
## How it works
The **TS06 touch controller** continuously monitors small electrical changes on five golden contact points located on the front surface of the device.\
When your finger approaches, the chip detects a shift in capacitance — instantly identifying which area has been touched.
This detection happens **thousands of times per second**, allowing smooth navigation without delay or misfires.
***
## Touch layout
The five golden points are arranged ergonomically in a cross pattern:
| Direction | Function |
| -------------- | -------------------------------- |
| **Up (↑)** | Scroll or change character |
| **Down (↓)** | Reverse scroll or decrease value |
| **Left (←)** | Go back / Previous screen |
| **Right (→)** | Continue / Confirm / Next |
| **Center (•)** | Select or execute action |
The sixth hidden channel is used internally to stabilize readings and filter out environmental noise.
***
## Short vs. long press
Each touch can be interpreted as a **short tap** or a **long press**, depending on duration:
| Press type | Hold time | Typical use |
| --------------- | --------- | -------------------------------------------------------------------------------------- |
| **Short press** | \< 800 ms | Move, select, confirm |
| **Long press** | > 800 ms | Trigger special actions (e.g., open menu, next credential, factory reset confirmation) |
The firmware uses an **adaptive debounce filter** to ensure reliable input even with wet fingers or slight contact.
***
## Visual feedback
When you touch a point, the OLED display reacts instantly with:
* **Highlight animation** for the selected option.
* **Progress indicator** for long presses.
* **Soft transitions** between menu screens.
This immediate feedback helps users feel confident that every action has been registered — even without sound or vibration.
***
## Why touch instead of buttons?
| Feature | Physical buttons | ZeroKeyUSB touch system |
| ------------------ | ---------------- | ------------------------------------------- |
| Mechanical wear | High | None |
| Water resistance | Limited | Fully sealed |
| Dust protection | Requires gaskets | Hermetically encapsulated |
| Noise | Audible click | Silent |
| Lifespan | \~100k presses | Practically unlimited |
| Design flexibility | Fixed | Invisible, capacitive sensing through resin |
***
## Adaptive sensitivity
ZeroKeyUSB automatically calibrates the touch sensitivity during startup.\
This ensures stable performance across different environments — whether you’re using it in dry air, humid conditions, or even with light gloves.
The firmware dynamically adjusts thresholds to reject accidental touches from nearby objects or power noise.
***
## Minimal energy consumption
Despite scanning all channels continuously, the touch system consumes only a few microamps.\
This allows ZeroKeyUSB to remain **highly responsive** while staying **extremely power-efficient** — perfect for an always-ready USB device.
***
## Built to last
Because there are no mechanical parts, the touch interface contributes directly to the device’s lifespan and reliability.\
Even after years of use, the responsiveness remains identical to day one.
Combined with the waterproof encapsulation, this design ensures that **ZeroKeyUSB will continue to function flawlessly long after most electronic devices fail**.
***
The touch interface was designed for human interaction — it ignores static electricity, moisture, or random contact from objects.\
Only intentional touches are recognized.
# USB Interface
Source: https://docs.zerokeyusb.com/hardware/usb-interface
How ZeroKeyUSB powers up, enumerates, and secures communications over USB-C.
ZeroKeyUSB connects through a **USB-C receptacle** but behaves as a classic USB 2.0 full-speed device. The board keeps the wiring simple so that any USB-A or USB-C host can power and communicate with the key.
***
## Connector wiring
| Pin | Function | Notes |
| ------ | -------- | -------------------------------------------- |
| A1/B12 | GND | Tied together for symmetrical cables |
| A4/B9 | VBUS | 5 V input (up to 500 mA) |
| A5 | CC1 | 5.1 kΩ pull-down (Rd) advertises device mode |
| B5 | CC2 | 5.1 kΩ pull-down (Rd) for flipping cables |
| A6/B6 | D+ | Routed to SAMD21 USB pins |
| A7/B7 | D− | Routed to SAMD21 USB pins |
No high-speed pairs or USB 3.x pins are used. Shield is connected to ground through a 1 MΩ resistor and 10 nF capacitor for ESD suppression.
***
## Power path
* VBUS feeds a **3.3 V LDO regulator** (TPS73533) that powers the SAMD21, OLED, touch controller, and EEPROM.
* Total current stays under **120 mA** during OLED animations, well within USB 2.0 limits.
* Reverse current protection prevents powering the host when the device is off.
* A PTC resettable fuse (250 mA) adds short-circuit protection.
Because ZeroKeyUSB has no battery, unplugging the cable immediately cuts power and clears volatile RAM.
***
## USB descriptors
| Interface | Class | Purpose |
| ----------- | ------------------- | ------------------------------------------------------ |
| Interface 0 | HID Keyboard (0x03) | Auto-types usernames and passwords |
| Interface 1 | CDC ACM (0x02) | Serial console for backups, diagnostics, and time sync |
Each interface has its own endpoint pair, allowing simultaneous keyboard and serial communication without re-enumeration.
***
## Security considerations
* The firmware **ignores vendor-specific control requests** and only responds to standard USB descriptors.
* HID reports are generated solely from user-confirmed actions; there is no host-triggered typing.
* CDC commands require the device to be unlocked and, for destructive actions, a long-press confirmation.
* USB suspend triggers an immediate lock after 30 seconds of inactivity.
These safeguards ensure that plugging the key into an unknown host does not expose stored secrets.
***
## Troubleshooting enumeration
| Symptom | Cause | Solution |
| ------------------------------ | --------------------------------------- | -------------------------------------------------------------- |
| Device powers but not detected | CC resistors missing or incorrect | Verify 5.1 kΩ pull-downs on CC1/CC2. |
| Enumerates as “Unknown Device” | Firmware not running or bootloader mode | Reflash via UF2 or check for double-tap reset. |
| Serial port not appearing | CDC driver blocked | On Windows install the provided `.inf` from the firmware repo. |
If USB data lines are damaged, the device can still power up, but no credentials will be typed. Inspect the connector for debris or mechanical stress.
# ZeroKeyUSB Documentation
Source: https://docs.zerokeyusb.com/index
Central hub for guides, hardware specs, firmware internals, and support resources.
## Welcome
**ZeroKeyUSB** is a standalone, hardware-based password manager designed to keep your credentials completely offline.\
It behaves like a USB keyboard — typing your encrypted usernames, passwords, and optional TOTP codes wherever you need them.\
No apps. No cloud. No subscriptions.
Everything you need to **assemble the hardware**, **understand the firmware**, and **keep your device secure** is organized below.
Learn how to flash the firmware, set your Master PIN, and start using ZeroKeyUSB safely.
***
## Main Features
Protects your data using **AES-128 CBC encryption**. The key is generated by the ATECC608A's TRNG and stays inside slot 8 of the chip — encryption and decryption run on the secure element's hardware AES engine, never exposing the key to the MCU.\
The PIN is verified via SHA-256 and a hardware monotonic counter. No Internet connection ever required.
An **ATECC608A** secure element provides hardware-grade true random numbers, a tamper-resistant **monotonic counter** for PIN rate-limiting, and a chip-unique serial used as a PIN salt.
Built around a **SAMD21E18A microcontroller** and **M24C64-WMN6TP EEPROM**, fully powered by **USB-C**.\
No batteries, no wireless chips — truly air-gapped.
Acts as a standard **USB HID keyboard** supporting 9 language layouts (EN-US, DE, FR, ES, IT, PT, SV, DA, HU) to type credentials into any focused field.\
Works across all major operating systems.
Navigate stored credentials and 2FA codes on a **128×32-pixel SSD1306 OLED screen** with auto-scroll for long text.
Generate 6-digit 2FA codes **locally and offline**.\
Requires a one-time **time sync** from the host via USB — never through the Internet.
Every line of firmware and hardware schematic is public.\
Audit, verify, and contribute to improve the device.
Stores up to **61 site/user/password entries** plus optional TOTP secrets, all AES-128 CBC encrypted at rest in the external EEPROM.
***
## Security at a Glance
| Layer | Technology | Location |
| ------------------- | ----------------------------------------------------------------------- | ----------------------------------------- |
| Encryption | AES-128 ECB (hardware, ATECC608A `AES` command) + software CBC chaining | ATECC608A + SAMD21 MCU |
| Encryption key | 16 B random (ATECC608A TRNG, written by the chip itself at first boot) | ATECC608A slot 8 (IsSecret=1, never read) |
| IV | 16 B random (ATECC608A TRNG) | EEPROM `0x0010` |
| PIN hashing | SHA-256(PIN ∥ chip\_serial) | MCU (software) |
| PIN salt | 9-byte chip serial | ATECC608A Config Zone |
| PIN rate-limit | Persistent exponential backoff, re-applied at boot (no wipe) | EEPROM `0x0002` |
| Boot integrity | ECDSA P-256 signature verification | Bootloader |
| Physical protection | Epoxy encapsulation + BOOTPROT fuse | PCB / SAMD21 fuses |
***
## Documentation Overview
Step-by-step guide to flash the firmware, create your Master PIN, and store your first credentials.
Electrical schematics, component list, and EEPROM memory map for the **M24C64-WMN6TP**.
Explore how modules interact: display, menu navigation, EEPROM storage, keyboard output, and TOTP generation.
Understand how AES-128 CBC encryption, ATECC608A-assisted PIN verification, IV generation, and hardware rate-limiting secure your data.
Certifications, licensing, and community contribution guidelines.
FAQs, troubleshooting steps, and how to reach the ZeroKeyUSB community or request professional help.
***
## Need help?
Find user guides, updates, and community resources for ZeroKeyUSB.
# Open Source & Transparency
Source: https://docs.zerokeyusb.com/open-source
How we publish the firmware, schematics, and build process so you can verify — not blindly trust — ZeroKeyUSB.
## Philosophy
ZeroKeyUSB is intentionally **offline and closed for modification**, yet **open for inspection**.
Publishing the full firmware and hardware documentation lets anyone audit the security model while keeping production devices sealed and tamper-resistant.
***
## Repository overview
All public materials live in the [Depbit-lab/zerokeyusb](https://github.com/Depbit-lab/zerokeyusb) repository.
You will find:
* `firmware/` → C++ source code for the SAMD21 application, including crypto helpers and device drivers.
* `hardware/` → Schematics, PCB layout, and BOM files for each hardware revision.
* `tests/` → Unit tests that validate AES routines, EEPROM transactions, and TOTP calculations.
* `docs/` → Markdown guides that mirror this knowledge base.
Each tagged release includes the signed firmware binary (`zerokeyusb-vX.Y.Z.bin`) and SHA-256 checksums for independent verification.
***
## Reproducible builds
We publish the exact toolchain configuration used at the factory:
```bash theme={null}
docker pull ghcr.io/depbit-lab/zerokeyusb-toolchain:latest
docker run --rm -v "$PWD":/project ghcr.io/depbit-lab/zerokeyusb-toolchain make release
```
* The container ships with ARM GCC, openocd, and all dependencies pinned.
* Running `make release` produces a firmware image identical to the official one (matching checksum).
* The build artifacts include a manifest with git commit, build timestamp, and compiler flags.
***
## Security-first contributions
We welcome issues and pull requests that improve documentation, testing, or tooling.
To keep the production firmware auditable:
1. Development happens on feature branches.
2. Every change requires two maintainer reviews focused on security impact.
3. CI runs unit tests and static analysis (cppcheck, clang-tidy) on each commit.
4. Release candidates undergo manual hardware testing before a new tag is created.
No unsigned firmware is ever flashed to customer devices.
***
## Verifying your device
You can confirm that your ZeroKeyUSB runs the officially signed firmware:
1. Check the firmware version from **Menu → Settings → About**.
2. Download the matching release binary from GitHub and compute its SHA-256 hash.
3. Compare it against the checksum printed in the release notes.
4. (Optional) If you have factory tools, you can read the flash memory and verify the signature block — the repository documents the process.
This transparency gives you confidence that what you audit is exactly what ships.
***
## Community channels
* **Issues** → Report bugs, propose features, or request clarifications.
* **Discussions** → Share tips, automation scripts, or talk about self-hosted backups.
* **Security inbox** → Email `security@zerokeyusb.com` for coordinated vulnerability disclosure.
We believe trust is earned. Open documentation and reproducible builds are our way to prove it.
***
Open source does not mean modifiable firmware on retail units. The published code is for transparency, audits, and educational purposes.
# Getting Started
Source: https://docs.zerokeyusb.com/quickstart
Learn to use ZeroKeyUSB screen by screen. Initial tutorial, creating and editing credentials, 2FA, backups and more.
## Welcome to ZeroKeyUSB
This section walks you through the device screen by screen, with illustrations and the buttons you need to press at each step. No prior technical knowledge required — just the device and a USB-C cable.
***
## The five buttons
Everything is controlled with five golden touch pads arranged in a cross on the right of the screen:
| Pad | Symbol | Short tap | Long press (\~800 ms) |
| ---------- | ------ | --------------------------------- | --------------------------- |
| **Up** | ⬆ | Move/scroll up, increment digit | — |
| **Down** | ⬇ | Move/scroll down, decrement digit | — |
| **Left** | ⬅ | Go back, previous digit | Jump 10 credentials back |
| **Right** | ➡ | Go forward, add digit | Jump 10 credentials forward |
| **Center** | ● | Confirm, select, type to host | Edit the current field |
Throughout these guides you will see the button highlighted in **cyan** on the illustrations. That indicates the button to press to move to the next step.
***
## Scenario index
Full walkthrough of the wizard that appears the first time you plug the device in: orientation, keyboard layout and master PIN creation.
From unlocking with the PIN to saving your first password using the "Add New" screen and the on-device editor.
How to open the editor, move the cursor, use the three keyboard pages, generate random passwords and save.
How to set the date and time the first time, view the 6-digit code with countdown and type it to the host computer.
How to access the main menu from the credential list and move through Tools, Settings, Danger and Info.
Export all your credentials as a hardware-encrypted CSV. When to do it and how to store it safely.
Restore credentials from an earlier backup or migrate from another password manager.
***
## Before you start
Plug ZeroKeyUSB into any USB-C port on your computer, tablet, or phone. The device draws about 20 mA — less than a wireless mouse — and needs no battery.
If the golden dots end up on your left instead of on the right, simply flip the device. During the initial tutorial you can also rotate the screen in software.
Before starting the initial tutorial it helps to have a 4–16 digit PIN in mind. If you forget it, **there is no recovery** — only wiping and starting over.
***
## Conventions used in these guides
| Notation | Meaning |
| ------------------------------- | ----------------------------------------------------------------------------- |
| **Press X** | Short tap on button X (less than 800 ms) |
| **Hold X** | Long press on button X (more than 800 ms — you will see the cyan ring appear) |
| Cyan button in the illustration | The button to press to advance to the next step |
| Cyan button with large halo | Long press |
| `monospaced text` | Text that appears literally on the OLED screen |
Want to try before you have the device in hand? Open [`Animaciones/simulador.html`](https://github.com/Depbit-lab/zerokeyusb) in any modern browser: it emulates the screen and buttons with the keyboard (W/A/S/D/arrows/Space).
# FAQ
Source: https://docs.zerokeyusb.com/support/faq
Answers to the most common questions about ZeroKeyUSB — security, compatibility, and daily use.
## General
### 🧩 What is ZeroKeyUSB?
ZeroKeyUSB is a **hardware password manager** that stores your credentials completely **offline**.\
It behaves like a regular USB keyboard: when you select a saved account, it simply **types your login details** automatically — no software or internet connection required.
***
### 🔌 Does it need an app or subscription?
No.\
ZeroKeyUSB does not depend on any software, extensions, or subscriptions.\
It works instantly when plugged into any computer, phone, or tablet that accepts a USB keyboard.
***
### 💻 What devices is it compatible with?
ZeroKeyUSB works universally with:
* Windows
* macOS
* Linux
* Android (via USB-C or adapter)
* iPadOS (USB-C models)
Because it emulates a standard keyboard, it works wherever you can type.
***
### 🔋 Does it have a battery?
No.\
ZeroKeyUSB draws minimal power (around 20 mA) directly from the USB port.\
This makes it **maintenance-free** and ensures your credentials are always available — even years later.
***
### 🧑💻 How many credentials can it store?
Up to **61 encrypted credentials**, each containing:
* Website or service name (up to 32 characters)
* Username or email (up to 32 characters)
* Password (up to 32 characters)
* (Optional) TOTP 2FA secret
***
## Security
### 🔐 How are my passwords protected?
Your credentials are protected by **three layers**:
1. **AES-128 CBC encryption** — every credential is encrypted with a 128-bit key generated by the ATECC608A hardware random number generator. This key is unique to your device.
2. **PIN verification** — your Master PIN is hashed with SHA-256 using the chip's unique serial number as salt. The hash is compared in constant-time to prevent timing attacks.
3. **Persistent rate-limiting** — every wrong PIN triggers an exponential delay (up to ≈ 43 minutes) stored in EEPROM and re-applied on every boot, so it cannot be skipped by power-cycling. Guesses are throttled to the point of being impractical, without ever destroying your data.
Even if someone physically extracts the memory chip, the AES key required to decrypt the data was generated by the hardware TRNG and is not derived from your PIN.
***
### 🧠 What happens if I forget my PIN?
For security reasons, there is **no PIN recovery**.\
The only option is a **Factory Reset**, which wipes all encrypted data and lets you create a new PIN.\
This ensures that no one — not even the manufacturer — can access your information.
> 💡 Tip: Choose a memorable PIN and keep an encrypted backup of your credentials.
***
### 🕐 Does it connect to the Internet?
Never.\
ZeroKeyUSB is a **fully offline system**.\
It has no Wi-Fi, Bluetooth, or NFC modules, and it never exchanges data with external servers.\
You are the only one who can access the stored information.
***
### 💾 Can someone clone my device?
No.\
Each ZeroKeyUSB contains an **ATECC608A secure element** with a factory-programmed, unique 9-byte serial number. This serial is used as a salt in the PIN hash, meaning the same PIN on two different devices produces completely different cryptographic keys.\
The 128-bit AES master key is also device-unique (generated by the on-chip TRNG at provisioning).
***
### 🚫 What happens after too many wrong PIN attempts?
Two levels of protection:
**Soft backoff (UX layer):**
| Failed attempts | Waiting time |
| --------------- | ------------------------ |
| 1 | 5 seconds |
| 2 | 10 seconds |
| 3 | 20 seconds |
| 4 | 40 seconds |
| … | Doubles up to 43 minutes |
**Persistent enforcement:**
The failed-attempt counter lives in EEPROM and the accumulated delay is re-applied **on every boot, before the PIN screen accepts input** — so cutting power mid-countdown does not skip it. After \~10 wrong PINs each further guess costs ≈ 43 minutes, which makes brute force impractical. Your data is **never automatically wiped** by wrong PINs; the vault is only erased by a user-initiated Factory Reset.
> Note: an attacker who physically opens the device and reaches the I²C bus could read the PIN hash and crack it offline, bypassing this delay. That is why the board is encapsulated in epoxy resin and why a long PIN matters.
***
### 🧰 Can the firmware be updated?
Yes, but only with **physical access**.\
From the menu (**Danger Zone → Bootloader Mode**), the device reboots into a USB DFU bootloader.\
New firmware must be **cryptographically signed** — the bootloader checks both a CRC32 and a BLAKE2s MAC before accepting any image. Unsigned firmware triggers a 15-second penalty delay.
There is no remote or over-the-air update mechanism.
***
### 🔍 Is it really open source?
Yes — for **transparency and auditability**.\
Publishing the code lets anyone verify that:
* There are no backdoors or data collection mechanisms.
* Encryption follows established standards (AES-128, SHA-256, HMAC-SHA1).
* All functions operate exactly as described.
The device runs a **signed version** of this same code, verified by the bootloader at every boot.
***
## Usage
### 🌍 The keyboard types wrong symbols — what can I do?
Go to **Menu → Settings → Keyboard** and cycle through the 9 supported layouts:\
`EN-US`, `DA-DK`, `DE-DE`, `ES-ES`, `FR-FR`, `HU-HU`, `IT-IT`, `PT-PT`, `SV-SE`.
You can also set this during the initial setup wizard.
***
### 🧾 Can I back up my data?
Yes.\
Use **Menu → Backup → Export** to send all credentials over USB serial in plaintext CSV format.\
You can later **Import** the same backup.
> ⚠️ **Backup files are plaintext.** Encrypt them with GPG, age, or a password-protected ZIP, and store offline.
***
### ⏱️ How does the 2FA (TOTP) feature work?
ZeroKeyUSB can generate **time-based one-time passwords (TOTP)** offline.\
It supports **SHA-1**, **SHA-256**, and **SHA-512** algorithms.\
Once you import a TOTP secret and sync the time, the device displays a 6-digit code with a 30-second countdown — without needing your phone or Internet.
***
### 🌙 The screen went dark on its own — is it broken?
No. After **1 minute without touching it**, the screen turns off to save the
OLED. The device is **not locked** — just touch any pad and it wakes up on the
same screen where you left it. Using the [browser extension](/getting-started/browser-extension)
also wakes it. Your session and PIN state are untouched.
***
### 🧼 Is it waterproof?
Yes.\
Each ZeroKeyUSB unit is **fully resin-encapsulated**, making it resistant to water, dust, and everyday wear.\
It is not designed for submersion, but it will survive accidental spills or rain exposure.
***
### 🧱 What if the screen breaks?
Your data remains safe — it's still encrypted inside the EEPROM.\
However, you'll need to contact support for a replacement, as the device cannot be disassembled without breaking the resin seal.\
You can still export your credentials via the USB serial interface (the CDC channel works without the display).
***
### 🛡️ How long will it last?
ZeroKeyUSB has no moving parts or batteries.\
The EEPROM is rated for **>1 million write cycles per page**, and all other components are solid-state.\
With normal use, it should last **well over a decade**.
***
### 💬 How can I contact support?
For any questions, reach out directly at\
📧 **[support@zerokeyusb.com](mailto:support@zerokeyusb.com)**\
or visit **[zerokeyusb.com/support](https://zerokeyusb.com/support)**
***
ZeroKeyUSB is designed to give you peace of mind — you own your passwords, and your data never leaves your hands.
# Troubleshooting
Source: https://docs.zerokeyusb.com/support/troubleshooting
Recommended steps to diagnose and fix common issues with ZeroKeyUSB.
> Follow the sections in order — many issues are resolved after completing the previous steps.
## Before You Begin
1. Ensure the device is receiving stable power through the USB-C connection.
2. Confirm that you are using the **original factory firmware** (updates are rarely needed).
3. Note any on-screen error messages and recent configuration changes.
4. If possible, back up your credentials via the **local web manager** before making changes.
***
## Quick Symptom & Solution Table
| Symptom | Possible Cause | Recommended Action |
| ---------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Device does not power on | USB cable or port not supplying power | Try a different USB-C cable or port; avoid USB hubs; connect directly to a PC or power bank |
| OLED screen stays blank | Display initialization delay or EEPROM not responding | Wait 5 seconds after connection; if persistent, reconnect or check solder joints on the EEPROM |
| Touch buttons unresponsive | TS06 controller not detected or miscalibrated | Clean the golden pads and reconnect; if still unresponsive, perform a factory reset |
| `EEPROM ERROR` or `IV MISSING` on screen | Memory communication or data corruption | Power-cycle and retry; if persistent, contact support for inspection |
| Wrong PIN delays access | Exponential lockout triggered after failed attempts | Wait for the countdown to complete and retry with the correct PIN |
| TOTP shows `REQTIME` | Time not synchronized with host | Use the web manager and press **Sync Time** before generating codes |
***
## Step-by-Step Procedures
### 1. Safe Restart
* Unplug ZeroKeyUSB.
* Wait about **5 seconds**.
* Reconnect to a USB power source (PC or phone).\
The splash screen should appear within 3–5 seconds.
### 2. Reset Touch Controller
1. Disconnect and reconnect the device.
2. Wait until the **ZeroKeyUSB** logo appears.
3. Tap each golden pad to confirm all five respond.\
If touch remains unresponsive, perform a **factory reset** to recalibrate automatically.
### 3. Firmware Flash (development units only)
> ⚠️ Production devices are resin-encapsulated and cannot be reflashed by the user.\
> This section applies **only to pre-production boards**.
1. Quickly reconnect the USB cable twice within one second to enter bootloader mode.
2. Copy the firmware `.uf2` file to the drive named **ZEROBOOT**.
3. Wait for the file transfer to complete, then reconnect normally.
4. Verify the firmware version under **Menu → Settings → About**.
### 4. Factory Reset and Reconfiguration
* Export credentials via the web manager (**Backup → Export**).
* Hold the **center touch pad** for about 10 seconds until the confirmation countdown appears.
* The process erases all memory, including credentials, PIN signature, and IV.
* After restart, re-import your backup or run the first-time setup again.
***
## Collecting Data for Support
* Connect the device and open the **Serial Monitor** at **115200 bps** to capture log output.
* Take photos of any on-screen errors.
* Note the **serial number** (`SN ZK-XXXXXXXX`) and **firmware version** from **Menu → Settings → About**.
***
## Contacting Support
If problems persist:
* Submit a support ticket at [zerokeyusb.com/support](https://zerokeyusb.com/support) including:
* Device serial number
* Firmware version
* Screenshots or photos of the issue
* Steps you’ve already tried
* Our team will reply within **24 business hours** with further guidance.
> ⚠️ **Do not attempt to open or reflash a sealed device.**\
> Doing so will destroy the waterproof encapsulation and void warranty.
***
This guide covers user-level diagnostics.\
For factory calibration or advanced debugging, contact authorized service partners directly.