registry: passkeys, WebAuthn registration and sign-in (slice 4 of #16) #226

Closed
opened 2026-10-08 21:26:56 +00:00 by orion · 4 comments
Collaborator

Goal

Passkeys for the registry: a signed-in person registers a passkey on the account page, and signs in with it later. A passkey counts as the second factor on its own (owner decision on #16).

Owner decision (recorded on #16)

  • Account page served by the registry with one small script for WebAuthn.
  • Accepted algorithms: ES256 (std/signature's P-256, slice 3 (#225)), EdDSA (std/signature's Ed25519) and RS256 (the registry's src/rsa.trb).
  • A passkey is possession plus user verification and counts as the second factor by itself.

Pinned records

Acceptance criteria

  • Registration: a single-use challenge of at least 16 random bytes bound to the session; clientDataJSON type webauthn.create, the challenge and the registry's origin checked; RP ID hash, user-present and user-verified flags checked; attestation none accepted (no attestation trust store); the COSE public key (-7 ES256, -8 EdDSA, -257 RS256) parsed and stored with the credential id and the signature counter.
  • Authentication: challenge, type webauthn.get, origin, RP ID hash, user-present and user-verified flags, the signature over authenticatorData || SHA-256(clientDataJSON) with the stored key, and the signature counter (a counter that does not increase, where either side is non-zero, is refused and flagged).
  • A minimal CBOR reader for what attestation objects and COSE keys need (maps, byte and text strings, integers), refusing anything else, with its own tests.
  • Removing the last sign-in method is refused (shared rule with slice 1 (#223)).
  • Tests against recorded authenticator data for each of the three algorithms (made by a software authenticator, recorded into the test, no network): every step once as it works and once as it is refused (wrong challenge, origin, RP ID, missing UV flag, bad signature, replayed counter, unsupported algorithm); torb test tools/registry/tests green natively and in the VM.
  • tools/registry/README.md and RELEASE.md "Not built yet" updated.

Seed impact

None: TorbScript in tools/registry on std/signature, std/digest, std/json; no syntax change, no native, no driver flag, no looked-up std name. If the CBOR reader turns out generally useful it stays private to the registry in this slice.

Size and dependencies

One PR, one or two Conventional Commits. Depends on slice 1 (#223, sessions, account page) and slice 3 (#225, P-256 verification). Parent: #16. Not ready until both have landed. Security-relevant: architect review of the diff.

## Goal Passkeys for the registry: a signed-in person registers a passkey on the account page, and signs in with it later. A passkey counts as the second factor on its own (owner decision on #16). ## Owner decision (recorded on #16) - Account page served by the registry with one small script for WebAuthn. - Accepted algorithms: ES256 (`std/signature`'s P-256, slice 3 (#225)), EdDSA (`std/signature`'s Ed25519) and RS256 (the registry's `src/rsa.trb`). - A passkey is possession plus user verification and counts as the second factor by itself. ## Pinned records - [RELEASE.md 7.1 Owners](https://git.torb.dev/torbscript/language/src/commit/45be87a8bdba3d0a00212c7a82fa8e451bef398c/docs/design/RELEASE.md#L866) and [7.6 Security](https://git.torb.dev/torbscript/language/src/commit/45be87a8bdba3d0a00212c7a82fa8e451bef398c/docs/design/RELEASE.md#L1029) - W3C WebAuthn Level 3 sections 7.1 (registration) and 7.2 (authentication); RFC 8949 (CBOR) and RFC 9052/9053 (COSE keys) ## Acceptance criteria - [ ] Registration: a single-use challenge of at least 16 random bytes bound to the session; `clientDataJSON` type `webauthn.create`, the challenge and the registry's origin checked; RP ID hash, user-present and user-verified flags checked; attestation `none` accepted (no attestation trust store); the COSE public key (`-7` ES256, `-8` EdDSA, `-257` RS256) parsed and stored with the credential id and the signature counter. - [ ] Authentication: challenge, type `webauthn.get`, origin, RP ID hash, user-present and user-verified flags, the signature over `authenticatorData || SHA-256(clientDataJSON)` with the stored key, and the signature counter (a counter that does not increase, where either side is non-zero, is refused and flagged). - [ ] A minimal CBOR reader for what attestation objects and COSE keys need (maps, byte and text strings, integers), refusing anything else, with its own tests. - [ ] Removing the last sign-in method is refused (shared rule with slice 1 (#223)). - [ ] Tests against recorded authenticator data for each of the three algorithms (made by a software authenticator, recorded into the test, no network): every step once as it works and once as it is refused (wrong challenge, origin, RP ID, missing UV flag, bad signature, replayed counter, unsupported algorithm); `torb test tools/registry/tests` green natively and in the VM. - [ ] `tools/registry/README.md` and RELEASE.md "Not built yet" updated. ## Seed impact None: TorbScript in `tools/registry` on `std/signature`, `std/digest`, `std/json`; no syntax change, no native, no driver flag, no looked-up std name. If the CBOR reader turns out generally useful it stays private to the registry in this slice. ## Size and dependencies One PR, one or two Conventional Commits. Depends on slice 1 (#223, sessions, account page) and slice 3 (#225, P-256 verification). Parent: #16. Not `ready` until both have landed. Security-relevant: architect review of the diff.
Author
Collaborator

[loop:torbscript] intake: in scope (author orion, label loop), slice 4 of #16 filed by the split, not a duplicate, no collision with a design record, no seed impact. next: waiting (slice 1 #223 has not landed; slice 3 #225 landed) until 2026-10-10T04:00:00Z

[loop:torbscript] intake: in scope (author orion, label loop), slice 4 of #16 filed by the split, not a duplicate, no collision with a design record, no seed impact. next: waiting (slice 1 #223 has not landed; slice 3 #225 landed) until 2026-10-10T04:00:00Z
Author
Collaborator

[loop:torbscript] refine: the split body already meets the definition of ready (goal, recorded owner decision, pinned records, testable criteria, size, no seed impact); both dependencies have landed (#223 with PR #249, #225), label ready set. It edits the same registry files as #224 (database, account page, server routes), which starts now, so it queues behind it. next: waiting (#224 in implementation on the same files) until 2026-10-09T12:25Z

[loop:torbscript] refine: the split body already meets the definition of ready (goal, recorded owner decision, pinned records, testable criteria, size, no seed impact); both dependencies have landed (#223 with PR #249, #225), label `ready` set. It edits the same registry files as #224 (database, account page, server routes), which starts now, so it queues behind it. next: waiting (#224 in implementation on the same files) until 2026-10-09T12:25Z
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261009 until 2026-10-09T14:25Z

[loop:torbscript] claim implement by session:loop-torbscript-20261009 until 2026-10-09T14:25Z
argus closed this issue 2026-10-09 09:36:31 +00:00
Author
Collaborator

[loop:torbscript] close: PR #261 (registry passkeys, WebAuthn registration and sign-in) approved and merged by argus (merge commit 4eaf8b21), issue closed. Claim released. Next slice: #227 (refine when the ready buffer has room), then #228.

[loop:torbscript] close: PR #261 (registry passkeys, WebAuthn registration and sign-in) approved and merged by argus (merge commit 4eaf8b21), issue closed. Claim released. Next slice: #227 (refine when the ready buffer has room), then #228.
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
torbscript/language#226
No description provided.