Cleanup pbkdf2 use (#757)

This commit is contained in:
Jaco Greeff
2020-11-08 20:52:02 +01:00
committed by GitHub
parent 3a57c7da69
commit 68bcce7a27
11 changed files with 97 additions and 56 deletions
+2
View File
@@ -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": {
+5 -5
View File
@@ -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'
);
});
});
});
+6 -5
View File
@@ -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 };
}
+12 -1
View File
@@ -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: