Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion modules/sdk-lib-mpc/src/tss/ecdsa-dkls/dkg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,6 @@ export class Dkg {
}

dkg.dkgSessionBytes = sessionData.dkgSessionBytes;
dkg.dkgState = sessionData.dkgState;

if (sessionData.chainCodeCommitment) {
dkg.chainCodeCommitment = sessionData.chainCodeCommitment;
Expand All @@ -350,6 +349,15 @@ export class Dkg {
}

dkg._restoreSession();
// Re-derive state from WASM bytes rather than trusting the caller-supplied dkgState.
// This prevents a tampered or corrupted dkgState from causing handleIncomingMessages()
// to take the wrong branch (e.g. skipping chain code commitment or calling keyshare() prematurely).
dkg._deserializeState();
if (sessionData.keyShareBuff) {
// After keyshare() is called the WASM session is freed and dkgSessionBytes still reflect WaitMsg4.
// Presence of keyShareBuff is the reliable signal that the protocol completed.
dkg.dkgState = DkgState.Complete;
}
Comment on lines +356 to +360
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be handled by _deserializeState

return dkg;
}
}
24 changes: 24 additions & 0 deletions modules/sdk-lib-mpc/test/unit/tss/ecdsa/dklsDkg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,30 @@ describe('DKLS Dkg 2x3', function () {
assert.deepEqual(DklsTypes.getCommonKeychain(backupKeyShare), DklsTypes.getCommonKeychain(bitgoKeyShare));
});

it('restoreSession() should ignore tampered dkgState and re-derive from WASM bytes', async function () {
const user = new DklsDkg.Dkg(3, 2, 0);

// After initDkg() the WASM session encodes WaitMsg1 → DkgState.Round1
await user.initDkg();

const legitimateSessionData = user.getSessionData();

// Tamper: claim the session is at Round4 when WASM bytes still say Round1
const tamperedSessionData = {
...legitimateSessionData,
dkgState: DklsTypes.DkgState.Round4,
};

const restoredUser = await DklsDkg.Dkg.restoreSession(3, 2, 0, tamperedSessionData);

// Must reflect the actual WASM state (Round1), not the tampered Round4
assert.strictEqual(
restoredUser['dkgState'],
DklsTypes.DkgState.Round1,
'restoreSession() must re-derive dkgState from WASM bytes and ignore caller-supplied value'
);
});

it('should successfully finish DKG using restored sessions', async function () {
const user = new DklsDkg.Dkg(3, 2, 0);
const backup = new DklsDkg.Dkg(3, 2, 1);
Expand Down
Loading