Align JS bip39 with wasm-crypto (#730)
* Align JS bip39 with wasm-crypto * Cleanups
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
"@polkadot/util": "^3.6.2-5",
|
||||
"@polkadot/wasm-crypto": "^1.4.1",
|
||||
"base-x": "^3.0.8",
|
||||
"bip39": "^3.0.2",
|
||||
"blakejs": "^1.1.0",
|
||||
"bn.js": "^5.1.3",
|
||||
"create-hash": "^1.2.0",
|
||||
"elliptic": "^6.5.3",
|
||||
"js-sha3": "^0.8.0",
|
||||
"pbkdf2": "^3.1.1",
|
||||
@@ -42,6 +42,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^4.11.6",
|
||||
"@types/create-hash": "^1.2.0",
|
||||
"@types/elliptic": "^6.4.12",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"@types/scryptsy": "^2.0.0",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Adapted from the bitcoinjs/bip39 source
|
||||
// https://github.com/bitcoinjs/bip39/blob/1d063b6a6aee4145b34d701037cd3e67f5446ff9/ts_src/index.ts
|
||||
// Copyright (c) 2014, Wei Lu <luwei.here@gmail.com> and Daniel Cousens <email@dcousens.com>
|
||||
// ISC Licence
|
||||
//
|
||||
// Change made in this version -
|
||||
// - Adjust formatting (just eslint differences)
|
||||
// - Only English wordlist (this aligns with the wasm-crypto implementation)
|
||||
// - Use util-crypto randomAsU8a (instead of randombytes)
|
||||
// - Remove setting of wordlost passing of wordlist in functions
|
||||
// - Remove mnemonicToSeed (we only use the sync variant)
|
||||
|
||||
import createHash from 'create-hash';
|
||||
import { pbkdf2Sync } from 'pbkdf2';
|
||||
|
||||
import randomAsU8a from '../random/asU8a';
|
||||
import DEFAULT_WORDLIST from './bip39-en';
|
||||
|
||||
const INVALID_MNEMONIC = 'Invalid mnemonic';
|
||||
const INVALID_ENTROPY = 'Invalid entropy';
|
||||
const INVALID_CHECKSUM = 'Invalid mnemonic checksum';
|
||||
|
||||
function normalize (str?: string): string {
|
||||
return (str || '').normalize('NFKD');
|
||||
}
|
||||
|
||||
function binaryToByte (bin: string): number {
|
||||
return parseInt(bin, 2);
|
||||
}
|
||||
|
||||
function bytesToBinary (bytes: number[]): string {
|
||||
return bytes.map((x: number) => x.toString(2).padStart(8, '0')).join('');
|
||||
}
|
||||
|
||||
function deriveChecksumBits (entropyBuffer: Uint8Array): string {
|
||||
const ENT = entropyBuffer.length * 8;
|
||||
const CS = ENT / 32;
|
||||
const hash = createHash('sha256')
|
||||
.update(entropyBuffer)
|
||||
.digest();
|
||||
|
||||
return bytesToBinary(Array.from(hash)).slice(0, CS);
|
||||
}
|
||||
|
||||
function salt (password?: string): string {
|
||||
return 'mnemonic' + (password || '');
|
||||
}
|
||||
|
||||
export function mnemonicToSeedSync (mnemonic: string, password?: string): Buffer {
|
||||
const mnemonicBuffer = Buffer.from(normalize(mnemonic), 'utf8');
|
||||
const saltBuffer = Buffer.from(salt(normalize(password)), 'utf8');
|
||||
|
||||
return pbkdf2Sync(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512');
|
||||
}
|
||||
|
||||
export function mnemonicToEntropy (mnemonic: string): string {
|
||||
const words = normalize(mnemonic).split(' ');
|
||||
|
||||
if (words.length % 3 !== 0) {
|
||||
throw new Error(INVALID_MNEMONIC);
|
||||
}
|
||||
|
||||
// convert word indices to 11 bit binary strings
|
||||
const bits = words
|
||||
.map(
|
||||
(word: string): string => {
|
||||
const index = DEFAULT_WORDLIST.indexOf(word);
|
||||
|
||||
if (index === -1) {
|
||||
throw new Error(INVALID_MNEMONIC);
|
||||
}
|
||||
|
||||
return index.toString(2).padStart(11, '0');
|
||||
}
|
||||
)
|
||||
.join('');
|
||||
|
||||
// split the binary string into ENT/CS
|
||||
const dividerIndex = Math.floor(bits.length / 33) * 32;
|
||||
const entropyBits = bits.slice(0, dividerIndex);
|
||||
const checksumBits = bits.slice(dividerIndex);
|
||||
|
||||
// calculate the checksum and compare
|
||||
const entropyBytes = entropyBits.match(/(.{1,8})/g)?.map(binaryToByte);
|
||||
|
||||
if (!entropyBytes || entropyBytes.length < 16) {
|
||||
throw new Error(INVALID_ENTROPY);
|
||||
} else if (entropyBytes.length > 32) {
|
||||
throw new Error(INVALID_ENTROPY);
|
||||
} else if (entropyBytes.length % 4 !== 0) {
|
||||
throw new Error(INVALID_ENTROPY);
|
||||
}
|
||||
|
||||
const entropy = Buffer.from(entropyBytes);
|
||||
const newChecksum = deriveChecksumBits(entropy);
|
||||
|
||||
if (newChecksum !== checksumBits) {
|
||||
throw new Error(INVALID_CHECKSUM);
|
||||
}
|
||||
|
||||
return entropy.toString('hex');
|
||||
}
|
||||
|
||||
export function entropyToMnemonic (entropy: Uint8Array): string {
|
||||
// 128 <= ENT <= 256
|
||||
if (entropy.length < 16) {
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
}
|
||||
|
||||
if (entropy.length > 32) {
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
} else if (entropy.length % 4 !== 0) {
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
}
|
||||
|
||||
const entropyBits = bytesToBinary(Array.from(entropy));
|
||||
const checksumBits = deriveChecksumBits(entropy);
|
||||
|
||||
const bits = entropyBits + checksumBits;
|
||||
|
||||
// we just set it prior, so this is a safe check
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const chunks = bits.match(/(.{1,11})/g)!;
|
||||
|
||||
const words = chunks.map(
|
||||
(binary: string): string => {
|
||||
const index = binaryToByte(binary);
|
||||
|
||||
return DEFAULT_WORDLIST[index];
|
||||
}
|
||||
);
|
||||
|
||||
return words.join(' ');
|
||||
}
|
||||
|
||||
export function generateMnemonic (strength?: number): string {
|
||||
strength = strength || 128;
|
||||
|
||||
if (strength % 32 !== 0) {
|
||||
throw new TypeError(INVALID_ENTROPY);
|
||||
}
|
||||
|
||||
return entropyToMnemonic(randomAsU8a(strength / 8));
|
||||
}
|
||||
|
||||
export function validateMnemonic (mnemonic: string): boolean {
|
||||
try {
|
||||
mnemonicToEntropy(mnemonic);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
import '../polyfill';
|
||||
|
||||
import { generateMnemonic } from 'bip39';
|
||||
import { bip39Generate, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { generateMnemonic } from './bip39';
|
||||
|
||||
export type WordCount = 12 | 15 | 18 | 21 | 24;
|
||||
|
||||
// mapping of words to the actual strength (as expected)
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
import '../polyfill';
|
||||
|
||||
import { mnemonicToEntropy } from 'bip39';
|
||||
import { hexAddPrefix, hexToU8a } from '@polkadot/util';
|
||||
import { bip39ToEntropy, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { mnemonicToEntropy } from './bip39';
|
||||
|
||||
export default function toEntropy (mnemonic: string): Uint8Array {
|
||||
return isReady()
|
||||
? bip39ToEntropy(mnemonic)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { mnemonicToSeedSync } from 'bip39';
|
||||
import { bufferToU8a } from '@polkadot/util';
|
||||
|
||||
import { bip39ToSeed, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { mnemonicToSeedSync } from './bip39';
|
||||
|
||||
/**
|
||||
* @name toSeed
|
||||
* @summary Creates a valid Ethereum/Bitcoin-compatible seed from a mnemonic input
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
|
||||
import '../polyfill';
|
||||
|
||||
import { validateMnemonic } from 'bip39';
|
||||
import { bip39Validate, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { validateMnemonic } from './bip39';
|
||||
|
||||
/**
|
||||
* @name mnemonicValidate
|
||||
* @summary Validates a mnemonic input using [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki).
|
||||
|
||||
@@ -1845,14 +1845,15 @@ __metadata:
|
||||
"@polkadot/util": ^3.6.2-5
|
||||
"@polkadot/wasm-crypto": ^1.4.1
|
||||
"@types/bn.js": ^4.11.6
|
||||
"@types/create-hash": ^1.2.0
|
||||
"@types/elliptic": ^6.4.12
|
||||
"@types/pbkdf2": ^3.1.0
|
||||
"@types/scryptsy": ^2.0.0
|
||||
"@types/xxhashjs": ^0.2.2
|
||||
base-x: ^3.0.8
|
||||
bip39: ^3.0.2
|
||||
blakejs: ^1.1.0
|
||||
bn.js: ^5.1.3
|
||||
create-hash: ^1.2.0
|
||||
elliptic: ^6.5.3
|
||||
js-sha3: ^0.8.0
|
||||
pbkdf2: ^3.1.1
|
||||
@@ -2015,6 +2016,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/create-hash@npm:^1.2.0":
|
||||
version: 1.2.2
|
||||
resolution: "@types/create-hash@npm:1.2.2"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: d9bd1821c26137be78eeda9d78e6a402ae766637a30323241adf23f6601d3053f2d37073798e1d36dd370fea75592a73f6ccda50a20f2b4080d2cfaee9264eac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/elliptic@npm:^6.4.12":
|
||||
version: 6.4.12
|
||||
resolution: "@types/elliptic@npm:6.4.12"
|
||||
@@ -2131,13 +2141,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:11.11.6":
|
||||
version: 11.11.6
|
||||
resolution: "@types/node@npm:11.11.6"
|
||||
checksum: 2b64bfc2344f60fdcf3baea2f9f2e065189cee3a6144d3dbdcb32cb6728dd64933808e2da8bd4d69033f04510f7de1397c08d54d8f89d505b3a957b24dcc583f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/normalize-package-data@npm:^2.4.0":
|
||||
version: 2.4.0
|
||||
resolution: "@types/normalize-package-data@npm:2.4.0"
|
||||
@@ -3174,18 +3177,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bip39@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "bip39@npm:3.0.2"
|
||||
dependencies:
|
||||
"@types/node": 11.11.6
|
||||
create-hash: ^1.1.0
|
||||
pbkdf2: ^3.0.9
|
||||
randombytes: ^2.0.1
|
||||
checksum: 24c59dafc6c3b5c4ff0b8d0861509c0007bc0301b217125d1f1de6a39e84d21c9cfc1df743d472237c2ec4116387a46389843b36c95585b0eacebb6f5be6ecbd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"blakejs@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "blakejs@npm:1.1.0"
|
||||
@@ -8955,7 +8946,7 @@ fsevents@^1.2.7:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pbkdf2@npm:^3.0.3, pbkdf2@npm:^3.0.9, pbkdf2@npm:^3.1.1":
|
||||
"pbkdf2@npm:^3.0.3, pbkdf2@npm:^3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "pbkdf2@npm:3.1.1"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user