Skip to content

Commit fbcfcbf

Browse files
feat(sdk-core): add tests for new pick mpc gpg pub key function
TICKET: HSM-432
1 parent f2db18f commit fbcfcbf

File tree

6 files changed

+196
-10
lines changed

6 files changed

+196
-10
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import {
2+
BitgoMpcGpgPubKeys,
3+
common,
4+
ECDSAUtils,
5+
EddsaUtils,
6+
EnvironmentName,
7+
IRequestTracer,
8+
Wallet,
9+
} from '@bitgo/sdk-core';
10+
import { TestBitGo } from '@bitgo/sdk-test';
11+
12+
import { BitGo } from '../../../../../src';
13+
import * as openpgp from 'openpgp';
14+
import nock = require('nock');
15+
import assert = require('assert');
16+
17+
class TestEcdsaMpcv2Utils extends ECDSAUtils.EcdsaMPCv2Utils {
18+
public async testPickBitgoPubGpgKeyForSigning(
19+
isMpcv2: boolean,
20+
reqId?: IRequestTracer,
21+
enterpriseId?: string
22+
): Promise<openpgp.Key> {
23+
return this.pickBitgoPubGpgKeyForSigning(isMpcv2, reqId, enterpriseId);
24+
}
25+
}
26+
27+
class TestEddsaMpcv1Utils extends EddsaUtils {
28+
public async testPickBitgoPubGpgKeyForSigning(
29+
isMpcv2: boolean,
30+
reqId?: IRequestTracer,
31+
enterpriseId?: string
32+
): Promise<openpgp.Key> {
33+
return this.pickBitgoPubGpgKeyForSigning(isMpcv2, reqId, enterpriseId);
34+
}
35+
}
36+
37+
describe('TSS MPC Pick BitGo GPG Pub Key Utils:', function () {
38+
const walletId = '5b34252f1bf349930e34020a00000000';
39+
const enterpriseId = '6449153a6f6bc20006d66771cdbe15d3';
40+
const ecdsaCoinName = 'hteth';
41+
const eddsaCoinName = 'tsol';
42+
const ecdsaWalletData = {
43+
id: walletId,
44+
enterprise: enterpriseId,
45+
coin: ecdsaCoinName,
46+
coinSpecific: {},
47+
multisigType: 'tss',
48+
keys: ['key1', 'key2', 'key3'],
49+
};
50+
const eddsaWalletData = {
51+
id: walletId,
52+
enterprise: enterpriseId,
53+
coin: eddsaCoinName,
54+
coinSpecific: {},
55+
multisigType: 'tss',
56+
keys: ['key1', 'key2', 'key3'],
57+
};
58+
const envs: EnvironmentName[] = ['test', 'staging', 'prod'];
59+
const ecdsaMpcv2Utils: TestEcdsaMpcv2Utils[] = [];
60+
const eddsaMpcv1Utils: TestEddsaMpcv1Utils[] = [];
61+
62+
before(async function () {
63+
nock.cleanAll();
64+
for (const env of envs) {
65+
const bitgoInstance = TestBitGo.decorate(BitGo, { env });
66+
bitgoInstance.initializeTestVars();
67+
let coinInstance = bitgoInstance.coin(ecdsaCoinName);
68+
ecdsaMpcv2Utils.push(
69+
new TestEcdsaMpcv2Utils(bitgoInstance, coinInstance, new Wallet(bitgoInstance, coinInstance, ecdsaWalletData))
70+
);
71+
coinInstance = bitgoInstance.coin(eddsaCoinName);
72+
eddsaMpcv1Utils.push(
73+
new TestEddsaMpcv1Utils(bitgoInstance, coinInstance, new Wallet(bitgoInstance, coinInstance, eddsaWalletData))
74+
);
75+
}
76+
});
77+
78+
beforeEach(async function () {
79+
for (const env of envs) {
80+
const bgUrl = common.Environments[env].uri;
81+
nock(bgUrl).get(`/api/v2/${ecdsaCoinName}/key/key3`).times(envs.length).reply(200, { hsmType: 'onprem' });
82+
nock(bgUrl).get(`/api/v2/${eddsaCoinName}/key/key3`).times(envs.length).reply(200, { hsmType: 'nitro' });
83+
}
84+
});
85+
86+
envs.forEach(async function (env, index) {
87+
it(`should pick correct Mpcv2 BitGo GPG Pub Key for ${env} env`, async function () {
88+
const bitgoGpgPubKey = await ecdsaMpcv2Utils[index].testPickBitgoPubGpgKeyForSigning(true);
89+
bitgoGpgPubKey
90+
.armor()
91+
.should.equal(BitgoMpcGpgPubKeys.bitgoMpcGpgPubKeys['mpcv2']['onprem'][env === 'staging' ? 'test' : env]);
92+
});
93+
});
94+
95+
envs.forEach(async function (env, index) {
96+
it(`should pick correct Mpcv1 BitGo GPG Pub Key for ${env} env`, async function () {
97+
const bitgoGpgPubKey = await eddsaMpcv1Utils[index].testPickBitgoPubGpgKeyForSigning(false);
98+
bitgoGpgPubKey
99+
.armor()
100+
.should.equal(BitgoMpcGpgPubKeys.bitgoMpcGpgPubKeys['mpcv1']['nitro'][env === 'staging' ? 'test' : env]);
101+
});
102+
});
103+
104+
it(`should pick BitGo GPG Pub Key based on enterprise flag for mock env`, async function () {
105+
const bgUrl = common.Environments['mock'].uri;
106+
const testBitgo = TestBitGo.decorate(BitGo, { env: 'mock' });
107+
const testCoin = testBitgo.coin(ecdsaCoinName);
108+
const bitgoGPGKey = await openpgp.generateKey({
109+
userIDs: [
110+
{
111+
name: 'bitgo',
112+
113+
},
114+
],
115+
});
116+
nock(bgUrl)
117+
.get(`/api/v2/${ecdsaCoinName}/tss/pubkey`)
118+
.query({ enterpriseId })
119+
.reply(200, { mpcv2PublicKey: bitgoGPGKey.publicKey });
120+
const ecdsaMpcv2Util = new TestEcdsaMpcv2Utils(
121+
testBitgo,
122+
testCoin,
123+
new Wallet(testBitgo, testCoin, ecdsaWalletData)
124+
);
125+
const bitgoGpgPubKey = await ecdsaMpcv2Util.testPickBitgoPubGpgKeyForSigning(true, undefined, enterpriseId);
126+
bitgoGpgPubKey.armor().should.equal(bitgoGPGKey.publicKey);
127+
});
128+
129+
it(`should pick BitGo GPG Pub Key based on constants api for mock env if enterprise flag based fetch fails`, async function () {
130+
nock.cleanAll();
131+
const bgUrl = common.Environments['mock'].uri;
132+
const testBitgo = TestBitGo.decorate(BitGo, { env: 'mock' });
133+
const testCoin = testBitgo.coin(ecdsaCoinName);
134+
const bitgoGPGKey = await openpgp.generateKey({
135+
userIDs: [
136+
{
137+
name: 'bitgo',
138+
139+
},
140+
],
141+
});
142+
const constants = {
143+
mpc: {
144+
bitgoMPCv2PublicKey: bitgoGPGKey.publicKey,
145+
bitgoPublicKey: bitgoGPGKey.publicKey,
146+
},
147+
};
148+
nock(bgUrl).get('/api/v1/client/constants').times(2).reply(200, { ttl: 3600, constants });
149+
const ecdsaMpcv2Util = new TestEcdsaMpcv2Utils(
150+
testBitgo,
151+
testCoin,
152+
new Wallet(testBitgo, testCoin, ecdsaWalletData)
153+
);
154+
const bitgoGpgPubKey = await ecdsaMpcv2Util.testPickBitgoPubGpgKeyForSigning(true, undefined, enterpriseId);
155+
bitgoGpgPubKey.armor().should.equal(bitgoGPGKey.publicKey);
156+
});
157+
158+
it(`should throw an error if config is not available in one of test, staging, or prod`, async function () {
159+
nock.cleanAll();
160+
const testBitgo = TestBitGo.decorate(BitGo, { env: 'test' });
161+
const testCoin = testBitgo.coin(ecdsaCoinName);
162+
const ecdsaMpcv2Util = new TestEcdsaMpcv2Utils(
163+
testBitgo,
164+
testCoin,
165+
new Wallet(testBitgo, testCoin, ecdsaWalletData)
166+
);
167+
await assert.rejects(async () => await ecdsaMpcv2Util.testPickBitgoPubGpgKeyForSigning(true));
168+
});
169+
});

modules/express/test/unit/clientRoutes/externalSign.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ describe('External signer', () => {
367367
const reqCommitment = {
368368
bitgo: bgTest,
369369
body: {
370+
bitgoGpgPubKey: bitgoGpgKey.public,
370371
txRequest: {
371372
apiVersion: 'full',
372373
walletId: walletID,

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,20 @@ export const bitgoMpcGpgPubKeys = {
2424
},
2525
};
2626

27-
export function getBitgoMpcGpgPubKey(env: EnvironmentName, pubKeyType: 'nitro' | 'onprem', mpcVersion: 'mpcv1' | 'mpcv2'): string {
28-
assert(mpcVersion in bitgoMpcGpgPubKeys, `Invalid mpcVersion in getBitgoMpcGpgPubKey, got: ${mpcVersion}, expected: mpcv1 or mpcv2`);
29-
assert(pubKeyType in bitgoMpcGpgPubKeys[mpcVersion], `Invalid pubKeyType in getBitgoMpcGpgPubKey, got: ${pubKeyType}, expected: nitro or onprem`);
30-
if (env !== 'prod' && env !== 'test' && env !== 'staging' && env !== 'adminProd' && env !== 'adminTest'){
27+
export function getBitgoMpcGpgPubKey(
28+
env: EnvironmentName,
29+
pubKeyType: 'nitro' | 'onprem',
30+
mpcVersion: 'mpcv1' | 'mpcv2'
31+
): string {
32+
assert(
33+
mpcVersion in bitgoMpcGpgPubKeys,
34+
`Invalid mpcVersion in getBitgoMpcGpgPubKey, got: ${mpcVersion}, expected: mpcv1 or mpcv2`
35+
);
36+
assert(
37+
pubKeyType in bitgoMpcGpgPubKeys[mpcVersion],
38+
`Invalid pubKeyType in getBitgoMpcGpgPubKey, got: ${pubKeyType}, expected: nitro or onprem`
39+
);
40+
if (env !== 'prod' && env !== 'test' && env !== 'staging' && env !== 'adminProd' && env !== 'adminTest') {
3141
throw new Error('Invalid environment to get a BitGo MPC GPG public key');
3242
}
3343
if (env !== 'prod' && env !== 'adminProd') {
@@ -37,7 +47,7 @@ export function getBitgoMpcGpgPubKey(env: EnvironmentName, pubKeyType: 'nitro' |
3747
if (env === 'adminProd') {
3848
env = 'prod';
3949
}
40-
if (pubKeyType === 'nitro' && env === 'prod') {
50+
if (pubKeyType === 'nitro' && env === 'prod' && mpcVersion === 'mpcv2') {
4151
throw new Error('Nitro mpcv2 pub key is not available in production environments yet.');
4252
}
4353
if (pubKeyType !== 'nitro') {
@@ -52,5 +62,5 @@ export function isBitgoMpcPubKey(key: string, mpcvVersion: 'mpcv1' | 'mpcv2'): b
5262
}
5363

5464
export function envRequiresBitgoPubGpgKeyConfig(env: EnvironmentName): boolean {
55-
return env === 'prod' || env === 'test' || env === 'staging' || env === 'adminProd' || env === 'adminTest';
65+
return env === 'prod' || env === 'test' || env === 'staging' || env === 'adminProd' || env === 'adminTest';
5666
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ECDSAMethods, { ECDSAMethodTypes, DKLSMethods } from './ecdsa';
33

44
export { EDDSAMethods, EDDSAMethodTypes, ECDSAMethods, ECDSAMethodTypes, DKLSMethods };
55
export { ShareKeyPosition } from './types';
6+
export * as BitgoMpcGpgPubKeys from './bitgoPubKeys';
67

78
// exporting this types for backward compatibility.
89
/** @deprecated Use EDDSAMethods */

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,14 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
6666

6767
protected async setBitgoGpgPubKey(bitgo) {
6868
const { mpcV1, mpcV2 } = await getBitgoGpgPubKey(bitgo);
69-
this.bitgoPublicGpgKey = mpcV1;
70-
this.bitgoMPCv2PublicGpgKey = mpcV2;
69+
// Do not unset the MPCv1 key if it is already set. This is to avoid unsetting if extra constants api calls fail.
70+
if (mpcV1 !== undefined) {
71+
this.bitgoPublicGpgKey = mpcV1;
72+
}
73+
// Do not unset the MPCv2 key if it is already set
74+
if (mpcV2 !== undefined) {
75+
this.bitgoMPCv2PublicGpgKey = mpcV2;
76+
}
7177
}
7278

7379
protected async pickBitgoPubGpgKeyForSigning(

modules/sdk-core/src/bitgo/utils/tss/ecdsa/base.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import * as openpgp from 'openpgp';
21
import { ec } from 'elliptic';
32

43
import { IBaseCoin } from '../../../baseCoin';
54
import baseTSSUtils from '../baseTSSUtils';
65
import { KeyShare } from './types';
76
import { BackupGpgKey } from '../baseTypes';
8-
import { generateGPGKeyPair, getBitgoGpgPubKey, getTrustGpgPubKey } from '../../opengpgUtils';
7+
import { generateGPGKeyPair, getTrustGpgPubKey } from '../../opengpgUtils';
98
import { BitGoBase } from '../../../bitgoBase';
109
import { IWallet } from '../../../wallet';
1110

0 commit comments

Comments
 (0)