Ramblings of an aging IT geek
← Ramblings of an aging IT geek
linux

rosec 0.0.33: The Security Key Signs In

rosec 0.0.33 lands WebAuthn end to end: register and sign in with passkeys from an unmodified browser, plus a rotatable plugin trust root and a hardening pass.

Listen to this post
A once-phantom security key now solid and connected, a browser success tick, a ring of keys alongside

This is a pretty chunky release for rosec, big enough that I genuinely flirted with calling it 0.1.0 before coming to my senses.

The star of the show is something I've been waiting for: WebAuthn support. An unmodified browser can now register a passkey with rosec and log in with it, full ceremony, no extensions, browser ↔ vault and back. Passkeys, end-to-end.

A passkey is really just a private key with some metadata, and since rosec's providers already know how to look after private keys, it was mostly a matter of exposing them properly. The local vault, Bitwarden items, and KeePassXC databases all feed into the same model now. rosec only hands the key over at the exact moment of signing, then zeroizes it immediately. That's the "don't lose it" part handled.

The tricky half was the "make the browser happy" part. Browsers talk CTAP2 to actual security keys, not random files, so FUSE was never going to cut it. We had to become a real (virtual) security key. /dev/uhid lets userspace create one… but it's root-only, because the same interface could just as easily pretend to be a keyboard and type whatever it likes.

Enter the tiny socket-activated broker rosec-uhid. It runs as root for a few milliseconds, spins up a FIDO-only device from a hard-coded descriptor, hands the file descriptor to your unprivileged daemon, and immediately exits. Close the fd and the device disappears. Crash the daemon? No phantom key left behind. Lock the vault? It's the digital equivalent of yanking the USB stick. It's off by default and desktop-only: install the broker and set fido2 = true if you want it.

Trust you can actually rotate

The other notable change is how we handle trust for provider plugins. They were already signed WASM modules, but the daemon only trusted one hardcoded key. Rotating it was… awkward. Now we have a proper set of trust roots, so key rotation gets a graceful deprecation period instead of a surprise flag day. There's also a user-configurable trust anchor in rosec.toml, so third-party authors can ship signed plugins without forcing people to turn off verification entirely.

We also added proper tooling: rosec-package-wasm for authors and rosec provider validate for users. It even schema-validates the sandbox policy before signing, so any silly mistakes blow up on the author's machine instead of yours. Considerate, aren't we?

The rest

We did a full security review on the new code and the wider daemon: the virtual authenticator's device node is now 0600 owned by you (no more leaning on seat uaccess), passed file descriptors are close-on-exec, the portal backend properly verifies its caller, and we sprinkled in some DoS and zeroization fixes for good measure.

The prompt also gained a scrollable single-select mode, very handy when you have multiple passkeys that match the same site.

Usual caveat: these are software passkeys, not hardware-bound. So sites that demand hardware attestation will still turn up their noses. That's the trade-off for keys you can actually back up and move between machines.

Repo and docs are on GitHub.