Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/keyring/src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ describe('keypair', (): void => {
});

it('creates with dev phrase with derivation path specified', (): void => {
const pair = keyring.createFromUri(PHRASE, undefined, undefined, undefined, 2048);
const pair = keyring.createFromUri(PHRASE);

expect(
pair.address
Expand All @@ -367,20 +367,20 @@ describe('keypair', (): void => {

it('creates with dev phrase with derivation path specified - addFromUri', (): void => {
expect(
keyring.addFromUri(PHRASE, undefined, undefined, undefined, 2048).address
keyring.addFromUri(PHRASE).address
).toEqual(ETH_ADDRESS_ONE);
});

it('creates with dev phrase with derivation path specified - addFromUri with type', (): void => {
const keyringUntyped = new Keyring();

expect(
keyringUntyped.addFromUri(PHRASE, {}, 'ethereum', undefined, 2048).address
keyringUntyped.addFromUri(PHRASE, {}, 'ethereum').address
).toEqual(ETH_ADDRESS_ONE);
});

it('encodes a pair toJSON (and decodes)', (): void => {
const pair = keyring.createFromUri(PHRASE, undefined, undefined, undefined, 2048);
const pair = keyring.createFromUri(PHRASE);
const json = pair.toJson('password');

expect(json.address).toEqual('0x0381351b1b46d2602b0992bb5d5531f9c1696b0812feb2534b6884adc47e2e1d8b'); // this is the public key (different from address for ethereum)
Expand All @@ -399,7 +399,7 @@ describe('keypair', (): void => {
});

it('encodes a pair toJSON and back', (): void => {
const pairOriginal = keyring.createFromUri(PHRASE, undefined, undefined, undefined, 2048);
const pairOriginal = keyring.createFromUri(PHRASE);
const json = pairOriginal.toJson('password');
const pair = keyring.addFromJson(
json
Expand Down
23 changes: 6 additions & 17 deletions packages/keyring/src/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,10 @@ export class Keyring implements KeyringInstance {
* @name addFromUri
* @summary Creates an account via an suri
* @description Extracts the phrase, path and password from a SURI format for specifying secret keys `<secret>/<soft-key>//<hard-key>///<password>` (the `///password` may be omitted, and `/<soft-key>` and `//<hard-key>` maybe repeated and mixed). The secret can be a hex string, mnemonic phrase or a string (to be padded)
* @param _suri
* @param meta
* @param type The supported types of pairs. Either 'ed25519' | 'sr25519' | 'ecdsa' | 'ethereum'.
* @param wordlist - Optional custom wordlist for mnemonic.
* @param rounds - Optional: Number of PBKDF2 iterations to run (default: 210000 (when onlyJS = true) or 2048 (when onlyJS = false).
*/
public addFromUri (suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, wordlist?: string[], rounds?: number): KeyringPair {
*/
public addFromUri (suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, wordlist?: string[]): KeyringPair {
return this.addPair(
this.createFromUri(suri, meta, type, wordlist, rounds)
this.createFromUri(suri, meta, type, wordlist)
);
}

Expand Down Expand Up @@ -207,14 +202,8 @@ export class Keyring implements KeyringInstance {
* @name createFromUri
* @summary Creates a Keypair from an suri
* @description This creates a pair from the suri, but does not add it to the keyring
*
* @param _suri
* @param meta
* @param type The supported types of pairs. Either 'ed25519' | 'sr25519' | 'ecdsa' | 'ethereum'.
* @param wordlist - Optional custom wordlist for mnemonic.
* @param rounds - Optional: Number of PBKDF2 iterations to run (default: 210000 (when onlyJS = true) or 2048 (when onlyJS = false).
*/
public createFromUri (_suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, wordlist?: string[], rounds?: number): KeyringPair {
public createFromUri (_suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type, wordlist?: string[]): KeyringPair {
// here we only aut-add the dev phrase if we have a hard-derived path
const suri = _suri.startsWith('//')
? `${DEV_PHRASE}${_suri}`
Expand All @@ -230,8 +219,8 @@ export class Keyring implements KeyringInstance {

if ([12, 15, 18, 21, 24].includes(parts.length)) {
seed = type === 'ethereum'
? mnemonicToLegacySeed(phrase, '', false, 64, rounds)
: mnemonicToMiniSecret(phrase, password, wordlist, false, rounds);
? mnemonicToLegacySeed(phrase, '', false, 64)
: mnemonicToMiniSecret(phrase, password, wordlist);
} else {
if (phrase.length > 32) {
throw new Error('specified phrase is not a valid mnemonic and is invalid as a raw seed at > 32 bytes');
Expand Down
2 changes: 1 addition & 1 deletion packages/keyring/src/suri.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('keyring.addFromUri', (): void => {
describe(`${type}`, (): void => {
tests.forEach(({ pk, ss, uri }): void => {
it(`creates ${uri}`, (): void => {
const pair = keyring.addFromUri(uri, {}, type as KeypairType, undefined, 2048);
const pair = keyring.addFromUri(uri, {}, type as KeypairType);

expect(u8aToHex(pair.publicKey)).toEqual(pk);
expect(pair.address).toEqual(ss);
Expand Down
4 changes: 2 additions & 2 deletions packages/util-crypto/src/hd/ethereum/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ describe('hdEthereum', (): void => {
]);

it('derives the right key pair from a mnemonic', (): void => {
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64, 2048));
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64));

expect(key.publicKey).toEqual(PUBLIC);
expect(key.secretKey).toEqual(SECRET);
});

it('derives the right key pair from a mnemonic and a derivation path', (): void => {
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64, 2048), derivationPath);
const key = hdEthereum(mnemonicToLegacySeed(PHRASE, '', false, 64), derivationPath);

expect(key.publicKey).toEqual(PUBLICDERIVED);
expect(key.secretKey).toEqual(SECRETDERIVED);
Expand Down
2 changes: 1 addition & 1 deletion packages/util-crypto/src/hd/ledger/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('ledgerDerive', (): void => {
tests.forEach(({ ed25519, index: [account, address], mnemonic }, index): void => {
it(`derives a known ed25519 seed for ${network} (${index})`, (): void => {
expect(u8aToHex(
hdLedger(mnemonic, `m/44'/${slip44}'/${account}'/0'/${address}'`, 2048)
hdLedger(mnemonic, `m/44'/${slip44}'/${account}'/0'/${address}'`)
.secretKey
.slice(0, 32)
)).toEqual(ed25519);
Expand Down
4 changes: 2 additions & 2 deletions packages/util-crypto/src/hd/ledger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { HARDENED, hdValidatePath } from '../validatePath.js';
import { ledgerDerivePrivate } from './derivePrivate.js';
import { ledgerMaster } from './master.js';

export function hdLedger (_mnemonic: string, path: string, rounds?: number): Keypair {
export function hdLedger (_mnemonic: string, path: string): Keypair {
const words = _mnemonic
.split(' ')
.map((s) => s.trim())
Expand All @@ -30,7 +30,7 @@ export function hdLedger (_mnemonic: string, path: string, rounds?: number): Key
}

const parts = path.split('/').slice(1);
let seed = ledgerMaster(mnemonic, password, rounds);
let seed = ledgerMaster(mnemonic, password);

for (const p of parts) {
const n = parseInt(p.replace(/'$/, ''), 10);
Expand Down
2 changes: 1 addition & 1 deletion packages/util-crypto/src/hd/ledger/master.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const XPRV = '0x402b03cd9c8bed9ba9f9bd6cd9c315ce9fcc59c7c25d37c85a36096617e69d41
describe('ledgerDerive', (): void => {
it('derives a known master xprv', (): void => {
expect(u8aToHex(
ledgerMaster(MNEMONIC, undefined, 2048)
ledgerMaster(MNEMONIC)
)).toEqual(XPRV);
});
});
14 changes: 3 additions & 11 deletions packages/util-crypto/src/hd/ledger/master.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,9 @@ import { mnemonicToSeedSync } from '../../mnemonic/bip39.js';

const ED25519_CRYPTO = 'ed25519 seed';

/**
* Gets an xprv from a mnemonic
*
* @param mnemonic - The BIP-39 mnemonic phrase to derive the secret from.
* @param password - Optional: password to secure the seed (default: empty string).
* @param wordlist - Optional custom wordlist for mnemonic.
* @param onlyJs - Optional: If `true`, forces use of the JavaScript implementation instead of WASM.
* @param rounds - Optional: Number of PBKDF2 iterations to run (default: 210000).
*/
export function ledgerMaster (mnemonic: string, password?: string, rounds?: number): Uint8Array {
const seed = mnemonicToSeedSync(mnemonic, password, rounds);
// gets an xprv from a mnemonic
export function ledgerMaster (mnemonic: string, password?: string): Uint8Array {
const seed = mnemonicToSeedSync(mnemonic, password);
const chainCode = hmacShaAsU8a(ED25519_CRYPTO, new Uint8Array([1, ...seed]), 256);
let priv;

Expand Down
10 changes: 2 additions & 8 deletions packages/util-crypto/src/mnemonic/bip39.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,10 @@ function deriveChecksumBits (entropyBuffer: Uint8Array): string {
).slice(0, (entropyBuffer.length * 8) / 32);
}

/*
* @param mnemonic - The BIP-39 mnemonic phrase to derive the secret from.
* @param password - Optional: password to secure the seed (default: empty string).
* @param rounds - Optional: Number of PBKDF2 iterations to run (default: 210000).
*/
export function mnemonicToSeedSync (mnemonic: string, password?: string, rounds?: number): Uint8Array {
export function mnemonicToSeedSync (mnemonic: string, password?: string): Uint8Array {
return pbkdf2Encode(
stringToU8a(normalize(mnemonic)),
stringToU8a(`mnemonic${normalize(password)}`),
rounds
stringToU8a(`mnemonic${normalize(password)}`)
).password;
}

Expand Down
10 changes: 5 additions & 5 deletions packages/util-crypto/src/mnemonic/toLegacySeed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ describe('mnemonicToLegacySeed', (): void => {
it(`generates Wasm & Js equivalents for password=${password || 'undefined'}`, (): void => {
expect(
u8aEq(
mnemonicToLegacySeed(MNEMONIC, password, true, 32, 2048),
mnemonicToLegacySeed(MNEMONIC, password, false, 32, 2048)
mnemonicToLegacySeed(MNEMONIC, password, true),
mnemonicToLegacySeed(MNEMONIC, password, false)
)
).toEqual(true);
});
Expand All @@ -32,19 +32,19 @@ describe('mnemonicToLegacySeed', (): void => {
describe(`onlyJs=${(onlyJs && 'true') || 'false'}`, (): void => {
it('generates a valid 64bytes seed', (): void => {
expect(
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs, 64, 2048))
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs, 64))
).toEqual(SEED_64);
});

it('generates a valid 32bytes seed', (): void => {
expect(
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs, 32, 2048))
u8aToHex(mnemonicToLegacySeed(MNEMONIC, undefined, onlyJs))
).toEqual(SEED_32);
});

it('fails with non-mnemonics', (): void => {
expect(
() => mnemonicToLegacySeed('foo bar baz', undefined, onlyJs, 32, 2048)
() => mnemonicToLegacySeed('foo bar baz', undefined, onlyJs)
).toThrow(/mnemonic specified/);
});
});
Expand Down
14 changes: 4 additions & 10 deletions packages/util-crypto/src/mnemonic/toLegacySeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,8 @@ import { mnemonicValidate } from './validate.js';
* console.log(`Seed generated from mnemonic: ${mnemonicToLegacySeed(mnemonic)}`); => u8a
* }
* ```
*
* @param mnemonic - The BIP-39 mnemonic phrase to derive the secret from.
* @param password - Optional: password to secure the seed (default: empty string).
* @param onlyJs - Optional: If `true`, forces use of the JavaScript implementation instead of WASM.
* @param byteLength - Optional: Either 32 or 64. Default is 32
* @param rounds - Optional: Number of PBKDF2 iterations to run (default: 210000).
*/
export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs?: boolean, byteLength: 32 | 64 = 32, rounds?: number): Uint8Array {
*/
export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs?: boolean, byteLength: 32 | 64 = 32): Uint8Array {
if (!mnemonicValidate(mnemonic)) {
throw new Error('Invalid bip39 mnemonic specified');
} else if (![32, 64].includes(byteLength)) {
Expand All @@ -40,6 +34,6 @@ export function mnemonicToLegacySeed (mnemonic: string, password = '', onlyJs?:
return byteLength === 32
? !hasBigInt || (!onlyJs && isReady())
? bip39ToSeed(mnemonic, password)
: mnemonicToSeedSync(mnemonic, password, rounds).subarray(0, 32)
: mnemonicToSeedSync(mnemonic, password, rounds);
: mnemonicToSeedSync(mnemonic, password).subarray(0, 32)
: mnemonicToSeedSync(mnemonic, password);
}
14 changes: 7 additions & 7 deletions packages/util-crypto/src/mnemonic/toMiniSecret.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ describe('mnemonicToMiniSecret', (): void => {
it(`generates Wasm & Js equivalents for password=${password || 'undefined'}`, (): void => {
expect(
u8aEq(
mnemonicToMiniSecret(MNEMONIC, password, undefined, true, 2048),
mnemonicToMiniSecret(MNEMONIC, password, undefined, false, 2048)
mnemonicToMiniSecret(MNEMONIC, password, undefined, true),
mnemonicToMiniSecret(MNEMONIC, password, undefined, false)
)
).toEqual(true);
});
Expand All @@ -31,31 +31,31 @@ describe('mnemonicToMiniSecret', (): void => {
const mnemonic = '엉덩이 능동적 숫자 팩시밀리 비난 서적 파출소 도움 독창적 인생 상류 먼지 답변 음반 수박 사업 노란색 공사 우체국 특급 도대체 금지 굉장히 고무신';

expect(
() => mnemonicToMiniSecret(mnemonic, 'testing', undefined, false, 2048)
() => mnemonicToMiniSecret(mnemonic, 'testing')
).toThrow();
expect(
u8aToHex(mnemonicToMiniSecret(mnemonic, 'testing', koreanWords, false, 2048))
u8aToHex(mnemonicToMiniSecret(mnemonic, 'testing', koreanWords))
).toEqual('0xefa278a62535581767a2f49cb542ed91b65fb911e1b05e7a09c702b257f10c13');
});

for (const onlyJs of [false, true]) {
describe(`onlyJs=${(onlyJs && 'true') || 'false'}`, (): void => {
it('generates a valid seed', (): void => {
expect(
u8aToHex(mnemonicToMiniSecret(MNEMONIC, undefined, undefined, onlyJs, 2048))
u8aToHex(mnemonicToMiniSecret(MNEMONIC, undefined, undefined, onlyJs))
).toEqual(SEED);
});

it('fails with non-mnemonics', (): void => {
expect(
() => mnemonicToMiniSecret('foo bar baz', undefined, undefined, onlyJs, 2048)
() => mnemonicToMiniSecret('foo bar baz', undefined, undefined, onlyJs)
).toThrow(/mnemonic specified/);
});

tests.forEach(([mnemonic, , seed], index): void => {
it(`Created correct seed for ${index}`, (): void => {
expect(
u8aToHex(mnemonicToMiniSecret(mnemonic, 'Substrate', undefined, onlyJs, 2048))
u8aToHex(mnemonicToMiniSecret(mnemonic, 'Substrate', undefined, onlyJs))
).toEqual(
// mini returned here, only check first 32-bytes (64 hex + 2 prefix)
seed.substring(0, 66)
Expand Down
11 changes: 2 additions & 9 deletions packages/util-crypto/src/mnemonic/toMiniSecret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,7 @@ import { pbkdf2Encode } from '../pbkdf2/index.js';
import { mnemonicToEntropy } from './toEntropy.js';
import { mnemonicValidate } from './validate.js';

/**
* @param mnemonic - The BIP-39 mnemonic phrase to derive the secret from.
* @param password - Optional: password to secure the seed (default: empty string).
* @param wordlist - Optional custom wordlist for mnemonic.
* @param onlyJs - Optional: If `true`, forces use of the JavaScript implementation instead of WASM.
* @param rounds - Optional: Number of PBKDF2 iterations to run (default: 210000 (when onlyJS = true) or 2048 (when onlyJS = false).
*/
export function mnemonicToMiniSecret (mnemonic: string, password = '', wordlist?: string[], onlyJs?: boolean, rounds?: number): Uint8Array {
export function mnemonicToMiniSecret (mnemonic: string, password = '', wordlist?: string[], onlyJs?: boolean): Uint8Array {
if (!mnemonicValidate(mnemonic, wordlist, onlyJs)) {
throw new Error('Invalid bip39 mnemonic specified');
} else if (!wordlist && !onlyJs && isReady()) {
Expand All @@ -26,5 +19,5 @@ export function mnemonicToMiniSecret (mnemonic: string, password = '', wordlist?
const salt = stringToU8a(`mnemonic${password}`);

// return the first 32 bytes as the seed
return pbkdf2Encode(entropy, salt, rounds).password.slice(0, 32);
return pbkdf2Encode(entropy, salt).password.slice(0, 32);
}
2 changes: 1 addition & 1 deletion packages/util-crypto/src/pbkdf2/encode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ describe('pbkdf2Encode', (): void => {
}

perfWasm('pbkdf2Encode', 8, (input, onlyJs) =>
pbkdf2Encode(input, input, 2048, onlyJs)
pbkdf2Encode(input, input, undefined, onlyJs)
);
});
9 changes: 1 addition & 8 deletions packages/util-crypto/src/pbkdf2/encode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,7 @@ interface Result {
salt: Uint8Array;
}

export function pbkdf2Encode (passphrase?: string | Uint8Array, salt: Uint8Array = randomAsU8a(), rounds = 210000, onlyJs?: boolean): Result {
// When using the JS implementation (pbkdf2Js), large iteration counts can
// cause excessive computation time or memory usage. In that case, we cap
// the number of rounds to 2048 to ensure reliable performance.
if (onlyJs && rounds > 2048) {
rounds = 2048;
}

export function pbkdf2Encode (passphrase?: string | Uint8Array, salt: Uint8Array = randomAsU8a(), rounds = 2048, onlyJs?: boolean): Result {
const u8aPass = u8aToU8a(passphrase);
const u8aSalt = u8aToU8a(salt);

Expand Down