Skip to content

Restrict host key-writes to user ECC slots (block reserved 117-132, keep backup)#29

Open
0c-coder wants to merge 1 commit into
trustcrypto:nemoclaw-openclaw-security-layerfrom
0c-coder:fix/reject-host-key-set-reserved-slots
Open

Restrict host key-writes to user ECC slots (block reserved 117-132, keep backup)#29
0c-coder wants to merge 1 commit into
trustcrypto:nemoclaw-openclaw-security-layerfrom
0c-coder:fix/reject-host-key-set-reserved-slots

Conversation

@0c-coder

@0c-coder 0c-coder commented Jun 29, 2026

Copy link
Copy Markdown

Restrict host key-writes to user ECC slots (block reserved 117–132, keep backup)

Problem

ECC key slots split into user slots 101–116 and reserved slots 117–132
(128 web-derivation, 129/130 HMAC, 131 backup, 132 derivation; 117–127 held in
reserve). The OKSETPRIV host message takes the target slot directly from the
host (recv_buffer[5]), and the handler did not restrict it — so a host could
write a key (RSA, ECC, or an ML-KEM/X-Wing seed) into a reserved slot and
overwrite an internal key.

Fix

Guard the host dispatch in recvmsg’s case OKSETPRIV so a host-specified
slot must be a user slot, with one carve-out for the backup key:

if (recv_buffer[0] != 0xBA) {
    if (recv_buffer[5] >= 117 && recv_buffer[5] <= 132 &&
        !(recv_buffer[5] == RESERVED_KEY_DEFAULT_BACKUP && (recv_buffer[6] & 0x80))) {
        hidprint("Error cannot set key in reserved slot (117-132)");
        return;
    }
    set_private(recv_buffer);
}

Result:

  • Host can set any key type in user ECC slots 101–116 (and RSA 1–4).
  • Host cannot set arbitrary keys in reserved slots 117–132.
  • The designated backup key (slot 131 with the 0x80 backup-type flag) is
    still allowed — this is what the OnlyKey app sends from setBackupPassphrase.

Why this does not break HMAC / backup / derivation

These were checked against both the firmware call graph and the OnlyKey app:

  • Backup passphrase — app setBackupPassphrase()setPrivateKey(slot=131, type=0xA1)OKSETPRIV. This does hit the guarded dispatch, so it is
    explicitly carved out (slot 131 + backup flag).
  • HMAC / Yubico challenge-response — app setYubiAuth() sends the dedicated
    YUBIAUTH message, handled by the feature-report path (process_setreport),
    which calls set_private() directly and never enters this dispatch. Slots
    129/130 unaffected.
  • Derivation / web-derivation (128/132) — written internally by the SSH/GPG
    derivation handlers via direct set_private() calls. Unaffected.
  • DUO backup app-setup — a separate else if branch
    (recv_buffer[6] > 0x80 && initialized == false), not the guarded path.

The guard lives at the host dispatch precisely because every internal/reserved
write path calls set_private() directly and bypasses it.

Compatibility / risk

  • No change for RSA key slots (1–4) or user ECC slots (101–116).
  • No change for HMAC, backup, or derivation provisioning.
  • Pairs with the python-onlykey age-plugin host change, which already limits
    PQ key slots to 101–116.

Testing

Requires the firmware build/flash toolchain (not run in this change). Verify:

  1. OKSETPRIV with a non-backup key type and slot 117–132Error cannot set key in reserved slot (117-132); nothing written.
  2. Setting the backup passphrase (slot 131, type 0xA1) still succeeds.
  3. HMAC/Yubico key setup still writes 129/130.
  4. OKSETPRIV to user slots 101–116 and RSA 1–4 → unchanged, all key
    types.
  5. SSH/GPG derivation still writes 128/132.

Host OKSETPRIV requests may only target user slots (RSA 1-4, ECC 101-116).
Reserved ECC slots 117-132 are rejected EXCEPT the designated backup key
(slot 131 with the 0x80 backup-type flag), which the app sets via
setBackupPassphrase. HMAC (129/130, YUBIAUTH/feature-report) and derivation
(128/132, internal) use other paths that call set_private() directly and
bypass this dispatch, so they remain unaffected.
@0c-coder 0c-coder force-pushed the fix/reject-host-key-set-reserved-slots branch from 84e8f31 to 2ca6f14 Compare June 29, 2026 20:55
@0c-coder 0c-coder changed the title Reject host attempts to set keys in reserved ECC slots (117-132) Restrict host key-writes to user ECC slots (block reserved 117-132, keep backup) Jun 29, 2026
@0c-coder

Copy link
Copy Markdown
Author

Host-side counterpart: trustcrypto/python-onlykey#90 — the age plugin now writes post-quantum keys only to user ECC slots 101–116 (and reads/decaps there), which this PR enforces on-device. These two should land together.

@0c-coder

Copy link
Copy Markdown
Author

Web app PQC counterpart: onlykey/onlykey.github.io#39 (ML-KEM-768 / X-Wing support in the OnlyKey web app).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants