fix(hub): pad short passkey coordinates and name the KeePassXC localhost switch

KeePassXC 2.7.12 drops leading zero bytes of the P-256 x and y coordinates, so about one key in 128 failed registration. p256_point now left-pads 1 to 32 bytes and still rejects empty, oversized, out-of-field and off-curve points. KeePassXC also refuses passkeys on http://localhost until its Allow using localhost with passkeys option is on; the passkey page now says where to tick it. The zero-led test key search is capped at 4096 tries.

🎯 Fix: 92 %
💥 Break: 3 %
💸 Cost: 396 %
⏱️ Time: 147 %
🛡️ Security: 100 %
🏆 Score: 15
🚦 Gate: ⚠️ cost 396 % > 100, time 147 % > 100: ask the operator before the commit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-06 18:46:40 +00:00
1 parent 249265cf1b
commit 5417ca33b4
4 files changed
+103 -4

No files matched your search

+3 -1
View File
@@ -5,6 +5,7 @@
const b64url = (buffer) => btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const unb64url = (text) => Uint8Array.from(atob(text.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(text.length / 4) * 4, "=")), (char) => char.charCodeAt(0));
const localLink = () => h("a", {href: localUrl()}, localUrl());
const KEEPASSXC_LOCALHOST = "KeePassXC lehnt Passkeys auf http://localhost ab, bis in KeePassXC unter Werkzeuge → Einstellungen → Browser-Integration → Fortgeschritten „Nutzung von localhost mit Passkeys zulassen“ (Allow using localhost with passkeys) angehakt ist; die Datenbank muss entsperrt sein. Sonst meldet KeePassXC-Browser „Passkeys request canceled.“ und Chrome öffnet seinen eigenen Passkey-Dialog.";
async function assertion(prepared, label) {
if (!onLocalhost()) {
@@ -67,7 +68,7 @@
h("div", {class: "command"}, h("code", {}, command), copyButton(command)),
h("label", {}, "Credential", h("textarea", {class: "field", readonly: true, rows: 3}, blob)));
} catch (error) {
output.replaceChildren(h("p", {class: "muted"}, `nicht registriert: ${(error && error.name) || error}`));
output.replaceChildren(h("p", {class: "muted"}, `nicht registriert: ${(error && error.name) || error}`), h("p", {class: "muted"}, KEEPASSXC_LOCALHOST));
}
}
@@ -79,6 +80,7 @@
return h("section", {}, h("h3", {}, "Passkey"),
h("p", {class: "muted"}, "Freigaben von Asks, Reviews und Operator-Befehlen signiert ein Passkey in KeePassXC; Runner, Hook und Commit-Skills prüfen die Signatur selbst gegen die gepinnten Schlüssel."),
keys,
h("p", {class: "muted"}, KEEPASSXC_LOCALHOST),
onLocalhost() ? null : h("p", {class: "muted"}, "WebAuthn braucht die Domain: ", localLink()),
h("div", {}, btn("Passkey registrieren", "btn small soft", () => registerPasskey(output))),
output);