Skip to content

Commit 92be23f

Browse files
committed
fix(sdk-lib-mpc): add keyShare to EdDSA MPCv2 getReducedKeyShare
getReducedKeyShare() was only serialising sharePk (the 32-byte public key) into the CBOR keycard payload, silently discarding the opaque WASM signing key share and chain code. Recovery signing via DSG requires the full keyShare buffer as input to ed25519_dsg_round0_process, making the old format unusable for SDK-local hot-wallet recovery. ReducedKeyShareType now includes keyShare, pub, and rootChainCode, matching the pattern of ECDSA MPCv2's ReducedKeyShare. Tests updated to assert all three fields are present and correct. Ticket: WCI-385
1 parent abb73a8 commit 92be23f

3 files changed

Lines changed: 37 additions & 5 deletions

File tree

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,18 @@ export class DKG {
234234
}
235235

236236
/**
237-
* Returns a CBOR-encoded reduced representation containing the public key.
237+
* Returns a CBOR-encoded ReducedKeyShare buffer containing the party's opaque
238+
* signing key share in the `keyShare` field. This buffer is private key material.
239+
* The caller encrypts it and stores it as `reducedEncryptedPrv` on the key card QR code.
238240
*/
239241
getReducedKeyShare(): Buffer {
240-
if (!this.sharePk) {
242+
if (!this.keyShare || !this.sharePk || !this.shareChaincode) {
241243
throw Error('DKG session not initialized');
242244
}
243245
const reducedKeyShare: EddsaReducedKeyShare = {
246+
keyShare: Array.from(this.keyShare),
244247
pub: Array.from(this.sharePk),
248+
rootChainCode: Array.from(this.shareChaincode),
245249
};
246250
return Buffer.from(encode(reducedKeyShare));
247251
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { isLeft } from 'fp-ts/Either';
33
import * as t from 'io-ts';
44

55
export const ReducedKeyShareType = t.type({
6+
keyShare: t.array(t.number),
67
pub: t.array(t.number),
8+
rootChainCode: t.array(t.number),
79
});
810

911
export type EddsaReducedKeyShare = t.TypeOf<typeof ReducedKeyShareType>;

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,38 @@ describe('EdDSA MPS DKG', function () {
205205
);
206206
assert(Buffer.isBuffer(bitgoReduced) && bitgoReduced.length > 0, 'BitGo reduced key share should be non-empty');
207207

208-
const userPub = Buffer.from(MPSTypes.getDecodedReducedKeyShare(userReduced).pub).toString('hex');
209-
const backupPub = Buffer.from(MPSTypes.getDecodedReducedKeyShare(backupReduced).pub).toString('hex');
210-
const bitgoPub = Buffer.from(MPSTypes.getDecodedReducedKeyShare(bitgoReduced).pub).toString('hex');
208+
const userDecoded = MPSTypes.getDecodedReducedKeyShare(userReduced);
209+
const backupDecoded = MPSTypes.getDecodedReducedKeyShare(backupReduced);
210+
const bitgoDecoded = MPSTypes.getDecodedReducedKeyShare(bitgoReduced);
211+
212+
const userPub = Buffer.from(userDecoded.pub).toString('hex');
213+
const backupPub = Buffer.from(backupDecoded.pub).toString('hex');
214+
const bitgoPub = Buffer.from(bitgoDecoded.pub).toString('hex');
211215

212216
assert.strictEqual(userPub, backupPub, 'User and backup should have same public key in reduced share');
213217
assert.strictEqual(backupPub, bitgoPub, 'Backup and BitGo should have same public key in reduced share');
218+
219+
// keyShare must be present and non-empty (opaque WASM bincode needed for DSG)
220+
assert(userDecoded.keyShare.length > 0, 'User reduced share must include keyShare');
221+
assert(backupDecoded.keyShare.length > 0, 'Backup reduced share must include keyShare');
222+
assert(bitgoDecoded.keyShare.length > 0, 'BitGo reduced share must include keyShare');
223+
224+
// rootChainCode must be 32 bytes
225+
assert.strictEqual(userDecoded.rootChainCode.length, 32, 'User rootChainCode must be 32 bytes');
226+
assert.strictEqual(backupDecoded.rootChainCode.length, 32, 'Backup rootChainCode must be 32 bytes');
227+
assert.strictEqual(bitgoDecoded.rootChainCode.length, 32, 'BitGo rootChainCode must be 32 bytes');
228+
229+
// All parties derive the same chaincode
230+
assert.strictEqual(
231+
Buffer.from(userDecoded.rootChainCode).toString('hex'),
232+
Buffer.from(backupDecoded.rootChainCode).toString('hex'),
233+
'User and backup should have same rootChainCode'
234+
);
235+
assert.strictEqual(
236+
Buffer.from(backupDecoded.rootChainCode).toString('hex'),
237+
Buffer.from(bitgoDecoded.rootChainCode).toString('hex'),
238+
'Backup and BitGo should have same rootChainCode'
239+
);
214240
});
215241
});
216242

0 commit comments

Comments
 (0)