Skip to content

Commit 2f7d1ac

Browse files
Marzooqabitgobot
authored andcommitted
chore(sdk-lib-mpc): bump @bitgo/wasm-mps to 1.10.0
Bump @bitgo/wasm-mps from 1.8.1 to 1.10.0. wasm-mps 1.10.0 changes DSG path derivation from the old Silence Labs formula to standard BIP32-Ed25519, matching Eddsa.deriveUnhardened. Update all DSG cross-check tests and the signRecoveryEddsaMPCv2 helper to use Eddsa.deriveUnhardened instead of the now-incompatible deriveUnhardenedMps: - sdk-lib-mpc/src: mark deriveUnhardenedMps @deprecated — DSG no longer uses this formula; callers should switch to Eddsa.deriveUnhardened. - sdk-lib-mpc/test: replace the old deriveUnhardenedMps DSG cross-check block with new tests verifying against Ed25519Bip32HdTree.publicDerive (Eddsa.deriveUnhardened). Remove the commented-out old block. - sdk-core/src: make signRecoveryEddsaMPCv2 async; verify via getInitializedMpcInstance().deriveUnhardened; drop deriveUnhardenedMps. - sdk-core/test: await signRecoveryEddsaMPCv2; verify against mpc.deriveUnhardened; use assert.rejects for the async throw test; drop deriveUnhardenedMps import; change path from m/0/0 to m/0. - sdk-coin-sol/src: await signRecoveryEddsaMPCv2 (now returns Promise). This is a prerequisite for WCI-644 (full removal of deriveUnhardenedMps). Ticket: WCI-793 Session-Id: 01cf0284-3429-472d-8205-b7ef491d6426 Task-Id: 2a3decb6-9318-4219-94b3-21a0acdfebfb
1 parent eddba81 commit 2f7d1ac

6 files changed

Lines changed: 59 additions & 32 deletions

File tree

modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
MPCv2PartyFromStringOrNumber,
1414
} from '@bitgo/public-types';
1515
import { ed25519 } from '@noble/curves/ed25519';
16-
import { deriveUnhardenedMps, EddsaMPSDkg, EddsaMPSDsg, MPSComms, MPSTypes, MPSUtil } from '@bitgo/sdk-lib-mpc';
16+
import { EddsaMPSDkg, EddsaMPSDsg, MPSComms, MPSTypes, MPSUtil } from '@bitgo/sdk-lib-mpc';
1717
import { KeychainsTriplet } from '../../../baseCoin';
1818
import { AddKeychainOptions, Keychain, KeyType, WebauthnKeyEncryptionInfo } from '../../../keychain';
1919
import { envRequiresBitgoPubGpgKeyConfig, isBitgoEddsaMpcv2PubKey } from '../../../tss/bitgoPubKeys';
@@ -26,6 +26,7 @@ import {
2626
verifyPeerMessageRoundOne,
2727
verifyPeerMessageRoundTwo,
2828
} from '../../../tss/eddsa/eddsaMPCv2';
29+
import { getInitializedMpcInstance } from '../../../tss/eddsa/eddsa';
2930
import { generateGPGKeyPair } from '../../opengpgUtils';
3031
import { MPCv2PartiesEnum } from '../ecdsa/typesMPCv2';
3132
import {
@@ -1015,7 +1016,7 @@ export async function getEddsaMpcV2RecoveryKeySharesFromReducedKey(
10151016
* Ed25519 signature against the public key derived from the common keychain.
10161017
*
10171018
* @param message raw bytes to sign
1018-
* @param derivationPath BIP-32-style derivation path, e.g. `"m/0/0"`
1019+
* @param derivationPath BIP-32-style derivation path, e.g. `"m/0"`
10191020
* @param userKeyShare opaque MPS signing key-share bytes for the user party
10201021
* @param backupKeyShare opaque MPS signing key-share bytes for the backup party
10211022
* @param commonKeyChain 128-hex-char string: 32-byte pub + 32-byte rootChainCode
@@ -1041,8 +1042,10 @@ export async function signRecoveryEddsaMPCv2(
10411042
derivationPath
10421043
)) as Buffer;
10431044

1044-
// deriveUnhardenedMps returns 128 hex chars: first 64 are the 32-byte public key
1045-
const derivedKeychain = deriveUnhardenedMps(commonKeyChain, derivationPath);
1045+
// Use Eddsa.deriveUnhardened (BIP32-Ed25519), which matches what DSG uses
1046+
// internally for path derivation since wasm-mps 1.9.0.
1047+
const mpc = await getInitializedMpcInstance();
1048+
const derivedKeychain = mpc.deriveUnhardened(commonKeyChain, derivationPath);
10461049
const publicKeyBytes = Buffer.from(derivedKeychain.slice(0, 64), 'hex');
10471050

10481051
const verified = ed25519.verify(new Uint8Array(signature), new Uint8Array(message), new Uint8Array(publicKeyBytes));

modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as assert from 'assert';
22
import * as sinon from 'sinon';
33
import * as pgp from 'openpgp';
44
import { randomBytes } from 'crypto';
5-
import { deriveUnhardenedMps, EddsaMPSDsg, MPSComms, MPSTypes, MPSUtil } from '@bitgo/sdk-lib-mpc';
5+
import { EddsaMPSDsg, MPSComms, MPSTypes, MPSUtil } from '@bitgo/sdk-lib-mpc';
66
import { ed25519 } from '@noble/curves/ed25519';
77
import * as sjcl from '@bitgo/sjcl';
88
import {
@@ -34,6 +34,7 @@ import {
3434
verifyPeerMessageRoundOne,
3535
verifyPeerMessageRoundTwo,
3636
} from '../../../../../../src/bitgo/tss/eddsa/eddsaMPCv2';
37+
import { getInitializedMpcInstance } from '../../../../../../src/bitgo/tss/eddsa/eddsa';
3738
import { getBitgoSignatureShare } from '../../../../../../src/bitgo/tss/common';
3839
import { decodeWithCodec } from '../../../../../../src/bitgo/utils/codecs';
3940
import { generateGPGKeyPair } from '../../../../../../src/bitgo/utils/opengpgUtils';
@@ -1775,7 +1776,7 @@ describe('EDDSAUtils.isEddsaMpcV1SigningMaterial', () => {
17751776
});
17761777

17771778
describe('signRecoveryEddsaMPCv2', () => {
1778-
const derivationPath = 'm/0/0';
1779+
const derivationPath = 'm/0';
17791780

17801781
it('should return a 64-byte signature that verifies against the derived public key', async () => {
17811782
const [userDkg, backupDkg] = await MPSUtil.generateEdDsaDKGKeyShares();
@@ -1792,13 +1793,14 @@ describe('signRecoveryEddsaMPCv2', () => {
17921793

17931794
assert.strictEqual(signature.length, 64);
17941795

1795-
const derivedKeychain = deriveUnhardenedMps(commonKeyChain, derivationPath);
1796+
const mpc = await getInitializedMpcInstance();
1797+
const derivedKeychain = mpc.deriveUnhardened(commonKeyChain, derivationPath);
17961798
const publicKeyBytes = Buffer.from(derivedKeychain.slice(0, 64), 'hex');
17971799
const ok = ed25519.verify(new Uint8Array(signature), new Uint8Array(message), new Uint8Array(publicKeyBytes));
17981800
assert.strictEqual(ok, true);
17991801
});
18001802

1801-
it('should throw when the signed message is different from the verified message', async () => {
1803+
it('should return false when verifying the signature against a different message', async () => {
18021804
const [userDkg, backupDkg] = await MPSUtil.generateEdDsaDKGKeyShares();
18031805
const message = Buffer.from('deadbeef', 'hex');
18041806
const commonKeyChain = userDkg.getCommonKeychain();
@@ -1812,7 +1814,8 @@ describe('signRecoveryEddsaMPCv2', () => {
18121814
);
18131815

18141816
const differentMessage = Buffer.from('cafebabe', 'hex');
1815-
const derivedKeychain = deriveUnhardenedMps(commonKeyChain, derivationPath);
1817+
const mpc = await getInitializedMpcInstance();
1818+
const derivedKeychain = mpc.deriveUnhardened(commonKeyChain, derivationPath);
18161819
const publicKeyBytes = Buffer.from(derivedKeychain.slice(0, 64), 'hex');
18171820
const ok = ed25519.verify(
18181821
new Uint8Array(signature),
@@ -1828,13 +1831,14 @@ describe('signRecoveryEddsaMPCv2', () => {
18281831
const message = Buffer.from('deadbeef', 'hex');
18291832

18301833
await assert.rejects(
1831-
EDDSAUtils.signRecoveryEddsaMPCv2(
1832-
message,
1833-
derivationPath,
1834-
userDkg.getKeyShare(),
1835-
backupDkg.getKeyShare(),
1836-
wrongDkg.getCommonKeychain() // key chain from a different wallet
1837-
),
1834+
() =>
1835+
EDDSAUtils.signRecoveryEddsaMPCv2(
1836+
message,
1837+
derivationPath,
1838+
userDkg.getKeyShare(),
1839+
backupDkg.getKeyShare(),
1840+
wrongDkg.getCommonKeychain() // key chain from a different wallet
1841+
),
18381842
/EdDSA MPCv2 recovery signature verification failed/
18391843
);
18401844
});

modules/sdk-lib-mpc/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
]
3737
},
3838
"dependencies": {
39-
"@bitgo/wasm-mps": "1.8.1",
39+
"@bitgo/wasm-mps": "1.10.0",
4040
"@noble/curves": "1.8.1",
4141
"@silencelaboratories/dkls-wasm-ll-node": "1.2.0-pre.4",
4242
"@silencelaboratories/dkls-wasm-ll-web": "1.2.0-pre.4",

modules/sdk-lib-mpc/src/tss/eddsa-mps/derive.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ import { pathToIndices } from '../../curves/util';
1717
*
1818
* Returns the same on-the-wire format as `Eddsa.deriveUnhardened`:
1919
* 128-char hex = 64-char derived pk + 64-char derived chaincode
20+
*
21+
* @deprecated Use `Eddsa.deriveUnhardened` instead. wasm-mps >=1.9.0 uses
22+
* standard BIP32-Ed25519 (the Cardano formula) for DSG path derivation, so
23+
* `deriveUnhardenedMps` no longer matches what DSG signs with. This function
24+
* will be removed in a future release as part of WCI-644.
2025
*/
2126
export function deriveUnhardenedMps(commonKeychainHex: string, path: string): string {
2227
if (commonKeychainHex.length !== 128) {

modules/sdk-lib-mpc/test/unit/tss/eddsa/derive.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import assert from 'assert';
22
import { ed25519 } from '@noble/curves/ed25519';
33
import { EddsaMPSDsg, MPSUtil } from '../../../../src/tss/eddsa-mps';
44
import { deriveUnhardenedMps } from '../../../../src/tss/eddsa-mps/derive';
5+
import { Ed25519Bip32HdTree } from '../../../../src/curves/ed25519Bip32HdTree';
6+
import { bigIntFromBufferBE, bigIntFromBufferLE, bigIntToBufferLE } from '../../../../src/util';
57
import { generateEdDsaDKGKeyShares } from './util';
68

79
const MESSAGE = Buffer.from('The Times 03/Jan/2009 Chancellor on brink of second bailout for banks');
@@ -14,15 +16,28 @@ describe('deriveUnhardenedMps', function () {
1416
let rootPubKey: Buffer;
1517
let userKeyShare: Buffer;
1618
let bitgoKeyShare: Buffer;
19+
let hdTree: Ed25519Bip32HdTree;
1720

1821
before(async function () {
1922
const [userDkg, , bitgoDkg] = await generateEdDsaDKGKeyShares();
2023
commonKeychain = userDkg.getCommonKeychain();
2124
rootPubKey = userDkg.getSharePublicKey();
2225
userKeyShare = userDkg.getKeyShare();
2326
bitgoKeyShare = bitgoDkg.getKeyShare();
27+
hdTree = await Ed25519Bip32HdTree.initialize();
2428
});
2529

30+
// Derives a public key from commonKeychain using the standard Ed25519 BIP32
31+
// formula (same as Eddsa.deriveUnhardened). Returns the 32-byte public key.
32+
function derivePublicKeyBip32(keychain: string, path: string): Buffer {
33+
const buf = Buffer.from(keychain, 'hex');
34+
const derived = hdTree.publicDerive(
35+
{ pk: bigIntFromBufferLE(buf.slice(0, 32)), chaincode: bigIntFromBufferBE(buf.slice(32, 64)) },
36+
path
37+
);
38+
return bigIntToBufferLE(derived.pk, 32);
39+
}
40+
2641
describe('input validation', function () {
2742
it('throws when commonKeychainHex is shorter than 128 chars', function () {
2843
assert.throws(() => deriveUnhardenedMps('deadbeef', 'm'), /expected 128 hex chars/);
@@ -63,10 +78,10 @@ describe('deriveUnhardenedMps', function () {
6378
});
6479
});
6580

66-
describe('DSG signature cross-check against the public key derived by deriveUnhardenedMps', function () {
81+
describe('DSG signature cross-check against Ed25519Bip32HdTree (deriveUnhardened)', function () {
6782
let sigAtRoot: Buffer;
6883
let sigAtM0: Buffer;
69-
let sigAtM01: Buffer;
84+
let sigAtM1: Buffer;
7085

7186
before(async function () {
7287
const dsgA1 = new EddsaMPSDsg.DSG(0);
@@ -78,27 +93,27 @@ describe('deriveUnhardenedMps', function () {
7893
sigAtM0 = dsgA2.getSignature();
7994

8095
const dsgA3 = new EddsaMPSDsg.DSG(0);
81-
await MPSUtil.executeTillRound(3, dsgA3, new EddsaMPSDsg.DSG(2), userKeyShare, bitgoKeyShare, MESSAGE, 'm/0/1');
82-
sigAtM01 = dsgA3.getSignature();
96+
await MPSUtil.executeTillRound(3, dsgA3, new EddsaMPSDsg.DSG(2), userKeyShare, bitgoKeyShare, MESSAGE, 'm/1');
97+
sigAtM1 = dsgA3.getSignature();
8398
});
8499

85100
it('signature from DSG at "m" verifies against the root public key', function () {
86101
assert(ed25519.verify(sigAtRoot, MESSAGE, rootPubKey), 'DSG at "m" should verify against the raw DKG public key');
87102
});
88103

89-
it('signature from DSG at "m/0" verifies against deriveUnhardenedMps(commonKeychain, "m/0")', function () {
90-
const derivedPk = Buffer.from(deriveUnhardenedMps(commonKeychain, 'm/0').slice(0, 64), 'hex');
104+
it('signature from DSG at "m/0" verifies against Eddsa.deriveUnhardened key at "m/0"', function () {
105+
const derivedPk = derivePublicKeyBip32(commonKeychain, 'm/0');
91106
assert(
92107
ed25519.verify(sigAtM0, MESSAGE, derivedPk),
93-
'DSG at "m/0" should verify against deriveUnhardenedMps result at "m/0"'
108+
'DSG at "m/0" should verify against Eddsa.deriveUnhardened result at "m/0"'
94109
);
95110
});
96111

97-
it('signature from DSG at "m/0/1" verifies against deriveUnhardenedMps(commonKeychain, "m/0/1")', function () {
98-
const derivedPk = Buffer.from(deriveUnhardenedMps(commonKeychain, 'm/0/1').slice(0, 64), 'hex');
112+
it('signature from DSG at "m/1" verifies against Eddsa.deriveUnhardened key at "m/1"', function () {
113+
const derivedPk = derivePublicKeyBip32(commonKeychain, 'm/1');
99114
assert(
100-
ed25519.verify(sigAtM01, MESSAGE, derivedPk),
101-
'DSG at "m/0/1" should verify against deriveUnhardenedMps result at "m/0/1"'
115+
ed25519.verify(sigAtM1, MESSAGE, derivedPk),
116+
'DSG at "m/1" should verify against Eddsa.deriveUnhardened result at "m/1"'
102117
);
103118
});
104119
});

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,10 +1013,10 @@
10131013
resolved "https://registry.npmjs.org/@bitgo/wasm-dot/-/wasm-dot-1.7.0.tgz"
10141014
integrity sha512-KoXavJvyDHlEN+sWcigbgxYJtdFaU7gS0EkYQbNH4npVjNlzo6rL6gwjyWbyOy7oEs65DhpJ9vY5kRbE/bKiTQ==
10151015

1016-
"@bitgo/wasm-mps@1.8.1":
1017-
version "1.8.1"
1018-
resolved "https://registry.npmjs.org/@bitgo/wasm-mps/-/wasm-mps-1.8.1.tgz#946673f5845696cdcf744f8122fd1fc2be3edce1"
1019-
integrity sha512-CV8EXYc1BGYtXdCRDxJ5h04nj/LpMgu3VlkfowlodI6UKcj1zotAvk4OMIdgiPPbKVr1l+xibHDXZYx/uf3rnw==
1016+
"@bitgo/wasm-mps@1.10.0":
1017+
version "1.10.0"
1018+
resolved "https://registry.npmjs.org/@bitgo/wasm-mps/-/wasm-mps-1.10.0.tgz#df6a056247ce04c7d92369d257b659876e03261d"
1019+
integrity sha512-f42sMCyqqlaId3AtcvdpOfR+mOjAyVopCxCCAqW7wcTAQ8ZBS9rMGQIzTMiqFmZDBMWsAe+QHWA6XseIuzTVdQ==
10201020

10211021
"@bitgo/wasm-solana@^2.6.0":
10221022
version "2.6.0"

0 commit comments

Comments
 (0)