Clarify password role, random die, updated wording
All checks were successful
check / check (push) Successful in 14s
All checks were successful
check / check (push) Successful in 14s
- Password help text now explains it encrypts the recovery phrase on disk and is not used for address derivation - Die button generates cryptographically random phrases using crypto.getRandomValues(), different each click - "roll the die for a new one" wording - README documents full encryption scheme (PBKDF2 + AES-256-GCM) and explicitly notes password is not part of BIP-39 derivation
This commit is contained in:
20
README.md
20
README.md
@@ -201,12 +201,22 @@ want maximum privacy can point it at their own Ethereum node.
|
|||||||
|
|
||||||
- **No framework**: The popup UI is vanilla JS and HTML. The extension is small
|
- **No framework**: The popup UI is vanilla JS and HTML. The extension is small
|
||||||
enough that a framework adds unnecessary complexity and attack surface.
|
enough that a framework adds unnecessary complexity and attack surface.
|
||||||
- **Encrypted storage**: Seed phrases are encrypted with a user-provided
|
- **Encrypted storage**: Recovery phrases and private keys are encrypted at rest
|
||||||
password using AES-256-GCM before being stored in the extension's local
|
in the extension's local storage. The encryption scheme:
|
||||||
storage. The encryption key is derived from the password using PBKDF2 with a
|
- The user's password is run through PBKDF2-SHA256 (600,000 iterations) with
|
||||||
high iteration count.
|
a random salt to derive a 256-bit encryption key.
|
||||||
|
- The encryption key + a random IV encrypt the secret material using
|
||||||
|
AES-256-GCM.
|
||||||
|
- Stored blob: `{ salt, iv, ciphertext, authTag }`.
|
||||||
|
- **The password is NOT used in address derivation.** It exists solely to
|
||||||
|
protect the recovery phrase / private key on disk. Anyone with the
|
||||||
|
recovery phrase can restore the wallet on any device without this
|
||||||
|
password. This matches MetaMask's behavior.
|
||||||
- **BIP-39 / BIP-44**: Standard mnemonic generation and HD key derivation
|
- **BIP-39 / BIP-44**: Standard mnemonic generation and HD key derivation
|
||||||
(`m/44'/60'/0'/0/n`) for Ethereum address compatibility.
|
(`m/44'/60'/0'/0/n`) for Ethereum address compatibility. The BIP-39 passphrase
|
||||||
|
is always empty (matching MetaMask and most wallet software). The user's
|
||||||
|
password is completely separate and has no effect on which addresses are
|
||||||
|
generated.
|
||||||
- **EIP-1193 provider**: The content script injects a `window.ethereum` object
|
- **EIP-1193 provider**: The content script injects a `window.ethereum` object
|
||||||
that implements the EIP-1193 provider interface, enabling web3 site
|
that implements the EIP-1193 provider interface, enabling web3 site
|
||||||
connectivity.
|
connectivity.
|
||||||
|
|||||||
@@ -56,8 +56,8 @@
|
|||||||
Add Wallet
|
Add Wallet
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mb-2">
|
<p class="mb-2">
|
||||||
Enter your 12 or 24 word recovery phrase below, or press the
|
Enter your 12 or 24 word recovery phrase below, or click the
|
||||||
die to generate a new one.
|
button to roll the die for a new one.
|
||||||
</p>
|
</p>
|
||||||
<div class="mb-1 flex justify-end">
|
<div class="mb-1 flex justify-end">
|
||||||
<button
|
<button
|
||||||
@@ -88,8 +88,10 @@
|
|||||||
<div class="mb-2" id="add-wallet-password-section">
|
<div class="mb-2" id="add-wallet-password-section">
|
||||||
<label class="block mb-1">Choose a password</label>
|
<label class="block mb-1">Choose a password</label>
|
||||||
<p class="text-xs text-muted mb-1">
|
<p class="text-xs text-muted mb-1">
|
||||||
This password locks the wallet on this device. It is not
|
This password encrypts your recovery phrase on this
|
||||||
the same as your recovery phrase.
|
device. It does not affect your wallet addresses or
|
||||||
|
funds — anyone with your recovery phrase can restore
|
||||||
|
your wallet without this password.
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@@ -154,7 +156,9 @@
|
|||||||
<div class="mb-2" id="import-key-password-section">
|
<div class="mb-2" id="import-key-password-section">
|
||||||
<label class="block mb-1">Choose a password</label>
|
<label class="block mb-1">Choose a password</label>
|
||||||
<p class="text-xs text-muted mb-1">
|
<p class="text-xs text-muted mb-1">
|
||||||
This password locks the wallet on this device.
|
This password encrypts your private key on this device.
|
||||||
|
Anyone with your private key can access your funds
|
||||||
|
without this password.
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ function makeStubAddress() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateStubMnemonic() {
|
// Stub wordlist for random phrase generation.
|
||||||
// TODO: replace with real BIP-39 generation via background
|
// TODO: replace with real BIP-39 generation via background.
|
||||||
const words = [
|
const STUB_WORDLIST = [
|
||||||
"abandon",
|
"abandon",
|
||||||
"ability",
|
"ability",
|
||||||
"able",
|
"able",
|
||||||
@@ -85,8 +85,138 @@ function generateStubMnemonic() {
|
|||||||
"abuse",
|
"abuse",
|
||||||
"access",
|
"access",
|
||||||
"accident",
|
"accident",
|
||||||
|
"account",
|
||||||
|
"accuse",
|
||||||
|
"achieve",
|
||||||
|
"acid",
|
||||||
|
"acoustic",
|
||||||
|
"acquire",
|
||||||
|
"across",
|
||||||
|
"act",
|
||||||
|
"action",
|
||||||
|
"actor",
|
||||||
|
"actual",
|
||||||
|
"adapt",
|
||||||
|
"add",
|
||||||
|
"addict",
|
||||||
|
"address",
|
||||||
|
"adjust",
|
||||||
|
"admit",
|
||||||
|
"adult",
|
||||||
|
"advance",
|
||||||
|
"advice",
|
||||||
|
"aerobic",
|
||||||
|
"affair",
|
||||||
|
"afford",
|
||||||
|
"afraid",
|
||||||
|
"again",
|
||||||
|
"age",
|
||||||
|
"agent",
|
||||||
|
"agree",
|
||||||
|
"ahead",
|
||||||
|
"aim",
|
||||||
|
"air",
|
||||||
|
"airport",
|
||||||
|
"aisle",
|
||||||
|
"alarm",
|
||||||
|
"album",
|
||||||
|
"alcohol",
|
||||||
|
"alert",
|
||||||
|
"alien",
|
||||||
|
"all",
|
||||||
|
"alley",
|
||||||
|
"allow",
|
||||||
|
"almost",
|
||||||
|
"alone",
|
||||||
|
"alpha",
|
||||||
|
"already",
|
||||||
|
"also",
|
||||||
|
"alter",
|
||||||
|
"always",
|
||||||
|
"amateur",
|
||||||
|
"amazing",
|
||||||
|
"among",
|
||||||
|
"amount",
|
||||||
|
"amused",
|
||||||
|
"analyst",
|
||||||
|
"anchor",
|
||||||
|
"ancient",
|
||||||
|
"anger",
|
||||||
|
"angle",
|
||||||
|
"angry",
|
||||||
|
"animal",
|
||||||
|
"ankle",
|
||||||
|
"announce",
|
||||||
|
"annual",
|
||||||
|
"another",
|
||||||
|
"answer",
|
||||||
|
"antenna",
|
||||||
|
"antique",
|
||||||
|
"anxiety",
|
||||||
|
"any",
|
||||||
|
"apart",
|
||||||
|
"apology",
|
||||||
|
"appear",
|
||||||
|
"apple",
|
||||||
|
"approve",
|
||||||
|
"april",
|
||||||
|
"arch",
|
||||||
|
"arctic",
|
||||||
|
"area",
|
||||||
|
"arena",
|
||||||
|
"argue",
|
||||||
|
"arm",
|
||||||
|
"armed",
|
||||||
|
"armor",
|
||||||
|
"army",
|
||||||
|
"around",
|
||||||
|
"arrange",
|
||||||
|
"arrest",
|
||||||
|
"arrive",
|
||||||
|
"arrow",
|
||||||
|
"art",
|
||||||
|
"artefact",
|
||||||
|
"artist",
|
||||||
|
"artwork",
|
||||||
|
"ask",
|
||||||
|
"aspect",
|
||||||
|
"assault",
|
||||||
|
"asset",
|
||||||
|
"assist",
|
||||||
|
"assume",
|
||||||
|
"asthma",
|
||||||
|
"athlete",
|
||||||
|
"atom",
|
||||||
|
"attack",
|
||||||
|
"attend",
|
||||||
|
"attitude",
|
||||||
|
"attract",
|
||||||
|
"auction",
|
||||||
|
"audit",
|
||||||
|
"august",
|
||||||
|
"aunt",
|
||||||
|
"author",
|
||||||
|
"auto",
|
||||||
|
"autumn",
|
||||||
|
"average",
|
||||||
|
"avocado",
|
||||||
|
"avoid",
|
||||||
|
"awake",
|
||||||
|
"aware",
|
||||||
|
"awesome",
|
||||||
|
"awful",
|
||||||
|
"awkward",
|
||||||
|
"axis",
|
||||||
];
|
];
|
||||||
return words.join(" ");
|
|
||||||
|
function generateStubMnemonic() {
|
||||||
|
const phrase = [];
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
const bytes = new Uint32Array(1);
|
||||||
|
crypto.getRandomValues(bytes);
|
||||||
|
phrase.push(STUB_WORDLIST[bytes[0] % STUB_WORDLIST.length]);
|
||||||
|
}
|
||||||
|
return phrase.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- render wallet list on main view --
|
// -- render wallet list on main view --
|
||||||
|
|||||||
Reference in New Issue
Block a user