Skip to content

Commit 7dda747

Browse files
feat(sdk-core): support hardcoded gpg keys for non-express hot wallet flow
TICKET: HSM-432
1 parent 664bd96 commit 7dda747

File tree

8 files changed

+138
-29
lines changed

8 files changed

+138
-29
lines changed

modules/sdk-core/src/bitgo/keychain/iKeychains.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { IRequestTracer } from '../../api';
22
import { KeychainsTriplet, KeyPair } from '../baseCoin';
3+
import { BitgoPubKeyType } from '../utils/tss/baseTypes';
34
import { BackupProvider, IWallet } from '../wallet';
45
import { BitGoKeyFromOvcShares, OvcToBitGoJSON } from './ovcJsonCodec';
56

@@ -39,6 +40,7 @@ export interface Keychain {
3940
commonKeychain?: string;
4041
keyShares?: ApiKeyShare[];
4142
walletHSMGPGPublicKeySigs?: string;
43+
hsmType?: BitgoPubKeyType;
4244
type: KeyType;
4345
source?: SourceType;
4446
coinSpecific?: { [coinName: string]: unknown };
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import assert from 'assert';
2+
import { EnvironmentName } from '../environments';
3+
4+
export const bitgoMpcGpgPubKeys = {
5+
mpcv1: {
6+
nitro: {
7+
test: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EYqEU5hMFK4EEAAoCAwQDdbAIZrsblEXIavyg2go6p9oG0SqWTgFsdHTc\nBhqdIS/WjQ8pj75q+vLqFtV9hlImYGInsIWh97fsigzB2owyzRhoc20gPGhz\nbUB0ZXN0LmJpdGdvLmNvbT7ChAQTEwgAFQUCYqEU5wILCQIVCAIWAAIbAwIe\nAQAhCRCJNRsIDGunexYhBHRL5D/8nRM3opQnXok1GwgMa6d7tg8A/24A9awq\nSCJx7RddiUzFHcKhVvvo3R5N7bHaOGP3TP79AP0TavF2WzhUXmZSjt3IK23O\n7/aknbijVeq52ghbWb1SwsJ1BBATCAAGBQJioRTnACEJEAWuA35KJgtgFiEE\nZttLPR0KcYvjgvJCBa4DfkomC2BsrwD/Z+43zOw+WpfPHxe+ypyVog5fnOKl\nXwleH6zDvqUWmWkA/iaHC6ullYkSG4Mv68k6qbtgR/pms/X7rkfa0QQFJy5p\nzlMEYqEU5hIFK4EEAAoCAwSsLqmfonjMF3o0nZ5JHvLpmfTA1RIVDsAEoRON\ntZA6rAA23pGl6s3Iyt4/fX9Adzoh3EElOjMsgi8Aj3dFpuqiAwEIB8J4BBgT\nCAAJBQJioRTnAhsMACEJEIk1GwgMa6d7FiEEdEvkP/ydEzeilCdeiTUbCAxr\np3vM7AD9GPp6HhYNEh2VVCDtFSt14Bni5FVM5icpVDo6w9ibvWAA/2Ti3Jv4\nIhIxl81/wqAgqigIblrz6vjtagr9/ykXQCW3\n=skCo\n-----END PGP PUBLIC KEY BLOCK-----\n',
8+
prod: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EY4m6ZBMFK4EEAAoCAwRSTwdXgiY+EBNj2JgNzisUygcVGVxp1Fv+pT64\nTsJ64y9Fr5h9ljqMIsmM0MWn9hczpmdAEHpkSg264wAPNcIWzQtCaXRHbyBO\naXRyb8KEBBMTCAA2BQJjibpmAgsJCRDHgvrWqx65HwIVCAIWAAIbAwIeARYh\nBLgnzI9Cn6UamNlJ2MeC+tarHrkfAABEwgD/W0+LXpHEMtSnShf7rSg7tQfG\n1Bb6be2Y1utd+auj/EcA/jGJO8MtejxcVGBpH/ZrODL+D0yS/I2YD3nveLtD\nD5z3wnUEEBMIACcFAmOJumkJEHuS1voAd5fJFiEE1Xxbfbbr5zLGqNJ7e5LW\n+gB3l8kAAPtmAP0WZnW/cgGCWzG1NYbAU1sJUwYdspM1WDLByjmo5JkCrQD+\nOK/6U8zvmQEcoOq0YXArhb+yWQDDHDEkLxRptB+KO8nOUwRjibpkEgUrgQQA\nCgIDBOUvn/oNKZnjEMtnAbB6hoos8vDf8mqyIbtGRjDil1T3t19q2Ke6xFFo\nJ+U2w4gtFxjDER8igas+ja4P3u7EFlMDAQgHwngEGBMIACoFAmOJumgJEMeC\n+tarHrkfAhsMFiEEuCfMj0KfpRqY2UnYx4L61qseuR8AANHPAP96lvwGT3A0\nNNz1WAr+Sn13mR3k8arfeqcvZ1FCmioMogD9GzJIaJlbAbdsRB4QnLkRcKJO\nnMH13PKq9qM6tg4UQFM=\n=SD0h\n-----END PGP PUBLIC KEY BLOCK-----\n',
9+
},
10+
onprem: {
11+
test: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EYqEU5hMFK4EEAAoCAwQDdbAIZrsblEXIavyg2go6p9oG0SqWTgFsdHTc\nBhqdIS/WjQ8pj75q+vLqFtV9hlImYGInsIWh97fsigzB2owyzRhoc20gPGhz\nbUB0ZXN0LmJpdGdvLmNvbT7ChAQTEwgAFQUCYqEU5wILCQIVCAIWAAIbAwIe\nAQAhCRCJNRsIDGunexYhBHRL5D/8nRM3opQnXok1GwgMa6d7tg8A/24A9awq\nSCJx7RddiUzFHcKhVvvo3R5N7bHaOGP3TP79AP0TavF2WzhUXmZSjt3IK23O\n7/aknbijVeq52ghbWb1SwsJ1BBATCAAGBQJioRTnACEJEAWuA35KJgtgFiEE\nZttLPR0KcYvjgvJCBa4DfkomC2BsrwD/Z+43zOw+WpfPHxe+ypyVog5fnOKl\nXwleH6zDvqUWmWkA/iaHC6ullYkSG4Mv68k6qbtgR/pms/X7rkfa0QQFJy5p\nzlMEYqEU5hIFK4EEAAoCAwSsLqmfonjMF3o0nZ5JHvLpmfTA1RIVDsAEoRON\ntZA6rAA23pGl6s3Iyt4/fX9Adzoh3EElOjMsgi8Aj3dFpuqiAwEIB8J4BBgT\nCAAJBQJioRTnAhsMACEJEIk1GwgMa6d7FiEEdEvkP/ydEzeilCdeiTUbCAxr\np3vM7AD9GPp6HhYNEh2VVCDtFSt14Bni5FVM5icpVDo6w9ibvWAA/2Ti3Jv4\nIhIxl81/wqAgqigIblrz6vjtagr9/ykXQCW3\n=skCo\n-----END PGP PUBLIC KEY BLOCK-----\n',
12+
prod: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmE8EYqKKQRMFK4EEAAoCAwROWJbH3UCPdZTPEJXpPZcktwtDJwil4QHlXZELcUbF\nETboq/cY22w+uG0IlRypdbo6+sDuaeg3dfja2ioq6TtJtAVCaXRHb4iEBBMTCAAV\nBQJioopDAgsJAhUIAhYAAhsDAh4BACEJEFXOMjZat5vMFiEEFYS/Xvdht8iMtmyP\nVc4yNlq3m8ym1AD+P9clE3kj764YmrHDOcRPl/+tX2CoUD0rbdSYyJyfCwAA/As0\nF0UFbPzlxPSaZhV/jQxB+PsF4LViwDdh4V4pUtn9iQIzBBABCgAdFiEEycUshFXI\nDdIAN2jlMSDsLY9HGToFAmKqV5gACgkQMSDsLY9HGToeiA/+MQXgfcLlzvNwHhGJ\nkgpSsW3aj2GOA+lZPHAbLVRomltn5GI/5jtLLi0hdVrNr14yuI1K954+ezuAz2DN\nHfDWVihEQaaQ+54fzoy9w8RjGqL+lhgQa/3BrAvIRP/WPFyo9eZcaW+T35x+Es7Z\nqyK50Sc/vpkHtNJ8rVHW1Y1wwAZzuHqdsY2xp1bKeS1hn2NHJ4QRqXZTNUY2zu0M\n3jzCvFdgnqUJC9d/t92QdUAAsENgAIIrVn7qCHoQIrXKrGAO+DTVmSqJp3kkbDI8\nbhR6EeionE6uualIsOR8bx4FlBHLk5JGCtzLVQ8XO/a7+hbJK8eFGUWSRQWiuq/1\nm11+MEjT6jkYDALajbrwYgQSNnqri9kHnGl1jnkNm7r+1PS9lB+J2F/c/t0jQd1C\nsQwIDmrD9adrs6yczbuEB+1kjt8SzJq8SGGPI84hzubQBhIr15mHNw5PK/1zaXlR\nWI0Sc8gOBcQsZshIRmOAvdhQzjU1uV8rYa19WOxpHB/Bss59Gk8oZsQu5aQ7qQ2l\nFBlUnewgPPIkljTrUBZLHSSExoUsoobOTGjs59VvDS5Jdy7rvA7roY1qYG/Dca2t\n1fwTwYTehI/u98qr+4N4d472vdCOqKhmcnbmSYNH35Ig8640yuhq/0IYKWmL8cBS\nO+5E4uMK4iBxSGFjLpGLRK9McMq4UwRioopBEgUrgQQACgIDBCHnAGs6S2ol7gbm\nxtI53LDWufX/US+Q7iGPxHgLdQArsCnPn31duLqO3ekPauF24vUWdLnOHOJhIGSS\nj8HYjDwDAQgHiHgEGBMIAAkFAmKiikQCGwwAIQkQVc4yNlq3m8wWIQQVhL9e92G3\nyIy2bI9VzjI2WrebzKZXAPwPAp119kb5WBC1evOHnbdHCTSOX4TB1xXaHoCIqhX/\ncAD+NX7JLf4pMeTpDz1MZHZB+2lyTBxRMo432m4Z7366Vqc=\n=yDyI\n-----END PGP PUBLIC KEY BLOCK-----',
13+
},
14+
},
15+
mpcv2: {
16+
nitro: {
17+
test: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZiF3CBMFK4EEAAoCAwQWD7Pa752fAl4z0PxfWVC05d89vfo80PyUQ3Er\nLXlhGLkik+NkAl/DBd8diN7i4kTvRoIo0xrHU+lZgdgt+ct5zRhoc20gPGhz\nbUB0ZXN0LmJpdGdvLmNvbT7ChAQTEwgANgUCZiF3CAILCQkQ5ycuezbbVOkC\nFQgCFgACGwMCHgEWIQRPr6GNiE7tRv0p4afnJy57NttU6QAAbAYA+wRvSLOa\ne0iREOx00HhYWP030GhN98BcZtehT9iTZMV8AP97Otkrtq6jby2f7PdEV7uv\nd4aikTa5BgnpKvl8yqL4ccKEBBATCAA2BQJmIXcKAgsJCRCZRBfch5MUcwIV\nCAIWAAIbAwIeARYhBAmXBS0TYEvmC/3L9JlEF9yHkxRzAABJ1wD+KyI1j9nu\nYWvDxwDB+JBGMt7mic77ajBOgaCabEZ0j1MA/2RCOiV2cOL3x1AOzosqofsh\niA1s9BpS14xAwrKJPwY+zlMEZiF3CBIFK4EEAAoCAwSgLs60kLzhHD3o1sDg\n0fQ/QHw6hgq9PQ5LvilUvuIGYDR79sPwrMuwy7wUcOQgJvwIOJHommDq5nj+\nKfgAtE6uAwEIB8KEBBgTCAA2BQJmIXcJAgsJCRDnJy57NttU6QIVCAIWAAIb\nDAIeARYhBE+voY2ITu1G/Snhp+cnLns221TpAADWmQD/bV9sBkwyYfYfJYTS\nqvTmubCesQDY5Ranv9wYvv7RiLQA/iwX6ZHwdbvQFVui0GrvV2iFaCHut1pn\nF4YCDqpUKidwzk8EZiF3CBMFK4EEAAoCAwTfm/HZxwvubP/rr2KOU88mkDL9\njcWjfQx1uFZ9mlIgMBV3++OgtkVE0eEe+lNWpwgksGOGrBWeQ3K0XRF0YlUp\nwsBKBBgTCAC8BQJmIXcJAgsJCRDnJy57NttU6QIVCAIWAAIbAgIeAYUgBBgT\nCAA2BQJmIXcJAgsJCRBrEMTq2oOYhgIVCAIWAAIbAgIeARYhBLFg1zIcwAmc\nRhGdOmsQxOrag5iGAAAxoAD/YNPhMmf3l4Qh7fprkmOjoU0CvFiiP+kcxTr9\nm9luVhUA/RvhIB4sqrAcSD7ZGVIQcEI14rdAFeok4Higz2cGf9R6FiEET6+h\njYhO7Ub9KeGn5ycuezbbVOkAAPnaAP0dYpya7EzvN5Q6RpIzqLFN9izyGt4Q\n6keZsvnVbW9qJAD9Fj7tAAMUbbstz/Kx9RY8qoIOFTuSwaeDXnJMrI9v84w=\n=uzVB\n-----END PGP PUBLIC KEY BLOCK-----\n',
18+
prod: '',
19+
},
20+
onprem: {
21+
test: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZiF3CBMFK4EEAAoCAwQWD7Pa752fAl4z0PxfWVC05d89vfo80PyUQ3Er\nLXlhGLkik+NkAl/DBd8diN7i4kTvRoIo0xrHU+lZgdgt+ct5zRhoc20gPGhz\nbUB0ZXN0LmJpdGdvLmNvbT7ChAQTEwgANgUCZiF3CAILCQkQ5ycuezbbVOkC\nFQgCFgACGwMCHgEWIQRPr6GNiE7tRv0p4afnJy57NttU6QAAbAYA+wRvSLOa\ne0iREOx00HhYWP030GhN98BcZtehT9iTZMV8AP97Otkrtq6jby2f7PdEV7uv\nd4aikTa5BgnpKvl8yqL4ccKEBBATCAA2BQJmIXcKAgsJCRCZRBfch5MUcwIV\nCAIWAAIbAwIeARYhBAmXBS0TYEvmC/3L9JlEF9yHkxRzAABJ1wD+KyI1j9nu\nYWvDxwDB+JBGMt7mic77ajBOgaCabEZ0j1MA/2RCOiV2cOL3x1AOzosqofsh\niA1s9BpS14xAwrKJPwY+zlMEZiF3CBIFK4EEAAoCAwSgLs60kLzhHD3o1sDg\n0fQ/QHw6hgq9PQ5LvilUvuIGYDR79sPwrMuwy7wUcOQgJvwIOJHommDq5nj+\nKfgAtE6uAwEIB8KEBBgTCAA2BQJmIXcJAgsJCRDnJy57NttU6QIVCAIWAAIb\nDAIeARYhBE+voY2ITu1G/Snhp+cnLns221TpAADWmQD/bV9sBkwyYfYfJYTS\nqvTmubCesQDY5Ranv9wYvv7RiLQA/iwX6ZHwdbvQFVui0GrvV2iFaCHut1pn\nF4YCDqpUKidwzk8EZiF3CBMFK4EEAAoCAwTfm/HZxwvubP/rr2KOU88mkDL9\njcWjfQx1uFZ9mlIgMBV3++OgtkVE0eEe+lNWpwgksGOGrBWeQ3K0XRF0YlUp\nwsBKBBgTCAC8BQJmIXcJAgsJCRDnJy57NttU6QIVCAIWAAIbAgIeAYUgBBgT\nCAA2BQJmIXcJAgsJCRBrEMTq2oOYhgIVCAIWAAIbAgIeARYhBLFg1zIcwAmc\nRhGdOmsQxOrag5iGAAAxoAD/YNPhMmf3l4Qh7fprkmOjoU0CvFiiP+kcxTr9\nm9luVhUA/RvhIB4sqrAcSD7ZGVIQcEI14rdAFeok4Higz2cGf9R6FiEET6+h\njYhO7Ub9KeGn5ycuezbbVOkAAPnaAP0dYpya7EzvN5Q6RpIzqLFN9izyGt4Q\n6keZsvnVbW9qJAD9Fj7tAAMUbbstz/Kx9RY8qoIOFTuSwaeDXnJMrI9v84w=\n=uzVB\n-----END PGP PUBLIC KEY BLOCK-----\n',
22+
prod: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZmHyKBMFK4EEAAoCAwS+tBY/2P47G0mgYRhq90jK475f02f3f3W4VbKA\nSwd9s6aI5spk7GeYsjRvP6rBf4vFIjLj7Ty7K2V03rZPQc8bzQVCaXRHb8KE\nBBMTCAA2BQJmYfIpAgsJCRAKMB4ATA5V7QIVCAIWAAIbAwIeARYhBAIdflLB\nK4deHok+gQowHgBMDlXtAACRpAD/UUbTsFEkjt+CCJmVq2v5l6oocR9hXXkT\nzhRQKQIwSigA/RVvS2RsoZLkaL68GUHLy63XVHtG149pN3BYPwb63EcQwoQE\nEBMIADYFAmZh8ioCCwkJEFlh2DLM6IVNAhUIAhYAAhsDAh4BFiEEsb9f1VA0\n9rOLgFM+WWHYMszohU0AAFC8AP4wH0ndmzCSg2O/a+ZfqW2yA465BFvDM1ij\nvMtCJYSxzAD/RjcfDfkN4Ipjaa2LRuHxfHZbvgCgoOChsJLv4KQLTafOUwRm\nYfIoEgUrgQQACgIDBM+W01KEUaAm8a3hMBWG9EShyNrZxbtv9ryd8JIIxeEb\nEckLTVQvIer3YvDUyjeY/v83VCRdm6H5cahV92sydrIDAQgHwoQEGBMIADYF\nAmZh8ikCCwkJEAowHgBMDlXtAhUIAhYAAhsMAh4BFiEEAh1+UsErh14eiT6B\nCjAeAEwOVe0AAEcSAP9H96t/z9uKe9lAoq2d9Dt3Hrq9eM6sLQ2+cVblngP+\nDQD/dCqHYQzDdsuc9Y3HmWbhCK1Um6ewppkct1v5lmbaJ1bOTwRmYfIoEwUr\ngQQACgIDBJDIofWOLj/JkBFkZDh3a++LNEH8TBNlDZvU7tNfURXWApxV2VAb\nFBKYddN03Q1SBpMR0GkPl42rH7whYdeaEBHCwEoEGBMIALwFAmZh8ioCCwkJ\nEAowHgBMDlXtAhUIAhYAAhsCAh4BhSAEGBMIADYFAmZh8ioCCwkJEGAfBsMT\nFzVjAhUIAhYAAhsCAh4BFiEE2zAGHSaLnswqIvBrYB8GwxMXNWMAANroAP0f\ntFPumKFwQrCf7OMHQWsesrQYpKT6Z65VbewBoGaGigD/UkeeygTtlyzTV2YF\nNAjWAzaQtXWmmzRgnOj0IKub39MWIQQCHX5SwSuHXh6JPoEKMB4ATA5V7QAA\nTjMA/jDSVXJNblr/kSLNFTordgDjKP0nN1aElvFUFh/QEVT0AP9lmf2Fc/o7\nyYOGPPg4OvvU6odrTsuNgljvPqBlaCc2EA==\n=ZLkt\n-----END PGP PUBLIC KEY BLOCK-----\n',
23+
},
24+
},
25+
};
26+
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'){
31+
throw new Error('Invalid environment to get a BitGo MPC GPG public key');
32+
}
33+
if (env !== 'prod' && env !== 'adminProd') {
34+
// default to test gpg keys if not in prod
35+
env = 'test';
36+
}
37+
if (env === 'adminProd') {
38+
env = 'prod';
39+
}
40+
if (pubKeyType === 'nitro' && env === 'prod') {
41+
throw new Error('Nitro mpcv2 pub key is not available in production environments yet.');
42+
}
43+
if (pubKeyType !== 'nitro') {
44+
// This will be the default key type
45+
pubKeyType = 'onprem';
46+
}
47+
return bitgoMpcGpgPubKeys[mpcVersion][pubKeyType][env];
48+
}
49+
50+
export function isBitgoMpcPubKey(key: string, mpcvVersion: 'mpcv1' | 'mpcv2'): boolean {
51+
return Object.values(bitgoMpcGpgPubKeys[mpcvVersion]).some((envKeys) => Object.values(envKeys).includes(key));
52+
}
53+
54+
export function envRequiresBitgoPubGpgKeyConfig(env: EnvironmentName): boolean {
55+
return env === 'prod' || env === 'test' || env === 'staging' || env === 'adminProd' || env === 'adminTest';
56+
}

modules/sdk-core/src/bitgo/utils/mpcUtils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { AddKeychainOptions, Keychain, KeyType } from '../keychain';
99
import { BackupProvider } from '../wallet';
1010
import { encryptText, getBitgoGpgPubKey } from './opengpgUtils';
1111
import { IntentRecipient, PopulatedIntent, PrebuildTransactionWithIntentOptions } from './tss/baseTypes';
12+
import { envRequiresBitgoPubGpgKeyConfig, isBitgoMpcPubKey } from '../tss/bitgoPubKeys';
1213

1314
export interface MpcKeyShare {
1415
publicShare: string;
@@ -52,6 +53,10 @@ export abstract class MpcUtils {
5253
enterprise?: string
5354
): Promise<Keychain> {
5455
const bitgoKey = (await getBitgoGpgPubKey(this.bitgo)).mpcV1;
56+
if (envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
57+
// Ensure the public key is one of the expected BitGo public keys when in test or prod.
58+
assert(isBitgoMpcPubKey(bitgoKey.armor(), 'mpcv1'), 'Invalid BitGo GPG public key');
59+
}
5560
const encUserToBitGoMessage = await encryptText(userKeyShare.privateShare, bitgoKey);
5661
const encBackupToBitGoMessage = await encryptText(backupKeyShare.privateShare, bitgoKey);
5762

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

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IRequestTracer } from '../../../api';
22
import { Key, readKey, SerializedKeyPair } from 'openpgp';
33
import { IBaseCoin, KeychainsTriplet } from '../../baseCoin';
44
import { BitGoBase } from '../../bitgoBase';
5-
import { Keychain } from '../../keychain';
5+
import { Keychain, KeyIndices } from '../../keychain';
66
import { getTxRequest } from '../../tss';
77
import { IWallet, BackupProvider } from '../../wallet';
88
import { MpcUtils } from '../mpcUtils';
@@ -40,12 +40,17 @@ import {
4040
} from './baseTypes';
4141
import { GShare, SignShare } from '../../../account-lib/mpc/tss';
4242
import { RequestTracer } from '../util';
43+
import * as openpgp from 'openpgp';
44+
import { envRequiresBitgoPubGpgKeyConfig, getBitgoMpcGpgPubKey } from '../../tss/bitgoPubKeys';
45+
import { getBitgoGpgPubKey } from '../opengpgUtils';
4346

4447
/**
4548
* BaseTssUtil class which different signature schemes have to extend
4649
*/
4750
export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtils<KeyShare> {
4851
private _wallet?: IWallet;
52+
protected bitgoPublicGpgKey: openpgp.Key;
53+
protected bitgoMPCv2PublicGpgKey: openpgp.Key | undefined;
4954

5055
constructor(bitgo: BitGoBase, baseCoin: IBaseCoin, wallet?: IWallet) {
5156
super(bitgo, baseCoin);
@@ -59,6 +64,63 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
5964
return this._wallet;
6065
}
6166

67+
protected async setBitgoGpgPubKey(bitgo) {
68+
const { mpcV1, mpcV2 } = await getBitgoGpgPubKey(bitgo);
69+
this.bitgoPublicGpgKey = mpcV1;
70+
this.bitgoMPCv2PublicGpgKey = mpcV2;
71+
}
72+
73+
protected async pickBitgoPubGpgKeyForSigning(isMpcv2: boolean, reqId: IRequestTracer, enterpriseId?: string): Promise<openpgp.Key> {
74+
let bitgoGpgPubKey;
75+
try {
76+
const bitgoKeyChain = await this.baseCoin.keychains().get({ id: this.wallet.keyIds()[KeyIndices.BITGO], reqId });
77+
if (!bitgoKeyChain || !bitgoKeyChain.hsmType) {
78+
throw new Error('Missing Bitgo GPG Pub Key Type.');
79+
}
80+
bitgoGpgPubKey = await openpgp.readKey({
81+
armoredKey: getBitgoMpcGpgPubKey(this.bitgo.getEnv(), bitgoKeyChain.hsmType === 'nitro' ? 'nitro' : 'onprem', isMpcv2 ? 'mpcv2' : 'mpcv1'),
82+
});
83+
} catch (e) {
84+
if (!envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
85+
console.warn(
86+
`Unable to get BitGo GPG key based on key data with error: ${e}. Fetching BitGo GPG key based on feature flags.`
87+
);
88+
bitgoGpgPubKey = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(
89+
enterpriseId,
90+
isMpcv2,
91+
reqId
92+
).then(async (pubKey) => pubKey ?? (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey()));
93+
} else {
94+
throw new Error(`Environment "${this.bitgo.getEnv()}" requires a BitGo GPG Pub Key Config in BitGoJS for TSS. Error thrown while getting the key from config: ${e}`);
95+
}
96+
}
97+
return bitgoGpgPubKey;
98+
}
99+
100+
async getBitgoPublicGpgKey(): Promise<openpgp.Key> {
101+
if (!this.bitgoPublicGpgKey) {
102+
// retry getting bitgo's gpg key
103+
await this.setBitgoGpgPubKey(this.bitgo);
104+
if (!this.bitgoPublicGpgKey) {
105+
throw new Error("Failed to get Bitgo's gpg key");
106+
}
107+
}
108+
109+
return this.bitgoPublicGpgKey;
110+
}
111+
112+
async getBitgoMpcv2PublicGpgKey(): Promise<openpgp.Key> {
113+
if (!this.bitgoMPCv2PublicGpgKey) {
114+
// retry getting bitgo's gpg key
115+
await this.setBitgoGpgPubKey(this.bitgo);
116+
if (!this.bitgoMPCv2PublicGpgKey) {
117+
throw new Error("Failed to get Bitgo's gpg key");
118+
}
119+
}
120+
121+
return this.bitgoMPCv2PublicGpgKey;
122+
}
123+
62124
async createBitgoHeldBackupKeyShare(
63125
userGpgKey: SerializedKeyPair<string>,
64126
enterprise: string | undefined

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ export type TSSParamsForMessageWithPrv = TSSParamsForMessage & {
408408
mpcv2PartyId?: 0 | 1;
409409
};
410410

411+
export type BitgoPubKeyType = 'nitro' | 'onprem';
412+
411413
export type TSSParams = {
412414
txRequest: string | TxRequest; // can be either a string or TxRequest
413415
reqId: IRequestTracer;

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

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,12 @@ import { IWallet } from '../../../wallet';
1212
/** @inheritdoc */
1313
export class BaseEcdsaUtils extends baseTSSUtils<KeyShare> {
1414
// We do not have full support for 3-party verification (w/ external source) of key shares and signature shares. There is no 3rd party key service support with this release.
15-
protected bitgoPublicGpgKey: openpgp.Key;
16-
protected bitgoMPCv2PublicGpgKey: openpgp.Key | undefined;
1715

1816
constructor(bitgo: BitGoBase, baseCoin: IBaseCoin, wallet?: IWallet) {
1917
super(bitgo, baseCoin, wallet);
2018
this.setBitgoGpgPubKey(bitgo);
2119
}
2220

23-
private async setBitgoGpgPubKey(bitgo) {
24-
const { mpcV1, mpcV2 } = await getBitgoGpgPubKey(bitgo);
25-
this.bitgoPublicGpgKey = mpcV1;
26-
this.bitgoMPCv2PublicGpgKey = mpcV2;
27-
}
28-
29-
async getBitgoPublicGpgKey(): Promise<openpgp.Key> {
30-
if (!this.bitgoPublicGpgKey) {
31-
// retry getting bitgo's gpg key
32-
await this.setBitgoGpgPubKey(this.bitgo);
33-
if (!this.bitgoPublicGpgKey) {
34-
throw new Error("Failed to get Bitgo's gpg key");
35-
}
36-
}
37-
38-
return this.bitgoPublicGpgKey;
39-
}
40-
4121
/**
4222
* Gets backup pub gpg key string
4323
* if a third party provided then get from trust

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
} from '../baseTypes';
4949
import { BaseEcdsaUtils } from './base';
5050
import { EcdsaMPCv2KeyGenSendFn, KeyGenSenderForEnterprise } from './ecdsaMPCv2KeyGenSender';
51+
import { envRequiresBitgoPubGpgKeyConfig, isBitgoMpcPubKey } from '../../../tss/bitgoPubKeys';
5152

5253
export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
5354
/** @inheritdoc */
@@ -66,6 +67,11 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
6667
const bitgoPublicGpgKey = (
6768
(await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise, true)) ?? this.bitgoMPCv2PublicGpgKey
6869
).armor();
70+
71+
if (envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
72+
// Ensure the public key is one of the expected BitGo public keys when in test or prod.
73+
assert(isBitgoMpcPubKey(bitgoPublicGpgKey, 'mpcv2'), 'Invalid BitGo GPG public key');
74+
}
6975

7076
const userGpgPrvKey: DklsTypes.PartyGpgKey = {
7177
partyId: MPCv2PartiesEnum.USER,
@@ -711,16 +717,12 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
711717
typeof params.txRequest === 'string'
712718
? await getTxRequest(this.bitgo, this.wallet.id(), params.txRequest, params.reqId)
713719
: params.txRequest;
714-
715720
let txOrMessageToSign;
716721
let derivationPath;
717722
let bufferContent;
718-
const [userGpgKey, bitgoGpgPubKey] = await Promise.all([
719-
generateGPGKeyPair('secp256k1'),
720-
this.getBitgoGpgPubkeyBasedOnFeatureFlags(txRequest.enterpriseId, true, params.reqId).then(
721-
(pubKey) => pubKey ?? this.bitgoMPCv2PublicGpgKey
722-
),
723-
]);
723+
const userGpgKey = await generateGPGKeyPair('secp256k1');
724+
const bitgoGpgPubKey = await this.pickBitgoPubGpgKeyForSigning(true, params.reqId, txRequest.enterpriseId);
725+
724726
if (!bitgoGpgPubKey) {
725727
throw new Error('Missing BitGo GPG key for MPCv2');
726728
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ export class EddsaUtils extends baseTSSUtils<KeyShare> {
588588

589589
const bitgoIndex = ShareKeyPosition.BITGO;
590590
const signerShare = signingKey.yShares[bitgoIndex].u + signingKey.yShares[bitgoIndex].chaincode;
591-
const bitgoGpgKey = (await getBitgoGpgPubKey(this.bitgo)).mpcV1;
591+
const bitgoGpgKey = await this.pickBitgoPubGpgKeyForSigning(false, params.reqId, txRequestResolved.enterpriseId);
592592
const userToBitgoEncryptedSignerShare = await encryptText(signerShare, bitgoGpgKey);
593593

594594
const userGpgKey = await generateGPGKeyPair('secp256k1');

0 commit comments

Comments
 (0)