All files / src secrets.ts

86.48% Statements 32/37
60% Branches 3/5
71.42% Functions 5/7
84.84% Lines 28/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 633x 3x 3x 3x 3x 3x   3x   3x 3x 3x     3x         3x 1x 1x     3x 5x     3x               3x           5x 5x 5x 5x 5x     5x     3x   5x       5x   5x    
import { HDKey } from '@scure/bip32';
import { generateMnemonic, validateMnemonic, mnemonicToSeedSync } from '@scure/bip39';
import { wordlist } from '@scure/bip39/wordlists/english';
import { encodeBase64toUint8 } from './base64';
import { bytesToNumber } from './utils';
import { hexToNumber } from '@noble/curves/abstract/utils';
 
const STANDARD_DERIVATION_PATH = `m/129372'/0'`;
 
enum DerivationType {
	SECRET = 0,
	BLINDING_FACTOR = 1
}
 
export const generateNewMnemonic = (): string => {
	const mnemonic = generateMnemonic(wordlist, 128);
	return mnemonic;
};
 
export const deriveSeedFromMnemonic = (mnemonic: string): Uint8Array => {
	const seed = mnemonicToSeedSync(mnemonic);
	return seed;
};
 
export const deriveSecret = (seed: Uint8Array, keysetId: string, counter: number): Uint8Array => {
	return derive(seed, keysetId, counter, DerivationType.SECRET);
};
 
export const deriveBlindingFactor = (
	seed: Uint8Array,
	keysetId: string,
	counter: number
): Uint8Array => {
	return derive(seed, keysetId, counter, DerivationType.BLINDING_FACTOR);
};
 
const derive = (
	seed: Uint8Array,
	keysetId: string,
	counter: number,
	secretOrBlinding: DerivationType
): Uint8Array => {
	const hdkey = HDKey.fromMasterSeed(seed);
	const keysetIdInt = getKeysetIdInt(keysetId);
	const derivationPath = `${STANDARD_DERIVATION_PATH}/${keysetIdInt}'/${counter}'/${secretOrBlinding}`;
	const derived = hdkey.derive(derivationPath);
	Iif (derived.privateKey === null) {
		throw new Error('Could not derive private key');
	}
	return derived.privateKey;
};
 
const getKeysetIdInt = (keysetId: string): bigint => {
	let keysetIdInt: bigint;
	Iif (/^[a-fA-F0-9]+$/.test(keysetId)) {
		keysetIdInt = hexToNumber(keysetId) % BigInt(2 ** 31 - 1);
	} else {
		//legacy keyset compatibility
		keysetIdInt = bytesToNumber(encodeBase64toUint8(keysetId)) % BigInt(2 ** 31 - 1);
	}
	return keysetIdInt;
};