Cleanup pbkdf2 use (#757)
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
"js-sha3": "^0.8.0",
|
||||
"pbkdf2": "^3.1.1",
|
||||
"scryptsy": "^2.1.0",
|
||||
"sha.js": "^2.4.11",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"xxhashjs": "^0.2.2"
|
||||
},
|
||||
@@ -47,6 +48,7 @@
|
||||
"@types/elliptic": "^6.4.12",
|
||||
"@types/pbkdf2": "^3.1.0",
|
||||
"@types/scryptsy": "^2.0.0",
|
||||
"@types/sha.js": "^2.4.0",
|
||||
"@types/xxhashjs": "^0.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
// - 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 createSha from 'sha.js';
|
||||
import { assert, u8aToU8a } from '@polkadot/util';
|
||||
|
||||
import { pbkdf2Encode } from '../pbkdf2';
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
import DEFAULT_WORDLIST from './bip39-en';
|
||||
|
||||
@@ -39,7 +39,7 @@ function bytesToBinary (bytes: number[]): string {
|
||||
function deriveChecksumBits (entropyBuffer: Uint8Array): string {
|
||||
const ENT = entropyBuffer.length * 8;
|
||||
const CS = ENT / 32;
|
||||
const hash = createHash('sha256')
|
||||
const hash = createSha('sha256')
|
||||
.update(entropyBuffer)
|
||||
.digest();
|
||||
|
||||
@@ -50,11 +50,11 @@ function salt (password?: string): string {
|
||||
return 'mnemonic' + (password || '');
|
||||
}
|
||||
|
||||
export function mnemonicToSeedSync (mnemonic: string, password?: string): Buffer {
|
||||
export function mnemonicToSeedSync (mnemonic: string, password?: string): Uint8Array {
|
||||
const mnemonicBuffer = Buffer.from(normalize(mnemonic), 'utf8');
|
||||
const saltBuffer = Buffer.from(salt(normalize(password)), 'utf8');
|
||||
|
||||
return pbkdf2Sync(mnemonicBuffer, saltBuffer, 2048, 64, 'sha512');
|
||||
return pbkdf2Encode(mnemonicBuffer, saltBuffer).password;
|
||||
}
|
||||
|
||||
export function mnemonicToEntropy (mnemonic: string): Uint8Array {
|
||||
|
||||
@@ -30,14 +30,12 @@ describe('mnemonicGenerate', (): void => {
|
||||
|
||||
[true, false].forEach((onlyJs): void => {
|
||||
it(`generates not deterministic (onlyJs=${onlyJs.toString()})`, (): void => {
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
const m1 = mnemonicGenerate(24, onlyJs);
|
||||
const m2 = mnemonicGenerate(24, onlyJs);
|
||||
const m1 = mnemonicGenerate(24, onlyJs);
|
||||
const m2 = mnemonicGenerate(24, onlyJs);
|
||||
|
||||
expect(m1 === m2).toEqual(false);
|
||||
expect(mnemonicValidate(m1)).toEqual(true);
|
||||
expect(mnemonicValidate(m2)).toEqual(true);
|
||||
});
|
||||
expect(m1 === m2).toEqual(false);
|
||||
expect(mnemonicValidate(m1)).toEqual(true);
|
||||
expect(mnemonicValidate(m2)).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,9 +13,10 @@ describe('mnemonicToEntropy', (): void => {
|
||||
});
|
||||
|
||||
tests.forEach(([mnemonic, entropy], index): void => {
|
||||
it(`Created correct entropy for ${index}`, (): void => {
|
||||
expect(u8aToHex(mnemonicToEntropy(mnemonic))).toEqual(entropy);
|
||||
expect(u8aToHex(mnemonicToEntropy(mnemonic, true))).toEqual(entropy);
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`Created correct entropy for ${index} (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(u8aToHex(mnemonicToEntropy(mnemonic, onlyJs))).toEqual(entropy);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,12 +14,11 @@ describe('mnemonicToLegacySeed', (): void => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
it('generates a valid seed', (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MENMONIC))
|
||||
).toEqual(SEED);
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MENMONIC, undefined, true))
|
||||
).toEqual(SEED);
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToLegacySeed(MENMONIC, undefined, onlyJs))
|
||||
).toEqual(SEED);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { bufferToU8a } from '@polkadot/util';
|
||||
|
||||
import { bip39ToSeed, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { mnemonicToSeedSync } from './bip39';
|
||||
@@ -27,5 +25,5 @@ import { mnemonicToSeedSync } from './bip39';
|
||||
export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs = false): Uint8Array {
|
||||
return isReady() && !onlyJs
|
||||
? bip39ToSeed(mnemonic, password)
|
||||
: bufferToU8a(mnemonicToSeedSync(mnemonic, password)).subarray(0, 32);
|
||||
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32);
|
||||
}
|
||||
|
||||
@@ -15,29 +15,24 @@ describe('mnemonicToMiniSecret', (): void => {
|
||||
await cryptoWaitReady();
|
||||
});
|
||||
|
||||
it('generates a valid seed', (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToMiniSecret(MNEMONIC))
|
||||
).toEqual(SEED);
|
||||
expect(
|
||||
u8aToHex(mnemonicToMiniSecret(MNEMONIC, undefined, true))
|
||||
).toEqual(SEED);
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`generates a valid seed (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToMiniSecret(MNEMONIC, undefined, onlyJs))
|
||||
).toEqual(SEED);
|
||||
});
|
||||
});
|
||||
|
||||
tests.forEach(([mnemonic, , seed], index): void => {
|
||||
it(`Created correct seed for ${index}`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToMiniSecret(mnemonic, 'Substrate'))
|
||||
).toEqual(
|
||||
// mini returned here, only check first 32-bytes (64 hex + 2 prefix)
|
||||
seed.substr(0, 66)
|
||||
);
|
||||
expect(
|
||||
u8aToHex(mnemonicToMiniSecret(mnemonic, 'Substrate', true))
|
||||
).toEqual(
|
||||
// mini returned here, only check first 32-bytes (64 hex + 2 prefix)
|
||||
seed.substr(0, 66)
|
||||
);
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`Created correct seed for ${index} (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(mnemonicToMiniSecret(mnemonic, 'Substrate', onlyJs))
|
||||
).toEqual(
|
||||
// mini returned here, only check first 32-bytes (64 hex + 2 prefix)
|
||||
seed.substr(0, 66)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { pbkdf2Sync } from 'pbkdf2';
|
||||
import { bufferToU8a, stringToU8a, u8aToBuffer } from '@polkadot/util';
|
||||
import { stringToU8a, u8aToBuffer } from '@polkadot/util';
|
||||
import { bip39ToMiniSecret, isReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { pbkdf2Encode } from '../pbkdf2';
|
||||
import { mnemonicToEntropy } from './toEntropy';
|
||||
|
||||
export function mnemonicToMiniSecret (mnemonic: string, password = '', onlyJs = false): Uint8Array {
|
||||
@@ -16,7 +16,5 @@ export function mnemonicToMiniSecret (mnemonic: string, password = '', onlyJs =
|
||||
const salt = u8aToBuffer(stringToU8a(`mnemonic${password}`));
|
||||
|
||||
// return the first 32 bytes as the seed
|
||||
return bufferToU8a(
|
||||
pbkdf2Sync(entropy, salt, 2048, 64, 'sha512')
|
||||
).slice(0, 32);
|
||||
return pbkdf2Encode(entropy, salt).password.slice(0, 32);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { u8aEq, u8aToHex } from '@polkadot/util';
|
||||
import { waitReady } from '@polkadot/wasm-crypto';
|
||||
|
||||
import { randomAsU8a } from '../random/asU8a';
|
||||
import { pbkdf2Encode } from '.';
|
||||
|
||||
const KNOWN_SALT = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]);
|
||||
const TEST_PASSWORD = 'test password';
|
||||
|
||||
describe('pbkdf2Encode', (): void => {
|
||||
beforeEach(async (): Promise<void> => {
|
||||
await waitReady();
|
||||
});
|
||||
|
||||
it('has equivalent Wasm & JS results', (): void => {
|
||||
const salt = randomAsU8a();
|
||||
|
||||
expect(
|
||||
u8aEq(
|
||||
pbkdf2Encode(TEST_PASSWORD, salt, 2048, false).password,
|
||||
pbkdf2Encode(TEST_PASSWORD, salt, 2048, true).password
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
[false, true].forEach((onlyJs): void => {
|
||||
it(`creates known iterations (onlyJs = ${onlyJs.toString()})`, (): void => {
|
||||
expect(
|
||||
u8aToHex(pbkdf2Encode(TEST_PASSWORD, KNOWN_SALT, 2048, onlyJs).password)
|
||||
).toEqual(
|
||||
'0x600ba9ad65e4294d112e028fdad5dd8fce0a6a6e6b89fb36ed006785ccc3b3aec46831b3105c24237293e6cfa1a0ef6717c113f87ff9237a3f73d210adfa6634'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -13,11 +13,12 @@ interface Result {
|
||||
salt: Uint8Array;
|
||||
}
|
||||
|
||||
export function pbkdf2Encode (passphrase?: Uint8Array | string, salt = randomAsU8a(), rounds = 2048): Result {
|
||||
const u8a = u8aToU8a(passphrase);
|
||||
const password = isReady()
|
||||
? pbkdf2(u8a, salt, rounds)
|
||||
: bufferToU8a(pbkdf2Sync(u8aToBuffer(u8a), u8aToBuffer(salt), rounds, 64));
|
||||
export function pbkdf2Encode (passphrase?: Buffer | Uint8Array | string, salt: Buffer | Uint8Array = randomAsU8a(), rounds = 2048, onlyJs = false): Result {
|
||||
const u8aPass = u8aToU8a(passphrase);
|
||||
const u8aSalt = u8aToU8a(salt);
|
||||
const password = isReady() && !onlyJs
|
||||
? pbkdf2(u8aPass, u8aSalt, rounds)
|
||||
: bufferToU8a(pbkdf2Sync(u8aToBuffer(u8aPass), u8aToBuffer(u8aSalt), rounds, 64, 'sha512'));
|
||||
|
||||
return { password, rounds, salt };
|
||||
}
|
||||
|
||||
@@ -1871,6 +1871,7 @@ __metadata:
|
||||
"@types/elliptic": ^6.4.12
|
||||
"@types/pbkdf2": ^3.1.0
|
||||
"@types/scryptsy": ^2.0.0
|
||||
"@types/sha.js": ^2.4.0
|
||||
"@types/xxhashjs": ^0.2.2
|
||||
base-x: ^3.0.8
|
||||
blakejs: ^1.1.0
|
||||
@@ -1880,6 +1881,7 @@ __metadata:
|
||||
js-sha3: ^0.8.0
|
||||
pbkdf2: ^3.1.1
|
||||
scryptsy: ^2.1.0
|
||||
sha.js: ^2.4.11
|
||||
tweetnacl: ^1.0.3
|
||||
xxhashjs: ^0.2.2
|
||||
peerDependencies:
|
||||
@@ -2249,6 +2251,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/sha.js@npm:^2.4.0":
|
||||
version: 2.4.0
|
||||
resolution: "@types/sha.js@npm:2.4.0"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: d23139fb0c9ad65c884b057728b19944399e167fc2a05a22931760d3541c861af7c3c274de11ecc26d3e340f38c249e6d3b0359d249fb674bb702c3232c2d53f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/stack-utils@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@types/stack-utils@npm:2.0.0"
|
||||
@@ -9266,7 +9277,7 @@ fsevents@^1.2.7:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8":
|
||||
"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8":
|
||||
version: 2.4.11
|
||||
resolution: "sha.js@npm:2.4.11"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user