diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index b8174f11b..fe57fa3c9 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -29,7 +29,6 @@ import { getDefaultAmountPreference, splitAmount } from './utils.js'; -import { deriveBlindingFactor, deriveSecret, deriveSeedFromMnemonic } from './secrets.js'; import { validateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { hashToCurve, pointFromHex } from '@cashu/crypto/modules/common'; @@ -38,6 +37,11 @@ import { constructProofFromPromise, serializeProof } from '@cashu/crypto/modules/client'; +import { + deriveBlindingFactor, + deriveSecret, + deriveSeedFromMnemonic +} from '@cashu/crypto/modules/client/NUT09'; import { createP2PKsecret, getSignedProofs } from '@cashu/crypto/modules/client/NUT11'; import { type Proof as NUT11Proof } from '@cashu/crypto/modules/common/index'; diff --git a/src/secrets.ts b/src/secrets.ts deleted file mode 100644 index 48e4fbb69..000000000 --- a/src/secrets.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { HDKey } from '@scure/bip32'; -import { generateMnemonic, 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); - if (derived.privateKey === null) { - throw new Error('Could not derive private key'); - } - return derived.privateKey; -}; - -const getKeysetIdInt = (keysetId: string): bigint => { - let keysetIdInt: bigint; - if (/^[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; -}; diff --git a/test/secrets.test.ts b/test/secrets.test.ts deleted file mode 100644 index 34b4f1a1c..000000000 --- a/test/secrets.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { bytesToHex } from '@noble/curves/abstract/utils'; -import { deriveSeedFromMnemonic } from '../src/secrets'; -import { deriveBlindingFactor, deriveSecret } from '../src/secrets'; -import { HDKey } from '@scure/bip32'; - -const mnemonic = 'half depart obvious quality work element tank gorilla view sugar picture humble'; -const seed = deriveSeedFromMnemonic(mnemonic); - -describe('testing hdkey from seed', () => { - test('hdkey from seed', async () => { - const hdkey = HDKey.fromMasterSeed(seed); - expect(hdkey).not.toBeNull(); - }); - - test('hdkey to uint8array', async () => { - const hdkey = HDKey.fromMasterSeed(seed); - const privateKey = hdkey.privateKey; - expect(privateKey).not.toBeNull(); - - const seed_expected = - 'dd44ee516b0647e80b488e8dcc56d736a148f15276bef588b37057476d4b2b25780d3688a32b37353d6995997842c0fd8b412475c891c16310471fbc86dcbda8'; - const seed_uint8_array_expected = Uint8Array.from(Buffer.from(seed_expected, 'hex')); - expect(seed).toEqual(seed_uint8_array_expected); - }); -}); - -describe('testing deterministic secrets', () => { - const secrets = [ - '485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae', - '8f2b39e8e594a4056eb1e6dbb4b0c38ef13b1b2c751f64f810ec04ee35b77270', - 'bc628c79accd2364fd31511216a0fab62afd4a18ff77a20deded7b858c9860c8', - '59284fd1650ea9fa17db2b3acf59ecd0f2d52ec3261dd4152785813ff27a33bf', - '576c23393a8b31cc8da6688d9c9a96394ec74b40fdaf1f693a6bb84284334ea0' - ]; - test('derive Secret', async () => { - const secret1 = deriveSecret(seed, '009a1f293253e41e', 0); - const secret2 = deriveSecret(seed, '009a1f293253e41e', 1); - const secret3 = deriveSecret(seed, '009a1f293253e41e', 2); - const secret4 = deriveSecret(seed, '009a1f293253e41e', 3); - const secret5 = deriveSecret(seed, '009a1f293253e41e', 4); - - const bf1 = deriveBlindingFactor(seed, '009a1f293253e41e', 0); - const bf2 = deriveBlindingFactor(seed, '009a1f293253e41e', 1); - const bf3 = deriveBlindingFactor(seed, '009a1f293253e41e', 2); - const bf4 = deriveBlindingFactor(seed, '009a1f293253e41e', 3); - const bf5 = deriveBlindingFactor(seed, '009a1f293253e41e', 4); - - expect(bytesToHex(secret1)).toBe(secrets[0]); - expect(bytesToHex(secret2)).toBe(secrets[1]); - expect(bytesToHex(secret3)).toBe(secrets[2]); - expect(bytesToHex(secret4)).toBe(secrets[3]); - expect(bytesToHex(secret5)).toBe(secrets[4]); - }); -}); - -describe('testing deterministic blindedMessage', () => { - const secrets = ['485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae']; - test('derive Secret', async () => { - const secret1 = deriveSecret(seed, '009a1f293253e41e', 0); - - const bf1 = deriveBlindingFactor(seed, '009a1f293253e41e', 0); - - expect(bytesToHex(secret1)).toBe(secrets[0]); - - // blindMessage() - }); -}); - -describe('test private key derivation from derivation path', () => { - const seed = - 'dd44ee516b0647e80b488e8dcc56d736a148f15276bef588b37057476d4b2b25780d3688a32b37353d6995997842c0fd8b412475c891c16310471fbc86dcbda8'; - const seed_uint8_array = Uint8Array.from(Buffer.from(seed, 'hex')); - const hdkey = HDKey.fromMasterSeed(seed_uint8_array); - const expected_privatekey = '9d32fc57e6fa2942d05ee475d28ba6a56839b8cb8a3f174b05ed0ed9d3a420f6'; - const derivation_path = "m/129372'/0'/2004500376'/0'/0"; - const derived = hdkey.derive(derivation_path); - test('derive Secret', async () => { - expect(derived.privateKey).not.toBeNull(); - const privateKey = derived.privateKey || new Uint8Array(); - expect(bytesToHex(privateKey)).toBe(expected_privatekey); - }); -});