Skip to content

Commit 0aaa530

Browse files
authored
Merge pull request #48 from BitGo/WP-5151-mpc-ecdsa-onprem-ebe
feat(ebe): mpcv2 signing for all rounds
2 parents e810996 + 713f293 commit 0aaa530

File tree

7 files changed

+733
-5
lines changed

7 files changed

+733
-5
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"@types/express": "4.17.13",
5151
"@types/jasmine": "^5.1.8",
5252
"@types/jest": "^29.5.12",
53+
"@types/keccak": "^3.0.5",
5354
"@types/lodash": "^4.14.121",
5455
"@types/mocha": "^10.0.10",
5556
"@types/morgan": "^1.7.35",
@@ -65,6 +66,7 @@
6566
"eslint-plugin-prettier": "^4.0.0",
6667
"jasmine": "^5.8.0",
6768
"jest": "^29.7.0",
69+
"keccak": "^3.0.3",
6870
"mocha": "^11.6.0",
6971
"nock": "^13.3.1",
7072
"nodemon": "^3.1.10",
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// ECDSA MPCv2 specific imports
2+
import { DklsTypes, DklsComms, DklsDsg } from '@bitgo/sdk-lib-mpc';
3+
4+
import { TxRequest, SignatureShareRecord, SignatureShareType } from '@bitgo/sdk-core';
5+
6+
// MPCv2 type definitions
7+
import {
8+
MPCv2PartyFromStringOrNumber,
9+
MPCv2SignatureShareRound1Input,
10+
MPCv2SignatureShareRound1Output,
11+
MPCv2SignatureShareRound2Input,
12+
MPCv2SignatureShareRound2Output,
13+
MPCv2SignatureShareRound3Input,
14+
} from '@bitgo/public-types';
15+
import assert from 'assert';
16+
import { bitgoGpgKey } from '../../mocks/gpgKeys';
17+
18+
export async function signBitgoMPCv2Round1(
19+
bitgoSession: DklsDsg.Dsg,
20+
txRequest: TxRequest,
21+
userShare: SignatureShareRecord,
22+
userGPGPubKey: string,
23+
): Promise<TxRequest> {
24+
assert(
25+
txRequest.transactions && txRequest.transactions.length === 1,
26+
'txRequest.transactions is not an array of length 1',
27+
);
28+
txRequest.transactions[0].signatureShares.push(userShare);
29+
// Do the actual signing on BitGo's side based on User's messages
30+
const signatureShare = JSON.parse(userShare.share) as MPCv2SignatureShareRound1Input;
31+
const deserializedMessages = DklsTypes.deserializeMessages({
32+
p2pMessages: [],
33+
broadcastMessages: [
34+
{
35+
from: signatureShare.data.msg1.from,
36+
payload: signatureShare.data.msg1.message,
37+
},
38+
],
39+
});
40+
const bitgoToUserRound1BroadcastMsg = await bitgoSession.init();
41+
const bitgoToUserRound2Msg = bitgoSession.handleIncomingMessages({
42+
p2pMessages: [],
43+
broadcastMessages: deserializedMessages.broadcastMessages,
44+
});
45+
const serializedBitGoToUserRound1And2Msgs = DklsTypes.serializeMessages({
46+
p2pMessages: bitgoToUserRound2Msg.p2pMessages,
47+
broadcastMessages: [bitgoToUserRound1BroadcastMsg],
48+
});
49+
50+
const authEncMessages = await DklsComms.encryptAndAuthOutgoingMessages(
51+
serializedBitGoToUserRound1And2Msgs,
52+
[getUserPartyGpgKeyPublic(userGPGPubKey)],
53+
[getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)],
54+
);
55+
56+
const bitgoToUserSignatureShare: MPCv2SignatureShareRound1Output = {
57+
type: 'round1Output',
58+
data: {
59+
msg1: {
60+
from: authEncMessages.broadcastMessages[0].from as MPCv2PartyFromStringOrNumber,
61+
signature: authEncMessages.broadcastMessages[0].payload.signature,
62+
message: authEncMessages.broadcastMessages[0].payload.message,
63+
},
64+
msg2: {
65+
from: authEncMessages.p2pMessages[0].from as MPCv2PartyFromStringOrNumber,
66+
to: authEncMessages.p2pMessages[0].to as MPCv2PartyFromStringOrNumber,
67+
encryptedMessage: authEncMessages.p2pMessages[0].payload.encryptedMessage,
68+
signature: authEncMessages.p2pMessages[0].payload.signature,
69+
},
70+
},
71+
};
72+
txRequest.transactions[0].signatureShares.push({
73+
from: SignatureShareType.BITGO,
74+
to: SignatureShareType.USER,
75+
share: JSON.stringify(bitgoToUserSignatureShare),
76+
});
77+
return txRequest;
78+
}
79+
80+
export async function signBitgoMPCv2Round2(
81+
bitgoSession: DklsDsg.Dsg,
82+
txRequest: TxRequest,
83+
userShare: SignatureShareRecord,
84+
userGPGPubKey: string,
85+
): Promise<{ txRequest: TxRequest; bitgoMsg4: DklsTypes.SerializedBroadcastMessage }> {
86+
assert(
87+
txRequest.transactions && txRequest.transactions.length === 1,
88+
'txRequest.transactions is not an array of length 1',
89+
);
90+
txRequest.transactions[0].signatureShares.push(userShare);
91+
92+
// Do the actual signing on BitGo's side based on User's messages
93+
const parsedSignatureShare = JSON.parse(userShare.share) as MPCv2SignatureShareRound2Input;
94+
const serializedMessages = await DklsComms.decryptAndVerifyIncomingMessages(
95+
{
96+
p2pMessages: [
97+
{
98+
from: parsedSignatureShare.data.msg2.from,
99+
to: parsedSignatureShare.data.msg2.to,
100+
payload: {
101+
encryptedMessage: parsedSignatureShare.data.msg2.encryptedMessage,
102+
signature: parsedSignatureShare.data.msg2.signature,
103+
},
104+
},
105+
{
106+
from: parsedSignatureShare.data.msg3.from,
107+
to: parsedSignatureShare.data.msg3.to,
108+
payload: {
109+
encryptedMessage: parsedSignatureShare.data.msg3.encryptedMessage,
110+
signature: parsedSignatureShare.data.msg3.signature,
111+
},
112+
},
113+
],
114+
broadcastMessages: [],
115+
},
116+
[getUserPartyGpgKeyPublic(userGPGPubKey)],
117+
[getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)],
118+
);
119+
const deserializedMessages2 = DklsTypes.deserializeMessages({
120+
p2pMessages: [serializedMessages.p2pMessages[0]],
121+
broadcastMessages: [],
122+
});
123+
124+
const bitgoToUserRound3Msg = bitgoSession.handleIncomingMessages(deserializedMessages2);
125+
const serializedBitGoToUserRound3Msgs = DklsTypes.serializeMessages(bitgoToUserRound3Msg);
126+
127+
const authEncMessages = await DklsComms.encryptAndAuthOutgoingMessages(
128+
serializedBitGoToUserRound3Msgs,
129+
[getUserPartyGpgKeyPublic(userGPGPubKey)],
130+
[getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)],
131+
);
132+
133+
const bitgoToUserSignatureShare: MPCv2SignatureShareRound2Output = {
134+
type: 'round2Output',
135+
data: {
136+
msg3: {
137+
from: authEncMessages.p2pMessages[0].from as MPCv2PartyFromStringOrNumber,
138+
to: authEncMessages.p2pMessages[0].to as MPCv2PartyFromStringOrNumber,
139+
encryptedMessage: authEncMessages.p2pMessages[0].payload.encryptedMessage,
140+
signature: authEncMessages.p2pMessages[0].payload.signature,
141+
},
142+
},
143+
};
144+
145+
// handling user msg3 but not returning bitgo msg4 since its stored on bitgo side only
146+
const deserializedMessages3 = DklsTypes.deserializeMessages({
147+
p2pMessages: [serializedMessages.p2pMessages[1]],
148+
broadcastMessages: [],
149+
});
150+
const deserializedBitgoMsg4 = bitgoSession.handleIncomingMessages(deserializedMessages3);
151+
const serializedBitGoToUserRound4Msgs = DklsTypes.serializeMessages(deserializedBitgoMsg4);
152+
153+
txRequest.transactions[0].signatureShares.push({
154+
from: SignatureShareType.BITGO,
155+
to: SignatureShareType.USER,
156+
share: JSON.stringify(bitgoToUserSignatureShare),
157+
});
158+
return { txRequest, bitgoMsg4: serializedBitGoToUserRound4Msgs.broadcastMessages[0] };
159+
}
160+
161+
export async function signBitgoMPCv2Round3(
162+
bitgoSession: DklsDsg.Dsg,
163+
userShare: SignatureShareRecord,
164+
userGPGPubKey: string,
165+
): Promise<{ userMsg4: MPCv2SignatureShareRound3Input }> {
166+
const parsedSignatureShare = JSON.parse(userShare.share) as MPCv2SignatureShareRound3Input;
167+
const serializedMessages = await DklsComms.decryptAndVerifyIncomingMessages(
168+
{
169+
p2pMessages: [],
170+
broadcastMessages: [
171+
{
172+
from: parsedSignatureShare.data.msg4.from,
173+
payload: {
174+
message: parsedSignatureShare.data.msg4.message,
175+
signature: parsedSignatureShare.data.msg4.signature,
176+
},
177+
},
178+
],
179+
},
180+
[getUserPartyGpgKeyPublic(userGPGPubKey)],
181+
[getBitGoPartyGpgKeyPrv(bitgoGpgKey.private)],
182+
);
183+
const deserializedMessages = DklsTypes.deserializeMessages({
184+
p2pMessages: [],
185+
broadcastMessages: [serializedMessages.broadcastMessages[0]],
186+
});
187+
bitgoSession.handleIncomingMessages(deserializedMessages);
188+
189+
return {
190+
userMsg4: parsedSignatureShare,
191+
};
192+
}
193+
194+
function getBitGoPartyGpgKeyPrv(bitgoPrvKey: string): DklsTypes.PartyGpgKey {
195+
return {
196+
partyId: 2,
197+
gpgKey: bitgoPrvKey,
198+
};
199+
}
200+
201+
function getUserPartyGpgKeyPublic(userPubKey: string): DklsTypes.PartyGpgKey {
202+
return {
203+
partyId: 0,
204+
gpgKey: userPubKey,
205+
};
206+
}

0 commit comments

Comments
 (0)