Skip to content

Commit 746c96c

Browse files
WIP: generate eddsa tss wallet
1 parent 004de85 commit 746c96c

File tree

7 files changed

+73
-53
lines changed

7 files changed

+73
-53
lines changed

src/api/enclaved/mpcFinalize.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as bitgoSdk from '@bitgo/sdk-core';
2-
import * as crypto from 'crypto';
32
import * as openpgp from 'openpgp';
43
import {
54
EnclavedApiSpecRouteRequest,
@@ -32,7 +31,10 @@ export async function eddsaFinalize(req: EnclavedApiSpecRouteRequest<'v1.mpc.fin
3231
// Decrypt the encrypted payload using encryptedDataKey to retrieve the previous state of computation
3332
const decryptedDataKey = await kms.decryptDataKey(encryptedDataKey);
3433
const previousState = JSON.parse(
35-
crypto.publicDecrypt(Buffer.from(decryptedDataKey), Buffer.from(encryptedData)).toString(),
34+
req.bitgo.decrypt({
35+
input: encryptedData,
36+
password: decryptedDataKey,
37+
}),
3638
);
3739
console.log('Decrypted previous state:', previousState);
3840
const { sourceGpgPub, sourceGpgPrv, sourcePrivateShare } = previousState;
@@ -51,8 +53,8 @@ export async function eddsaFinalize(req: EnclavedApiSpecRouteRequest<'v1.mpc.fin
5153
);
5254

5355
await eddsaUtils.verifyWalletSignatures(
54-
sourceGpgPub,
55-
counterPartyGpgPub,
56+
source === 'user' ? sourceGpgPub : counterPartyGpgPub,
57+
source === 'user' ? counterPartyGpgPub : sourceGpgPub,
5658
bitgoKeyChain,
5759
bitgoToSourcePrivateShare,
5860
sourceIndex,
@@ -111,7 +113,7 @@ export async function eddsaFinalize(req: EnclavedApiSpecRouteRequest<'v1.mpc.fin
111113
console.log('Common keychain:', commonKeychain);
112114

113115
// if counterPartyGpgPub is provided, encrypt the private key share to be sent to the counter party
114-
if (!sourceToCounterPartyKeyShare) {
116+
if (sourceToCounterPartyKeyShare) {
115117
sourceToCounterPartyKeyShare = {
116118
...sourceToCounterPartyKeyShare,
117119
privateShare: counterPartyGpgPub
@@ -137,16 +139,18 @@ export async function eddsaFinalize(req: EnclavedApiSpecRouteRequest<'v1.mpc.fin
137139
* Helper function to encrypt text using OpenPGP
138140
*/
139141
async function gpgEncrypt(text: string, key: string): Promise<string> {
140-
return await openpgp.encrypt({
141-
message: await openpgp.createMessage({ text }),
142-
encryptionKeys: await openpgp.readKey({ armoredKey: key }),
143-
format: 'armored',
144-
config: {
145-
rejectCurves: new Set(),
146-
showVersion: false,
147-
showComment: false,
148-
},
149-
});
142+
return (
143+
await openpgp.encrypt({
144+
message: await openpgp.createMessage({ text }),
145+
encryptionKeys: await openpgp.readKey({ armoredKey: key }),
146+
format: 'armored',
147+
config: {
148+
rejectCurves: new Set(),
149+
showVersion: false,
150+
showComment: false,
151+
},
152+
})
153+
).toString();
150154
}
151155

152156
/**
@@ -166,7 +170,7 @@ async function gpgDecrypt(text: string, key: string): Promise<string> {
166170
})
167171
).data;
168172

169-
return decryptedPrivateShare;
173+
return decryptedPrivateShare.toString();
170174
}
171175

172176
// /**

src/api/enclaved/mpcInitialize.ts

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as bitgoSdk from '@bitgo/sdk-core';
22
import { KmsClient } from '../../kms/kmsClient';
3-
import * as crypto from 'crypto';
43
import {
54
EnclavedApiSpecRouteRequest,
65
KeyShareType,
@@ -97,30 +96,38 @@ export async function eddsaInitialize(
9796
counterPartyKeyShare: counterPartyGpgPub ? undefined : counterPartyKeyShare, // if counterPartyGpgPub is NOT gpg encrypted, store in payload to be encrypted in finalize
9897
};
9998
const { plaintextKey, encryptedKey } = await kms.generateDataKey({ keyType: 'AES-256' });
100-
const encryptedPayload = crypto
101-
.publicEncrypt(Buffer.from(plaintextKey), Buffer.from(JSON.stringify(payload)))
102-
.toString();
103-
104-
return {
105-
encryptedDataKey: encryptedKey,
106-
encryptedData: encryptedPayload,
107-
bitgoKeyShare,
108-
counterPartyKeyShare: counterPartyGpgPub ? counterPartyKeyShare : undefined, // if counterPartyGpgPub is encrypted, send the key share unecrypted
109-
};
99+
console.log({ plaintextKey, source });
100+
try {
101+
const encryptedPayload = req.bitgo.encrypt({
102+
input: JSON.stringify(payload),
103+
password: plaintextKey,
104+
});
105+
return {
106+
encryptedDataKey: encryptedKey,
107+
encryptedData: encryptedPayload,
108+
bitgoPayload: bitgoKeyShare,
109+
counterPartyKeyShare: counterPartyGpgPub ? counterPartyKeyShare : undefined, // if counterPartyGpgPub is encrypted, send the key share unecrypted
110+
};
111+
} catch (error) {
112+
console.error('Encryption error details:', error);
113+
throw error;
114+
}
110115
}
111116

112117
/**
113118
* Helper function to encrypt text using OpenPGP
114119
*/
115120
async function gpgEncrypt(text: string, key: string): Promise<string> {
116-
return await openpgp.encrypt({
117-
message: await openpgp.createMessage({ text }),
118-
encryptionKeys: await openpgp.readKey({ armoredKey: key }),
119-
format: 'armored',
120-
config: {
121-
rejectCurves: new Set(),
122-
showVersion: false,
123-
showComment: false,
124-
},
125-
});
121+
return (
122+
await openpgp.encrypt({
123+
message: await openpgp.createMessage({ text }),
124+
encryptionKeys: await openpgp.readKey({ armoredKey: key }),
125+
format: 'armored',
126+
config: {
127+
rejectCurves: new Set(),
128+
showVersion: false,
129+
showComment: false,
130+
},
131+
})
132+
).toString();
126133
}

src/enclavedBitgoExpress/routers/enclavedApiSpec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const KeyShare = {
9292
vssProof: t.string,
9393
gpgKey: t.string,
9494
};
95+
9596
const KeyShareType = t.type(KeyShare);
9697
export type KeyShareType = t.TypeOf<typeof KeyShareType>;
9798

@@ -118,7 +119,8 @@ const BitGoKeychainType = t.type({
118119
type: t.literal('tss'),
119120
commonKeychain: t.string,
120121
verifiedVssProof: t.boolean,
121-
keyShares: t.array(KeyShareType),
122+
// TODO: api-ts does not like optionalized gpgKey
123+
keyShares: t.array(t.any),
122124
});
123125

124126
const MpcFinalizeRequest = {
@@ -274,6 +276,7 @@ export function createKeyGenRouter(config: EnclavedConfig): WrappedRouter<typeof
274276
try {
275277
const typedReq = _req as EnclavedApiSpecRouteRequest<'v1.mpc.initialize', 'post'>;
276278
const response = await eddsaInitialize(typedReq);
279+
console.log(response);
277280
return Response.ok(response);
278281
} catch (error) {
279282
const err = error as Error;

src/kms/kmsClient.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ export class KmsClient {
103103
}
104104

105105
return {
106-
plaintextKey: kmsResponse.plaintextKey.split(',').map((x: any) => parseInt(x, 10)),
107-
encryptedKey: kmsResponse.encryptedKey,
106+
plaintextKey: kmsResponse.body.plaintextKey,
107+
encryptedKey: kmsResponse.body.encryptedKey,
108108
};
109109
}
110110

111-
async decryptDataKey(encryptedKey: string): Promise<Uint8Array> {
111+
async decryptDataKey(encryptedKey: string): Promise<string> {
112112
debugLogger('Decrypting data key: %s', encryptedKey);
113113

114114
// Call KMS to decrypt the data key
@@ -127,6 +127,8 @@ export class KmsClient {
127127
throw new Error('KMS did not return a valid plaintext key');
128128
}
129129

130-
return kmsResponse.body.plaintextKey;
130+
console.log(kmsResponse.body);
131+
console.log(`PlaintextKey: ${kmsResponse.body.plaintextKey.toString()}`);
132+
return kmsResponse.body.plaintextKey.toString();
131133
}
132134
}

src/kms/types/generateDataKey.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export interface GenerateDataKeyParams {
55
}
66

77
export interface GenerateDataKeyResponse {
8-
plaintextKey: Uint8Array;
8+
plaintextKey: string;
99
encryptedKey: string;
1010
}
1111

src/masterBitgoExpress/enclavedExpressClient.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import https from 'https';
22
import debug from 'debug';
33
import superagent from 'superagent';
44

5-
import { Keychain, SignedTransaction, TransactionPrebuild } from '@bitgo/sdk-core';
5+
import { ApiKeyShare, Keychain, SignedTransaction, TransactionPrebuild } from '@bitgo/sdk-core';
66
import { superagentRequestFactory, buildApiClient, ApiClient } from '@api-ts/superagent-wrapper';
77
import { OfflineVaultTxInfo, RecoveryInfo, UnsignedSweepTxMPCv2 } from '@bitgo/sdk-coin-eth';
88

@@ -34,6 +34,7 @@ export type FinalizeMpcKeyGenerationParams = {
3434
verifiedVssProof: boolean;
3535
isBitGo?: boolean;
3636
isTrust?: boolean;
37+
keyShares: ApiKeyShare[];
3738
};
3839
counterPartyGPGKey: string;
3940
counterPartyKeyShare: KeyShareType;
@@ -315,7 +316,7 @@ export class EnclavedExpressClient {
315316
source: 'bitgo',
316317
type: 'tss',
317318
commonKeychain: bitgoKeychain.commonKeychain ?? '',
318-
keyShares: bitgoKeychain.keyShares as any[],
319+
keyShares: bitgoKeychain.keyShares,
319320
},
320321
counterPartyGpgPub: params.counterPartyGPGKey,
321322
counterPartyKeyShare: params.counterPartyKeyShare,

src/masterBitgoExpress/generateWallet.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,12 @@ export async function handleGenerateOnPremMpcWallet(
139139
const { label, enterprise } = req.decoded;
140140

141141
// Create wallet parameters with type assertion to allow 'tss' subtype
142-
const walletParams = {
142+
const walletParams: SupplementGenerateWalletOptions = {
143143
label: label,
144144
m: 2,
145145
n: 3,
146146
keys: [],
147-
type: 'hot',
148-
subType: 'tss',
147+
type: 'cold',
149148
multisigType: 'tss',
150149
} as unknown as SupplementGenerateWalletOptions;
151150

@@ -206,7 +205,9 @@ export async function handleGenerateOnPremMpcWallet(
206205
reqId,
207206
});
208207

209-
console.log('BitGo keychain created:', bitgoKeychain);
208+
console.log('User BitGo payload:', JSON.stringify(userInitResponse.bitgoPayload, null, 2));
209+
console.log('Backup BitGo payload:', JSON.stringify(backupInitResponse.bitgoPayload, null, 2));
210+
console.log('BitGo keychain keyShares:', JSON.stringify(bitgoKeychain.keyShares, null, 2));
210211

211212
// TODO Create proper type guard for bitgoKeychain
212213
assert(bitgoKeychain.type === 'tss', 'BitGo keychain must be of type tss');
@@ -229,7 +230,7 @@ export async function handleGenerateOnPremMpcWallet(
229230
verifiedVssProof: true,
230231
isBitGo: true, // Ensure BitGo keychain is marked as BitGo
231232
isTrust: false,
232-
keyShares: bitgoKeychain.keyShares,
233+
keyShares: bitgoKeychain.keyShares as KeyShareType[], // Ensure keyShares are included
233234
},
234235
counterPartyGPGKey: backupGPGKey,
235236
counterPartyKeyShare: backupInitResponse.counterPartyKeyShare,
@@ -245,6 +246,7 @@ export async function handleGenerateOnPremMpcWallet(
245246
});
246247

247248
console.log('User keychain finalized:', userMpcKey);
249+
console.log(userKeychainPromise.counterpartyKeyShare);
248250

249251
const backupKeychainPromise = await enclavedExpressClient.finalizeMpcKeyGeneration({
250252
source: 'backup',
@@ -256,12 +258,13 @@ export async function handleGenerateOnPremMpcWallet(
256258
commonKeychain: bitgoKeychain.commonKeychain ?? '',
257259
hsmType: bitgoKeychain.hsmType,
258260
type: 'tss',
259-
source: 'bitgo', // Ensure BitGo keychain is marked as BitGo
261+
source: 'bitgo',
260262
verifiedVssProof: true,
261-
isBitGo: true, // Ensure BitGo keychain is marked as BitGo
263+
isBitGo: true,
262264
isTrust: false,
265+
keyShares: bitgoKeychain.keyShares as any, // Ensure keyShares are included
263266
},
264-
counterPartyGPGKey: userGPGKey as string, // not sure why I have to cast this here
267+
counterPartyGPGKey: userGPGKey,
265268
counterPartyKeyShare: userKeychainPromise.counterpartyKeyShare as KeyShareType, // also not sure why I have to cast this here
266269
});
267270

0 commit comments

Comments
 (0)