Skip to content

Commit

Permalink
Validate mnemonic phrases
Browse files Browse the repository at this point in the history
  • Loading branch information
Mrtenz committed Nov 25, 2024
1 parent 44fb90d commit 2cbb41d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src/derivers/bip39.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ describe('mnemonicToSeed', () => {
const generatedSeed = await mnemonicToSeed(mnemonic);
expect(generatedSeed).toStrictEqual(seed);
});

it('throws if the length of the mnemonic phrase is invalid', async () => {
await expect(mnemonicToSeed('test')).rejects.toThrow(
'Invalid mnemonic phrase: The mnemonic phrase must consist of 12, 15, 18, 21, or 24 words.',
);
});

it('throws if the mnemonic phrase contains invalid words', async () => {
await expect(
mnemonicToSeed(
'test test test test test test test test test invalid mnemonic phrase',
),
).rejects.toThrow(
'Invalid mnemonic phrase: The mnemonic phrase contains an unknown word.',
);
});
});

describe('with passphrase', () => {
Expand Down
37 changes: 32 additions & 5 deletions src/derivers/bip39.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ import type { Curve } from '../curves';
import { SLIP10Node } from '../SLIP10Node';
import { getFingerprint } from '../utils';

const MNEMONIC_PHRASE_LENGTHS = [12, 15, 18, 21, 24];

/**
* Validate a BIP-39 mnemonic phrase. The phrase must:
*
* - Consist of 12, 15, 18, 21, or 24 words.
* - Contain only words from the English wordlist.
*
* @param mnemonicPhrase - The mnemonic phrase to validate.
* @throws If the mnemonic phrase is invalid.
*/
function validateMnemonicPhrase(mnemonicPhrase: string) {
const words = mnemonicPhrase.split(' ');

assert(
MNEMONIC_PHRASE_LENGTHS.includes(words.length),
`Invalid mnemonic phrase: The mnemonic phrase must consist of 12, 15, 18, 21, or 24 words.`,
);

assert(
words.every((word) => englishWordlist.includes(word)),
'Invalid mnemonic phrase: The mnemonic phrase contains an unknown word.',
);
}

/**
* Encode a BIP-39 mnemonic phrase to a `Uint8Array` for use in seed generation.
* If the mnemonic is already a `Uint8Array`, it is assumed to contain the
Expand All @@ -25,14 +50,16 @@ function encodeMnemonicPhrase(
wordlist: string[],
) {
if (typeof mnemonic === 'string') {
validateMnemonicPhrase(mnemonic);
return stringToBytes(mnemonic.normalize('NFKD'));
}

return stringToBytes(
Array.from(new Uint16Array(mnemonic.buffer))
.map((i) => wordlist[i])
.join(' '),
);
const mnemonicString = Array.from(new Uint16Array(mnemonic.buffer))
.map((i) => wordlist[i])
.join(' ');

validateMnemonicPhrase(mnemonicString);
return stringToBytes(mnemonicString);
}

/**
Expand Down

0 comments on commit 2cbb41d

Please sign in to comment.