A-1209000154249630: passkey authentication - #256
Open
owlsua wants to merge 7 commits into
Open
Conversation
The suite resolved every wasm_wrappers import to a hand-written mock, so nothing exercised the real bindings and several suites had to stay disabled. - load the real module through src/tests/helpers/initWasm.js - stub import.meta in the test env only, guarded so new.target still works - map bare "buffer" to the npm polyfill; Buffer instances from node's builtin belong to another realm and the wasm bindings reject them - polyfill structuredClone for fake-indexeddb - delete the 1627-line mock, now unreferenced
Mintlayer is created by default for every new account, so the "Add wallet" tile only ever appeared for records predating that default. The flow asked for the seed phrase again and re-derived the mintlayer keys from it. Removes the component, the connect item that opened it, and the four pieces of dashboard state that fed it. An account left without mintlayer keys is fixed by recreating it.
The migrations were fired un-awaited from onupgradeneeded and each re-opened the database from inside the versionchange transaction. They therefore resolved after the first account had already been saved and overwrote it. Each also issued its own getAll() before any of them wrote, so a later migration clobbered an earlier one's work. - migrations become pure account -> account transforms - they run inside event.target.transaction over a single read - oldVersion === 0 returns early; a brand-new database has nothing to migrate - only records the transforms actually changed are written back - SCHEMAVERSION derives from ACCOUNT_MIGRATIONS.length so the ladder and the version cannot drift apart
A worker reported failure by posting { error }, and the caller resolved with
that object instead of rejecting. Mintlayer API payloads legitimately carry
an "error" field, so the marker is now __workerError, and an unknown job
answers instead of leaving the promise pending forever.
runJob also forwards the whole payload to the worker rather than re-listing
the fields it expects. Re-listing is how an argument gets silently dropped,
and the direct-import path used by jest never sees it.
A random 32-byte DEK encrypts the content. Each credential that may open the wallet wraps that DEK separately: the password through PBKDF2-SHA512, and each passkey through a WebAuthn PRF output run into HKDF-SHA256. Enrolling a credential adds 92 bytes and never touches the seed. Every AES-GCM operation is bound with additionalData, so a blob cannot be moved into another field, a password wrapper cannot be opened as a passkey wrapper, and an externally supplied HTLC hash cannot collide with an internal field name. Those strings and the KDF parameters are a storage format and are pinned by known-answer tests. Versions 1 to 3 keep opening: getContentKey returns the password key and the aad helper returns undefined below version 4. Migration happens on a successful unlock, under the account lock, with the version re-checked, and is swallowed on failure - an outdated format beats a wallet that will not open. Accounts without mintlayer keys no longer fail to unlock.
Adds the Passkeys section to Settings - enrol, list and remove - and a second button on the unlock screen that appears only when the account has a passkey. Enrolling asks for the password, because that is the only way to obtain the DEK to wrap. Removing an entry stops that authenticator opening this wallet; the copy says plainly that backups exported earlier still accept it. A passkey unlock runs no PBKDF2 at all, so it returns as fast as the authenticator answers.
Covers the things that cost time to rediscover: the packing step that creates popup.html, the three places path aliases are declared, why the worker path is invisible to jest, that the AAD strings are a storage format, and that the account lock only serialises within one document.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💬 Description
Unlock a wallet with Touch ID, Windows Hello or a hardware key instead of typing the password. The password keeps working and stays the only way to recover the wallet
Accounts moved to a v4 key envelope: a random 32-byte DEK encrypts the content, and each credential wraps its own copy of that DEK - the password through PBKDF2, each passkey through the WebAuthn PRF output. Enrolling adds 92 bytes and never re-encrypts the seed. A passkey unlock runs no PBKDF2 at all
Every AES-GCM blob is bound with
additionalData, so it cannot be moved into another fieldExisting wallets keep working. v1–v3 still open, and migrate to v4 on a successful unlock
More
📷 Screenshots
📋 Checklist:
A-[id of Asana task]A-[id of Asana task]: [short description]prettierchecklintcheck