Skip to content

v0.2.8 — single-touch WebAuthn login + touch-default build - #14

Merged
TheMaxMur merged 3 commits into
mainfrom
develop
Jun 21, 2026
Merged

TheMaxMur merged 3 commits into
mainfrom
develop

Conversation

@TheMaxMur

Copy link
Copy Markdown
Owner

v0.2.8 — single-touch WebAuthn login + touch-default build

Changed

  • A WebAuthn login is a single touch by default (420cfb2). RS-Key now
    honors the platform's silent pre-flight probe (getAssertion with up:false):
    it returns the credential-discovery assertion with no touch and the UP flag
    clear, as the spec and YubiKey do. Previously the up option was ignored and
    every assertion polled the button, so an allowCredentials (non-resident)
    login cost two touches. New strict-up cargo feature (off) restores the
    touch-on-every-assertion behavior; fido-conformance enables it implicitly.
  • Requiring a touch is the unconditional default, not a cargo feature
    (3079649). Dropped up-button (which was on by default); the no-touch test
    image is now the explicit opt-in --features no-touch (was
    --no-default-features) across CI / check.sh / nix / docs. The default
    firmware binary is unchanged.

bcdDevice 0x077F → 0x0780.

Verification

  • rsk-fido host tests (201, both default + strict-up); a mutation-checked test
    proves up:false is silent (UP=0) while up:true is denied.
  • Default + no-touch + strict-up firmware builds and clippy clean.
  • HW: flashed 0x0780 to the board — boots, Yubico identity, FIDO getInfo + CCID
    (PIV/OpenPGP/vendor) all green.

🤖 Generated with Claude Code

TheMaxMur and others added 3 commits June 21, 2026 19:51
…ict-up opt-in

A WebAuthn login with an allowCredentials (non-resident) credential cost two
touches: the platform runs a silent pre-flight getAssertion with the `up`
option false to discover which listed credential the key holds, then the real
assertion. RS-Key ignored the `up` option entirely and polled the button on
every assertion, so the pre-flight took a touch too.

Honor up:false: the options map now parses `up`, and get_assertion skips the
presence poll and clears the UP flag when up is false — the spec/YubiKey
behavior, which makes such a login a single touch. getNextAssertion reuses the
originating request's decision (stored in the assertion state) so a silent
resident discovery stays silent across the whole walk. Resident-credential /
passkey logins were and remain a single touch.

The new `strict-up` cargo feature (off by default) restores the
touch-on-every-assertion behavior for anyone who prefers an explicit gesture
per assertion. `fido-conformance` enables it implicitly so the conformance
image keeps the exact behavior the 235/0 pass was validated with.

Tests: up:false returns an assertion under a declining presence (no touch) with
UP=0, while up:true is denied (mutation-proof); a strict-up-gated test proves
up:false still polls the button there. docs/build.md documents the feature.

bcdDevice 0x077F -> 0x0780.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ure (was up-button)

Requiring a BOOTSEL touch is the secure, correct behavior, so it should be the
unconditional default — not a feature that happens to be on by default and that
`--no-default-features` silently removes. Invert it: drop the `up-button`
feature and add an explicit `no-touch` opt-in for the automated test image.

- firmware: the presence.rs / led.rs cfg gates flip from `feature = "up-button"`
  (and its negation) to `feature = "no-touch"`; firmware/Cargo.toml drops
  `default = ["up-button"]` + `up-button` and adds `no-touch`.
- build plumbing: the no-touch image is now `--features no-touch` everywhere it
  was `--no-default-features` — the CI `flavors` matrix, check.sh, the nix
  firmware-no-touch* flavors, and the docs (build / quickstart / testing /
  interop / architecture / led, README, releases).

The default firmware binary is the same touch build as before, so no bcdDevice
change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o-touch refactor) + readme version

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TheMaxMur
TheMaxMur merged commit 88fd975 into main Jun 21, 2026
12 checks passed
TheMaxMur added a commit that referenced this pull request Aug 13, 2026
`crates/rsk-display` was extracted from `firmware/` so that something other than
a flashed board with a panel soldered on could run the screen flow. It then sat
there with zero tests, which is the half of the extraction that was never done —
and it is the crate holding the anti-phishing guarantee.

It can run on the host because the panel and the touch controller are already
type parameters and the rest of the board is behind `Hooks`: the doubles here are
a panel that records what was drawn, a pad that reads back a scripted sequence of
samples, and a board whose backlight, wake button and presence flags are fields.
`embassy-time`'s `std` feature supplies the clock, so the deadlines and debounces
under test are the real ones rather than stubs.

115 cases. The ones worth naming are the ones this flow has already got wrong:

  - a finger already down when a screen appears is not a tap on it. The panel
    reports level, not edges, and a wake press held past the release wait landed
    on Onboard's full-width "Continue without PIN" button — consuming a fresh
    device's one-time PIN offer (audit run-33).
  - a status-glyph change repaints without disarming the panel. The host drives
    `led_status` around every dispatch, so counting it as a new surface let a
    plain CTAP loop swallow every tap (audit run-34 #14).
  - a host ceremony loop cannot hold the auto-lock off. It counts from the last
    LOCAL interaction, which is what `power.rs` promises a host cannot postpone
    (audit run-34 #15); and "Display sleep: Off" switches off blanking only, not
    a security control.
  - approve is a HOLD: a brush does not approve, and two three-quarter holds are
    not one whole one — lifting the finger resets the fill. A finger left over
    from a previous ceremony cannot approve the next one, and an expired
    registration card does not read a leftover press as a fresh Save.
  - a queued host command cannot shut the PIN pad on its first poll, and never
    mid-entry: without the floor, a host repeating any ungated command holds the
    owner's unlock pad shut for as long as it likes.
  - the device PIN and the FIDO clientPIN have separate retry counters, a spent
    budget is not revived by the right PIN, and a panel-set clientPIN ends the
    session token the old PIN authorized (CTAP 2.1 §6.5.5.6).
  - a `Deny` tap reaches the applet as `Declined`, not as a timeout — the BOOTSEL
    button has no such gesture, and over CCID, which carries no CTAPHID_CANCEL,
    a cancel correctly degrades to the timeout.

The `no_std` attribute is cfg'd off for test builds, as in `rsk-fs` and `rsk-ui`.
`docs/architecture.md` was missing the crate from its map entirely, right above
the line claiming everything but `firmware` runs the full suite on the host —
true only as of this commit.

Test-only: no `bcdDevice` bump. The gate now lints and runs the crate.
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.

1 participant