diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/addressVerification.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/addressVerification.ts index 679d9aaa2d..4570364d12 100644 --- a/modules/sdk-core/test/unit/bitgo/utils/tss/addressVerification.ts +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/addressVerification.ts @@ -184,24 +184,6 @@ describe('verifyEddsaTssWalletAddress', function () { result.should.be.false(); }); }); - - describe('legacy Silence Labs formula', function () { - it('rejects an address derived with the old Silence Labs single-HMAC formula', async function () { - const verifyEddsaTssWalletAddress = getVerifyEddsaTssWalletAddress(); - // The Silence Labs and BIP32-Ed25519 formulas produce different addresses for the same keychain. - // Addresses derived with the old formula no longer verify against the current derivation. - const { deriveUnhardenedMps: deriveSL } = await import('@bitgo/sdk-lib-mpc'); - const silenceLabsAddress = deriveSL(TEST_KEYCHAIN, 'm/0').slice(0, 64); - - const result = await verifyEddsaTssWalletAddress( - { address: silenceLabsAddress, keychains, index: 0 }, - isValidAddress, - getAddressFromPublicKey - ); - - result.should.be.false(); - }); - }); }); describe('verifyMPCWalletAddress - ECDSA (secp256k1)', function () { diff --git a/modules/sdk-lib-mpc/src/tss/eddsa-mps/derive.ts b/modules/sdk-lib-mpc/src/tss/eddsa-mps/derive.ts deleted file mode 100644 index f15dc83b3f..0000000000 --- a/modules/sdk-lib-mpc/src/tss/eddsa-mps/derive.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { createHmac } from 'crypto'; -import { ed25519 } from '@noble/curves/ed25519'; -import { pathToIndices } from '../../curves/util'; - -/** - * @deprecated Use `Eddsa.deriveUnhardened` instead. wasm-mps >=1.9.0 uses - * standard BIP32-Ed25519 (the Cardano formula) for DSG path derivation. - */ -export function deriveUnhardenedMps(commonKeychainHex: string, path: string): string { - if (commonKeychainHex.length !== 128) { - throw new Error( - `Invalid commonKeychain: expected 128 hex chars (32-byte pk + 32-byte chaincode), got ${commonKeychainHex.length}` - ); - } - - const buf = Buffer.from(commonKeychainHex, 'hex'); - let pkBytes = Buffer.from(buf.subarray(0, 32)); - let ccBytes = Buffer.from(buf.subarray(32, 64)); - - const indices = path === '' || path === 'm' ? [] : pathToIndices(path); - for (const index of indices) { - const indexBuf = Buffer.alloc(4); - indexBuf.writeUInt32BE(index, 0); - - const hmac = createHmac('sha512', ccBytes) - .update(Buffer.concat([pkBytes, indexBuf])) - .digest(); - - const zl = hmac.subarray(0, 32); - const zr = hmac.subarray(32); - - // parse_offset: 8 * LE(zl[0..28] || zeroes) - // Mirrors Rust: U256::from_le_slice(&z_l).shl(3), where z_l[28..32] = 0. - const truncZl = Buffer.alloc(32); - zl.copy(truncZl, 0, 0, 28); - const offset = BigInt('0x' + Buffer.from(truncZl).reverse().toString('hex')) * 8n; - - // child_pk = offset * G + parent_pk - const childPoint = ed25519.ExtendedPoint.BASE.multiply(offset).add(ed25519.ExtendedPoint.fromHex(pkBytes)); - pkBytes = Buffer.from(childPoint.toRawBytes()); - ccBytes = Buffer.from(zr); - } - - return pkBytes.toString('hex') + ccBytes.toString('hex'); -} diff --git a/modules/sdk-lib-mpc/src/tss/eddsa-mps/index.ts b/modules/sdk-lib-mpc/src/tss/eddsa-mps/index.ts index dee6ca0393..cc355458a4 100644 --- a/modules/sdk-lib-mpc/src/tss/eddsa-mps/index.ts +++ b/modules/sdk-lib-mpc/src/tss/eddsa-mps/index.ts @@ -3,4 +3,3 @@ export * as EddsaMPSDsg from './dsg'; export * as MPSUtil from './util'; export * as MPSTypes from './types'; export * as MPSComms from './commsLayer'; -export { deriveUnhardenedMps } from './derive'; diff --git a/modules/sdk-lib-mpc/test/unit/tss/eddsa/derive.ts b/modules/sdk-lib-mpc/test/unit/tss/eddsa/derive.ts deleted file mode 100644 index dc8c17a59b..0000000000 --- a/modules/sdk-lib-mpc/test/unit/tss/eddsa/derive.ts +++ /dev/null @@ -1,119 +0,0 @@ -import assert from 'assert'; -import { ed25519 } from '@noble/curves/ed25519'; -import { EddsaMPSDsg, MPSUtil } from '../../../../src/tss/eddsa-mps'; -import { deriveUnhardenedMps } from '../../../../src/tss/eddsa-mps/derive'; -import { Ed25519Bip32HdTree } from '../../../../src/curves/ed25519Bip32HdTree'; -import { bigIntFromBufferBE, bigIntFromBufferLE, bigIntToBufferLE } from '../../../../src/util'; -import { generateEdDsaDKGKeyShares } from './util'; - -const MESSAGE = Buffer.from('The Times 03/Jan/2009 Chancellor on brink of second bailout for banks'); - -describe('deriveUnhardenedMps', function () { - this.timeout(60_000); - - // DKG is expensive; run once and reuse across tests. - let commonKeychain: string; - let rootPubKey: Buffer; - let userKeyShare: Buffer; - let bitgoKeyShare: Buffer; - let hdTree: Ed25519Bip32HdTree; - - before(async function () { - const [userDkg, , bitgoDkg] = await generateEdDsaDKGKeyShares(); - commonKeychain = userDkg.getCommonKeychain(); - rootPubKey = userDkg.getSharePublicKey(); - userKeyShare = userDkg.getKeyShare(); - bitgoKeyShare = bitgoDkg.getKeyShare(); - hdTree = await Ed25519Bip32HdTree.initialize(); - }); - - // Local mock of Eddsa.deriveUnhardened (BIP32-Ed25519 dual-HMAC formula). - function deriveUnhardened(keychain: string, path: string): Buffer { - const buf = Buffer.from(keychain, 'hex'); - const derived = hdTree.publicDerive( - { pk: bigIntFromBufferLE(buf.slice(0, 32)), chaincode: bigIntFromBufferBE(buf.slice(32, 64)) }, - path - ); - return bigIntToBufferLE(derived.pk, 32); - } - - describe('input validation', function () { - it('throws when commonKeychainHex is shorter than 128 chars', function () { - assert.throws(() => deriveUnhardenedMps('deadbeef', 'm'), /expected 128 hex chars/); - }); - - it('throws when commonKeychainHex is longer than 128 chars', function () { - assert.throws(() => deriveUnhardenedMps('a'.repeat(130), 'm'), /expected 128 hex chars/); - }); - }); - - describe('derivation correctness using existing deriveUnhardened path parsing', function () { - it('returns the root key unchanged for path "m"', function () { - const result = deriveUnhardenedMps(commonKeychain, 'm'); - assert.strictEqual(result, commonKeychain, 'Path "m" should return the keychain unchanged'); - }); - - it('produces a different key at m/0 than at the root', function () { - const derived = deriveUnhardenedMps(commonKeychain, 'm/0'); - assert.notStrictEqual(derived.slice(0, 64), commonKeychain.slice(0, 64)); - }); - - it('is deterministic — same inputs produce the same output', function () { - const a = deriveUnhardenedMps(commonKeychain, 'm/0/1'); - const b = deriveUnhardenedMps(commonKeychain, 'm/0/1'); - assert.strictEqual(a, b); - }); - - it('produces different keys for different paths', function () { - const d0 = deriveUnhardenedMps(commonKeychain, 'm/0'); - const d1 = deriveUnhardenedMps(commonKeychain, 'm/1'); - assert.notStrictEqual(d0.slice(0, 64), d1.slice(0, 64)); - }); - - it('output is always 128 hex chars', function () { - assert.strictEqual(deriveUnhardenedMps(commonKeychain, 'm').length, 128); - assert.strictEqual(deriveUnhardenedMps(commonKeychain, 'm/0').length, 128); - assert.strictEqual(deriveUnhardenedMps(commonKeychain, 'm/0/1').length, 128); - }); - }); - - describe('DSG signature cross-check against Ed25519Bip32HdTree (deriveUnhardened)', function () { - let sigAtRoot: Buffer; - let sigAtM0: Buffer; - let sigAtM01: Buffer; - - before(async function () { - const dsgA1 = new EddsaMPSDsg.DSG(0); - await MPSUtil.executeTillRound(3, dsgA1, new EddsaMPSDsg.DSG(2), userKeyShare, bitgoKeyShare, MESSAGE, 'm'); - sigAtRoot = dsgA1.getSignature(); - - const dsgA2 = new EddsaMPSDsg.DSG(0); - await MPSUtil.executeTillRound(3, dsgA2, new EddsaMPSDsg.DSG(2), userKeyShare, bitgoKeyShare, MESSAGE, 'm/0'); - sigAtM0 = dsgA2.getSignature(); - - const dsgA3 = new EddsaMPSDsg.DSG(0); - await MPSUtil.executeTillRound(3, dsgA3, new EddsaMPSDsg.DSG(2), userKeyShare, bitgoKeyShare, MESSAGE, 'm/0/1'); - sigAtM01 = dsgA3.getSignature(); - }); - - it('signature from DSG at "m" verifies against the root public key', function () { - assert(ed25519.verify(sigAtRoot, MESSAGE, rootPubKey), 'DSG at "m" should verify against the raw DKG public key'); - }); - - it('signature from DSG at "m/0" verifies against Eddsa.deriveUnhardened key at "m/0"', function () { - const derivedPk = deriveUnhardened(commonKeychain, 'm/0'); - assert( - ed25519.verify(sigAtM0, MESSAGE, derivedPk), - 'DSG at "m/0" should verify against Eddsa.deriveUnhardened result at "m/0"' - ); - }); - - it('signature from DSG at "m/0/1" verifies against Eddsa.deriveUnhardened key at "m/0/1"', function () { - const derivedPk = deriveUnhardened(commonKeychain, 'm/0/1'); - assert( - ed25519.verify(sigAtM01, MESSAGE, derivedPk), - 'DSG at "m/0/1" should verify against Eddsa.deriveUnhardened result at "m/0/1"' - ); - }); - }); -});