Skip to content

Commit 20b1b66

Browse files
authored
Merge pull request #8681 from BitGo/WCI-311
feat: add eddsa mpcv2 gpg key support to tss signing
2 parents 8d45a1d + 0a53cbd commit 20b1b66

3 files changed

Lines changed: 95 additions & 13 deletions

File tree

modules/bitgo/test/v2/unit/internal/tssUtils/bitgoMpcGpgPubKeys.ts

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
BitgoMpcGpgPubKeys,
33
common,
44
ECDSAUtils,
5+
EDDSAUtils,
56
EddsaUtils,
67
EnvironmentName,
78
IRequestTracer,
@@ -28,9 +29,21 @@ class TestEddsaMpcv1Utils extends EddsaUtils {
2829
public async testPickBitgoPubGpgKeyForSigning(
2930
isMpcv2: boolean,
3031
reqId?: IRequestTracer,
31-
enterpriseId?: string
32+
enterpriseId?: string,
33+
isEddsaMpcv2?: boolean
3234
): Promise<openpgp.Key> {
33-
return this.pickBitgoPubGpgKeyForSigning(isMpcv2, reqId, enterpriseId);
35+
return this.pickBitgoPubGpgKeyForSigning(isMpcv2, reqId, enterpriseId, isEddsaMpcv2);
36+
}
37+
}
38+
39+
class TestEddsaMpcv2Utils extends EDDSAUtils.EddsaMPCv2Utils {
40+
public async testPickBitgoPubGpgKeyForSigning(
41+
isMpcv2: boolean,
42+
reqId?: IRequestTracer,
43+
enterpriseId?: string,
44+
isEddsaMpcv2?: boolean
45+
): Promise<openpgp.Key> {
46+
return this.pickBitgoPubGpgKeyForSigning(isMpcv2, reqId, enterpriseId, isEddsaMpcv2);
3447
}
3548
}
3649

@@ -58,6 +71,7 @@ describe('TSS MPC Pick BitGo GPG Pub Key Utils:', function () {
5871
const envs: EnvironmentName[] = ['test', 'staging', 'prod'];
5972
const ecdsaMpcv2Utils: TestEcdsaMpcv2Utils[] = [];
6073
const eddsaMpcv1Utils: TestEddsaMpcv1Utils[] = [];
74+
const eddsaMpcv2Utils: TestEddsaMpcv2Utils[] = [];
6175

6276
before(async function () {
6377
nock.cleanAll();
@@ -72,6 +86,9 @@ describe('TSS MPC Pick BitGo GPG Pub Key Utils:', function () {
7286
eddsaMpcv1Utils.push(
7387
new TestEddsaMpcv1Utils(bitgoInstance, coinInstance, new Wallet(bitgoInstance, coinInstance, eddsaWalletData))
7488
);
89+
eddsaMpcv2Utils.push(
90+
new TestEddsaMpcv2Utils(bitgoInstance, coinInstance, new Wallet(bitgoInstance, coinInstance, eddsaWalletData))
91+
);
7592
}
7693
});
7794

@@ -225,6 +242,49 @@ describe('TSS MPC Pick BitGo GPG Pub Key Utils:', function () {
225242
gpgKey.armor().should.equal(capturedKey);
226243
});
227244

245+
describe('EdDSA MPCv2 pickBitgoPubGpgKeyForSigning', function () {
246+
it('should pick correct EdDSA MPCv2 BitGo GPG Pub Key when keychain returns hsmType onprem', async function () {
247+
nock.cleanAll();
248+
// Mock hsmType as 'onprem' for the EdDSA coin so the onprem eddsaMpcv2 key is selected
249+
const bgUrl = common.Environments['test'].uri;
250+
nock(bgUrl).get(`/api/v2/${eddsaCoinName}/key/key3`).reply(200, { hsmType: 'onprem' });
251+
const bitgoGpgPubKey = await eddsaMpcv2Utils[0].testPickBitgoPubGpgKeyForSigning(
252+
true,
253+
undefined,
254+
undefined,
255+
true
256+
);
257+
bitgoGpgPubKey.armor().should.equal(BitgoMpcGpgPubKeys.bitgoMpcGpgPubKeys['eddsaMpcv2']['onprem']['test']);
258+
});
259+
260+
it('should pick EdDSA MPCv2 BitGo GPG Pub Key based on feature flags for mock env', async function () {
261+
const bgUrl = common.Environments['mock'].uri;
262+
const testBitgo = TestBitGo.decorate(BitGo, { env: 'mock' });
263+
const testCoin = testBitgo.coin(eddsaCoinName);
264+
const bitgoGPGKey = await openpgp.generateKey({
265+
userIDs: [
266+
{
267+
name: 'bitgo',
268+
email: 'bitgo@test.com',
269+
},
270+
],
271+
type: 'ecc',
272+
curve: 'ed25519',
273+
});
274+
nock(bgUrl)
275+
.get(`/api/v2/${eddsaCoinName}/tss/pubkey`)
276+
.query({ enterpriseId })
277+
.reply(200, { mpcv2PublicKey: bitgoGPGKey.publicKey, eddsaMpcv2PublicKey: bitgoGPGKey.publicKey });
278+
const eddsaMpcv2Util = new TestEddsaMpcv2Utils(
279+
testBitgo,
280+
testCoin,
281+
new Wallet(testBitgo, testCoin, eddsaWalletData)
282+
);
283+
const bitgoGpgPubKey = await eddsaMpcv2Util.testPickBitgoPubGpgKeyForSigning(true, undefined, enterpriseId, true);
284+
bitgoGpgPubKey.armor().should.equal(bitgoGPGKey.publicKey);
285+
});
286+
});
287+
228288
describe('BitgoMpcGpgPubKeys.isBitgoEddsaMpcv2PubKey', function () {
229289
it('should return true for the hardcoded on-prem test EdDSA MPCv2 key', function () {
230290
const key = BitgoMpcGpgPubKeys.bitgoMpcGpgPubKeys['eddsaMpcv2']['onprem']['test'];

modules/sdk-core/src/bitgo/tss/bitgoPubKeys.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,24 @@ export const bitgoMpcGpgPubKeys = {
2424
},
2525
eddsaMpcv2: {
2626
nitro: {
27-
test: '', // TODO WCI-205: add eddsaMpcv2 key for nitro
28-
prod: '',
27+
test: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxjMEae9GZBYJKwYBBAHaRw8BAQdASKNi5MpJRAvINROrOEYFGmJcYKzW3WM7\nkIPow2z0P/bNGGhzbSA8aHNtQHRlc3QuYml0Z28uY29tPsKEBBMWCgA2BYJp\n70Z8AgsJCZDqsFQZg1ASRAIVCgIWAAKbAwIeARYhBLHdfJtdM3kc4o7/wuqw\nVBmDUBJEAAB6WgD/XYI8GR6BZl32N4fu6VZiJ4Ean7ahPCZ+eKYuc8qnJeMB\nAIYqc0Tw9QcqvgrrZtqizUfbYzJDZjNY2R51LrszLFAOwoQEEBMIADYFgmnv\nRnwCCwkJkJsMk/69BgLvAhUKAhYAApsDAh4BFiEE1QB10qpW/55I4JfamwyT\n/r0GAu8AAGVbAP9l4Ffwk1MRYwMSgsXQdJIY8srwSrWaJqFavRZi2tl0iwD+\nJaJdr2DcPP6Pb/zq4kh7YnhjrEgHv2Ta4qTTJtEEq+bOOARp70ZkEgorBgEE\nAZdVAQUBAQdA7gOSvYvpCo8TmgPBHeTqlPDJpohcuPXYMRQ5g228WRgDAQgH\nwoUEGBYKADcFgmnvRnwCCwkJkOqwVBmDUBJEAhUKAhYAA5sECAIeARYhBLHd\nfJtdM3kc4o7/wuqwVBmDUBJEAADrrQD+N+V+lJLtaC3W6E1pHQhr9I+0FsdJ\n5+dQFcacKPls3k0A/0Jp6WA+BUcErw4ahkm4hkbbjbuwRLG9H4PSJfoFgccH\nzjMEae9GZBYJKwYBBAHaRw8BAQdAa3z6M5sueZ+yaWp6jQ5GCqo53k4dkGxg\npj1kB9XTYvTCwEoEGBYKALwFgmnvRnwCCwkJkOqwVBmDUBJEAhUKAhYAApsC\nAh4BhaAEGBYKADYFgmnvRnwCCwkJkA35LiZC8HBmAhUKAhYAApsCAh4BFiEE\n0Xa0VhKDLiNQVIiaDfkuJkLwcGYAAIODAQD+YEcJeL0Wi8jY5bdJXzE8lRkE\noikaQMn3pR6dPtL9SwD/eXJamcMAS6L59dawdD0gwCZn9GlKXmj6FFXEJdcd\nOQoWIQSx3XybXTN5HOKO/8LqsFQZg1ASRAAAiU4A/ji0YOT8ceTgDQn3a5P4\nRz9Fv+OUZxj9hMc8K1lB0N/FAQDgv6gXtzEnpGOjtiwPNuuRynnh9OeXzhgL\nPcQIOmW6DA==\n=FAug\n-----END PGP PUBLIC KEY BLOCK-----\n',
28+
prod: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxjMEae97HxYJKwYBBAHaRw8BAQdAkqY/gBSnHQ0bJv6Yik473lXLulCWdhW2\nyEjNq+YEf1bNC0JpdEdvIE5pdHJvwoQEExYKADYFgmnveyICCwkJkKw4yRSD\nFu2mAhUKAhYAApsDAh4BFiEEropjTBjLvFsKdb8ErDjJFIMW7aYAACjjAQC7\n5Sc9kjLdScHTbzsM4l90i9lyHFdTVbruSYvmZrHQAAEAusZMX2qpIJdOdtB7\nyiXeCEMxBm1PcdfdvDSGnUruRQ7ChAQQEwgANgWCae97IgILCQmQtCPvhcuG\nfK8CFQoCFgACmwMCHgEWIQSH0CHgaKWZ1TGhqs+0I++Fy4Z8rwAAM9QA/Ap9\n6YjnvCrv1nO0XT/mqUgSMirGzF6AIk//AfqC8kKHAPsH5I9fKTz8i8vWo6ES\nDSVilnIaEyV6sczxZ2drsLV0Ms44BGnvex8SCisGAQQBl1UBBQEBB0DzGVFB\n74c3y9lShX5DG8or1nJoyjpmb7+aZ710EO7YQwMBCAfChQQYFgoANwWCae97\nIgILCQmQrDjJFIMW7aYCFQoCFgADmwQIAh4BFiEEropjTBjLvFsKdb8ErDjJ\nFIMW7aYAAHthAQCPwozH6edvb4G6JRLFrX8i0rEqCje1yty9sXcwI6XQdwD/\nTm38FbU3qGPop4tePWBTEQYEhE5ams9MIWyvX5P0TA7OMwRp73sfFgkrBgEE\nAdpHDwEBB0AMfvO8IOn4Y9TEtx7IQeutQkgFKapa+jZlVcSmGm8yBsLASgQY\nFgoAvAWCae97IgILCQmQrDjJFIMW7aYCFQoCFgACmwICHgGFoAQYFgoANgWC\nae97IgILCQmQVobXdL8ULVQCFQoCFgACmwICHgEWIQQ+ApqjNkpq4BvXiLdW\nhtd0vxQtVAAAzL8A/2ZASI7HhAAYTmNXvTBRSkfGlgPrurY9cXnwvaUN3Mss\nAP9dCqsaPx6XY5z4GGpGMLVJx4gOu9kpLIonHnZVI6idDhYhBK6KY0wYy7xb\nCnW/BKw4yRSDFu2mAACiPAEA1lDVNbiG0nZFbgatcFF5xKd0urF0f9CpOfpB\ntrIGGbUBAOD7FkKLVXWGFYICYipOUzzGzpFiMNaJl1qbCpa9m1kN\n=R6r4\n-----END PGP PUBLIC KEY BLOCK-----\n',
2929
},
3030
onprem: {
3131
test: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxjMEad+QCxYJKwYBBAHaRw8BAQdAG0FBM/1JRvo7KlLvhp1Mwi6IWmV3V9xy\nZZcByg0fDQ3NGGhzbSA8aHNtQHRlc3QuYml0Z28uY29tPsKEBBMWCgA2BYJp\n35AMAgsJCZCuV6d9sal31QIVCgIWAAKbAwIeARYhBKSiXcKo4xMT5wwfh65X\np32xqXfVAAA2sAEAgmk543UetoUoOoOvEAhOrRBbF4h6VwcH9cyR9UGSwygA\n/2KJJadiAvaepqFZxyE77rFM7ZfqhRMsoAc2MfslvuQMwoQEEBMIADYFgmnf\nkA0CCwkJkN2vJwOOuE03AhUKAhYAApsDAh4BFiEEjMSQwTRbUtG1fSvR3a8n\nA464TTcAANNVAQD1RTu/bJmPBRvWbvuIiuT1WUxYsSuoXWwki1YImN1gMAD+\nOPU+v056hkdoD8Rcd8D+HhoNlJAbRbZWg/qjxr+S6lLOOARp35AMEgorBgEE\nAZdVAQUBAQdAgqwA9UhQGuseztLr2ZM189pBjrW6sAJ5m6icDYOWMHEDAQgH\nwoUEGBYKADcFgmnfkAwCCwkJkK5Xp32xqXfVAhUKAhYAA5sECAIeARYhBKSi\nXcKo4xMT5wwfh65Xp32xqXfVAAC4uwEAlkVzGDPJYETIV4pXYpCdaeGLBjm9\ny1sRb2nx9ET7m+4BANpb0vKKBrKZTAx/+rINgWoxKPnKPsycOE8bYHY3zKAN\nzjMEad+QDBYJKwYBBAHaRw8BAQdAanwKEY5QEAPafbhM5/BIJZRyLmyNpBTo\ntntTIq0nOt/CwEoEGBYKALwFgmnfkA0CCwkJkK5Xp32xqXfVAhUKAhYAApsC\nAh4BhaAEGBYKADYFgmnfkA0CCwkJkAuXAU6A6KYvAhUKAhYAApsCAh4BFiEE\neaGtxZYsjWFFYrD6C5cBToDopi8AAEp3AQCP4bCSYbhjNJfGnCCOq24DaozR\nUFg0hNlpfSA9NYZ3bwD/fdtV5m5a1QyvcyGEnv37l1H7UGbQlG1Zp8roqZh3\nqwMWIQSkol3CqOMTE+cMH4euV6d9sal31QAAMegA/i3sBuAwvrBsp8ozp7O2\nzQlIbjuvDBMomIXj1rRmgoOkAQDRqsQpAvITd0LMFN2dCqCBIGAeIqznFX0C\nyqvU0m8sCQ==\n=/R3k\n-----END PGP PUBLIC KEY BLOCK-----\n',
32-
prod: '', // TODO WCI-142: add prod eddsaMpcv2 key
32+
prod: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxjMEafDHRxYJKwYBBAHaRw8BAQdA4veo9hvphbH0gq+YFo8AwdtTR6SQN4tJ\nOWiA++eBqMvNBUJpdEdvwoQEExYKADYFgmnwx1UCCwkJkM4UKK9zJ+KPAhUK\nAhYAApsDAh4BFiEEB3Cyce1GqKAWbPdJzhQor3Mn4o8AAKS2AQDSo5ubHHyx\nvlAyRUgiyzVvupK8x1Jl/fPf54CHsWlIfgD+OIi8v+73cUoHTW3YxC3B9aPO\nqzAEV58w76dgZ2qeTgjChAQQEwgANgWCafDHVgILCQmQjcXpcUn3c9gCFQoC\nFgACmwMCHgEWIQS4vN2O383+kvuFGdaNxelxSfdz2AAAoVUA/A6NI89QMuvO\ngtX65BnmHP+D8vBJbwTwazDHpS0H62/SAPwJVaRXrRNkOTzc6bcAxg97sFBw\nhxJFzcFpAY0BaT5ods44BGnwx0gSCisGAQQBl1UBBQEBB0DZ18qpCkv6tiua\nCCf4Ct9Yfas7EilWG8LPNevujNh7EAMBCAfChQQYFgoANwWCafDHVgILCQmQ\nzhQor3Mn4o8CFQoCFgADmwQIAh4BFiEEB3Cyce1GqKAWbPdJzhQor3Mn4o8A\nAPBxAQD9MYbwZ6NxRabvF1lFMOAofAM4N8htqy6T0ZItfIt6KgEA9K3sycqT\nnWEFPCkJ1hl6QeCnp0qjGcbLxLRVGTWwxwrOMwRp8MdIFgkrBgEEAdpHDwEB\nB0DYMGRlbkTErdLqeeroxGghkYpoTNPHmgb5barxfnu2oMLASgQYFgoAvAWC\nafDHVgILCQmQzhQor3Mn4o8CFQoCFgACmwICHgGFoAQYFgoANgWCafDHVgIL\nCQmQU3o2I0o7U2YCFQoCFgACmwICHgEWIQRREfH7DgNjyYJM1c9TejYjSjtT\nZgAAa4ABAIPcFd0PzHQIQiMW/mvk57vQeOTSJmnY+/aXZ58ba3ykAP4oC5Mv\nRlTjUAmQdd67FX/Kmm49ayKKExrbUxMCHopGDBYhBAdwsnHtRqigFmz3Sc4U\nKK9zJ+KPAACetAEA0KpGMATxFZto3zinmPaHJAmc33RShm9mQJ7XkN8eIPMB\nAMK9J0qdW9jR7iOmJvLmULqUEAYWHAKJ1Ey9PWDSmUwM\n=REv+\n-----END PGP PUBLIC KEY BLOCK-----\n',
3333
},
3434
},
3535
};
3636

3737
export function getBitgoMpcGpgPubKey(
3838
env: EnvironmentName,
3939
pubKeyType: 'nitro' | 'onprem',
40-
mpcVersion: 'mpcv1' | 'mpcv2'
40+
mpcVersion: 'mpcv1' | 'mpcv2' | 'eddsaMpcv2'
4141
): string {
4242
assert(
4343
mpcVersion in bitgoMpcGpgPubKeys,
44-
`Invalid mpcVersion in getBitgoMpcGpgPubKey, got: ${mpcVersion}, expected: mpcv1 or mpcv2`
44+
`Invalid mpcVersion in getBitgoMpcGpgPubKey, got: ${mpcVersion}, expected: mpcv1, mpcv2, or eddsaMpcv2`
4545
);
4646
assert(
4747
pubKeyType in bitgoMpcGpgPubKeys[mpcVersion],

modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
8686
public async pickBitgoPubGpgKeyForSigning(
8787
isMpcv2: boolean,
8888
reqId?: IRequestTracer,
89-
enterpriseId?: string
89+
enterpriseId?: string,
90+
isEddsaMpcv2?: boolean
9091
): Promise<openpgp.Key> {
9192
let bitgoGpgPubKey;
9293
try {
@@ -98,7 +99,7 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
9899
armoredKey: getBitgoMpcGpgPubKey(
99100
this.bitgo.getEnv(),
100101
bitgoKeyChain.hsmType === 'nitro' ? 'nitro' : 'onprem',
101-
isMpcv2 ? 'mpcv2' : 'mpcv1'
102+
isEddsaMpcv2 ? 'eddsaMpcv2' : isMpcv2 ? 'mpcv2' : 'mpcv1'
102103
),
103104
});
104105
} catch (e) {
@@ -108,11 +109,20 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
108109
);
109110
// First try to get the key based on feature flags, if that fails, fallback to the default key from constants api.
110111
bitgoGpgPubKey = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(enterpriseId, isMpcv2, reqId)
111-
.then(
112-
async ({ mpcv2PublicKey }) =>
112+
.then(async ({ mpcv2PublicKey, eddsaMpcv2PublicKey }) => {
113+
if (isEddsaMpcv2) {
114+
return eddsaMpcv2PublicKey ?? (await this.getBitgoEddsaMpcv2PublicGpgKey());
115+
}
116+
return (
113117
mpcv2PublicKey ?? (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey())
114-
)
115-
.catch(async (e) => (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey()));
118+
);
119+
})
120+
.catch(async () => {
121+
if (isEddsaMpcv2) {
122+
return this.getBitgoEddsaMpcv2PublicGpgKey();
123+
}
124+
return isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey();
125+
});
116126
} else {
117127
throw new Error(
118128
`Environment "${this.bitgo.getEnv()}" requires a BitGo GPG Pub Key Config in BitGoJS for TSS. Error thrown while getting the key from config: ${e}`
@@ -146,6 +156,18 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
146156
return this.bitgoMPCv2PublicGpgKey;
147157
}
148158

159+
async getBitgoEddsaMpcv2PublicGpgKey(): Promise<openpgp.Key> {
160+
if (!this.bitgoEddsaMpcv2PublicGpgKey) {
161+
// retry getting bitgo's gpg key
162+
await this.setBitgoGpgPubKey(this.bitgo);
163+
if (!this.bitgoEddsaMpcv2PublicGpgKey) {
164+
throw new Error("Failed to get Bitgo's EdDSA MPCv2 gpg key");
165+
}
166+
}
167+
168+
return this.bitgoEddsaMpcv2PublicGpgKey;
169+
}
170+
149171
async createBitgoHeldBackupKeyShare(
150172
userGpgKey: SerializedKeyPair<string>,
151173
enterprise: string | undefined

0 commit comments

Comments
 (0)