> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zerokeyusb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ATECC608A Secure Element

> 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=<n>` 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.
