Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion modules/bitgo/test/v2/unit/internal/tssUtils/ecdsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@ describe('TSS Ecdsa Utils:', async function () {
],
});
const nockGPGKey = await nockGetBitgoPublicKeyBasedOnFeatureFlags(coinName, 'enterprise_id', nitroGPGKeypair);
const bitgoGpgPublicKey = await tssUtils.getBitgoGpgPubkeyBasedOnFeatureFlags('enterprise_id');
const { mpcv2PublicKey: bitgoGpgPublicKey } = await tssUtils.getBitgoGpgPubkeyBasedOnFeatureFlags(
'enterprise_id'
);
should.equal(nockGPGKey.publicKey, bitgoGpgPublicKey.armor());
});

Expand Down
14 changes: 14 additions & 0 deletions modules/sdk-core/src/bitgo/tss/bitgoPubKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ export const bitgoMpcGpgPubKeys = {
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',
},
},
eddsaMpcv2: {
nitro: {
test: '', // TODO WCI-205: add eddsaMpcv2 key for nitro
prod: '',
},
onprem: {
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',
prod: '', // TODO WCI-142: add prod eddsaMpcv2 key
},
},
};

export function getBitgoMpcGpgPubKey(
Expand Down Expand Up @@ -61,6 +71,10 @@ export function isBitgoMpcPubKey(key: string, mpcvVersion: 'mpcv1' | 'mpcv2'): b
return Object.values(bitgoMpcGpgPubKeys[mpcvVersion]).some((envKeys) => Object.values(envKeys).includes(key));
}

export function isBitgoEddsaMpcv2PubKey(key: string): boolean {
return Object.values(bitgoMpcGpgPubKeys.eddsaMpcv2).some((envKeys) => Object.values(envKeys).includes(key));
}

export function envRequiresBitgoPubGpgKeyConfig(env: EnvironmentName): boolean {
return env === 'prod' || env === 'test' || env === 'staging' || env === 'adminProd' || env === 'adminTest';
}
13 changes: 11 additions & 2 deletions modules/sdk-core/src/bitgo/utils/opengpgUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export type AuthEncMessage = {
* @param {BitGoBase} bitgo BitGo object
* @return {Key} public gpg key
*/
export async function getBitgoGpgPubKey(bitgo: BitGoBase): Promise<{ mpcV1: Key; mpcV2: Key | undefined }> {
export async function getBitgoGpgPubKey(
bitgo: BitGoBase
): Promise<{ mpcV1: Key; mpcV2: Key | undefined; eddsaMpcV2: Key | undefined }> {
const constants = await bitgo.fetchConstants();
if (!constants.mpc || !constants.mpc.bitgoPublicKey) {
throw new Error('Unable to create MPC keys - bitgoPublicKey is missing from constants');
Expand All @@ -45,7 +47,14 @@ export async function getBitgoGpgPubKey(bitgo: BitGoBase): Promise<{ mpcV1: Key;
const bitgoMPCv2PublicKeyStr = constants.mpc.bitgoMPCv2PublicKey
? await readKey({ armoredKey: constants.mpc.bitgoMPCv2PublicKey as string })
: undefined;
return { mpcV1: await readKey({ armoredKey: bitgoPublicKeyStr }), mpcV2: bitgoMPCv2PublicKeyStr };
const bitgoEddsaMpcv2PublicKeyStr = constants.mpc.bitgoEddsaMpcv2PublicKey
? await readKey({ armoredKey: constants.mpc.bitgoEddsaMpcv2PublicKey as string })
: undefined;
return {
mpcV1: await readKey({ armoredKey: bitgoPublicKeyStr }),
mpcV2: bitgoMPCv2PublicKeyStr,
eddsaMpcV2: bitgoEddsaMpcv2PublicKeyStr,
};
}

/**
Expand Down
34 changes: 27 additions & 7 deletions modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
private _wallet?: IWallet;
protected bitgoPublicGpgKey: openpgp.Key;
protected bitgoMPCv2PublicGpgKey: openpgp.Key | undefined;
protected bitgoEddsaMpcv2PublicGpgKey: openpgp.Key | undefined;

constructor(bitgo: BitGoBase, baseCoin: IBaseCoin, wallet?: IWallet) {
super(bitgo, baseCoin);
Expand All @@ -67,7 +68,7 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
}

protected async setBitgoGpgPubKey(bitgo) {
const { mpcV1, mpcV2 } = await getBitgoGpgPubKey(bitgo);
const { mpcV1, mpcV2, eddsaMpcV2 } = await getBitgoGpgPubKey(bitgo);
// Do not unset the MPCv1 key if it is already set. This is to avoid unsetting if extra constants api calls fail.
if (mpcV1 !== undefined) {
this.bitgoPublicGpgKey = mpcV1;
Expand All @@ -76,6 +77,10 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
if (mpcV2 !== undefined) {
this.bitgoMPCv2PublicGpgKey = mpcV2;
}
// Do not unset the EdDSA MPCv2 key if it is already set
if (eddsaMpcV2 !== undefined) {
this.bitgoEddsaMpcv2PublicGpgKey = eddsaMpcV2;
}
}

public async pickBitgoPubGpgKeyForSigning(
Expand Down Expand Up @@ -104,8 +109,8 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
// First try to get the key based on feature flags, if that fails, fallback to the default key from constants api.
bitgoGpgPubKey = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(enterpriseId, isMpcv2, reqId)
.then(
async (pubKey) =>
pubKey ?? (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey())
async ({ mpcv2PublicKey }) =>
mpcv2PublicKey ?? (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey())
)
.catch(async (e) => (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey()));
} else {
Expand Down Expand Up @@ -141,6 +146,17 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
return this.bitgoMPCv2PublicGpgKey;
}

async getBitgoEddsaMpcv2PublicGpgKey(): Promise<openpgp.Key> {
if (!this.bitgoEddsaMpcv2PublicGpgKey) {
// retry getting bitgo's EdDSA MPCv2 gpg key
await this.setBitgoGpgPubKey(this.bitgo);
if (!this.bitgoEddsaMpcv2PublicGpgKey) {
throw new Error("Failed to get Bitgo's EdDSA MPCv2 gpg key");
}
}
return this.bitgoEddsaMpcv2PublicGpgKey;
}

async createBitgoHeldBackupKeyShare(
userGpgKey: SerializedKeyPair<string>,
enterprise: string | undefined
Expand Down Expand Up @@ -552,8 +568,9 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
}

/**
* It gets the appropriate BitGo GPG public key for key creation based on a
* It gets the appropriate BitGo GPG public keys for key creation based on a
* combination of coin and the feature flags on the user and their enterprise if set.
* Returns both the default MPCv2 key and the EdDSA-specific MPCv2 key (if present).
* @param enterpriseId - enterprise under which user wants to create the wallet
* @param isMPCv2 - true to get the MPCv2 GPG public key, defaults to false
* @param reqId - request tracer request id
Expand All @@ -562,16 +579,19 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
enterpriseId: string | undefined,
isMPCv2 = false,
reqId?: IRequestTracer
): Promise<Key> {
): Promise<{ mpcv2PublicKey: Key; eddsaMpcv2PublicKey: Key }> {
const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
const response: BitgoGPGPublicKey = await this.bitgo
.get(this.baseCoin.url('/tss/pubkey'))
.query({ enterpriseId })
.retry(3)
.result();
const bitgoPublicKeyStr = isMPCv2 ? response.mpcv2PublicKey : response.publicKey;
return readKey({ armoredKey: bitgoPublicKeyStr as string });
const mpcv2PublicKey = await readKey({
armoredKey: (isMPCv2 ? response.mpcv2PublicKey : response.publicKey) as string,
});
const eddsaMpcv2PublicKey = await readKey({ armoredKey: response.eddsaMpcv2PublicKey as string });
return { mpcv2PublicKey, eddsaMpcv2PublicKey };
}

/**
Expand Down
1 change: 1 addition & 0 deletions modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ export interface BitgoGPGPublicKey {
name: string;
publicKey: string;
mpcv2PublicKey?: string;
eddsaMpcv2PublicKey?: string;
enterpriseId: string;
}

Expand Down
4 changes: 2 additions & 2 deletions modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ export class EcdsaUtils extends BaseEcdsaUtils {

// Get the BitGo public key based on user/enterprise feature flags
// If it doesn't work, use the default public key from the constants
const bitgoPublicGpgKey =
(await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise)) ?? this.bitgoPublicGpgKey;
const { mpcv2PublicKey: primaryGpgKey } = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise);
const bitgoPublicGpgKey = primaryGpgKey ?? this.bitgoPublicGpgKey;

const bitgoKeychain = await this.createBitgoKeychain({
userGpgKey,
Expand Down
7 changes: 4 additions & 3 deletions modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {

// Get the BitGo public key based on user/enterprise feature flags
// If it doesn't work, use the default public key from the constants
const bitgoPublicGpgKey = (
(await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise, true)) ?? this.bitgoMPCv2PublicGpgKey
).armor();
const { mpcv2PublicKey } = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise, true);
const mpcv2Key = mpcv2PublicKey ?? this.bitgoMPCv2PublicGpgKey;
assert(mpcv2Key, 'Failed to get BitGo MPCv2 GPG public key');
const bitgoPublicGpgKey = mpcv2Key.armor();

if (envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
// Ensure the public key is one of the expected BitGo public keys when in test or prod.
Expand Down
13 changes: 7 additions & 6 deletions modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { EddsaMPSDkg, MPSComms, MPSTypes } from '@bitgo/sdk-lib-mpc';
import { KeychainsTriplet } from '../../../baseCoin';
import { AddKeychainOptions, Keychain, KeyType } from '../../../keychain';
import { envRequiresBitgoPubGpgKeyConfig, isBitgoMpcPubKey } from '../../../tss/bitgoPubKeys';
import { envRequiresBitgoPubGpgKeyConfig, isBitgoEddsaMpcv2PubKey } from '../../../tss/bitgoPubKeys';
import { generateGPGKeyPair } from '../../opengpgUtils';
import { MPCv2PartiesEnum } from '../ecdsa/typesMPCv2';
import { BaseEddsaUtils } from './base';
Expand All @@ -35,14 +35,15 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils {
const backupGpgPublicKey = backupKeyPair.publicKey;
const [backupPk, backupSk] = await MPSComms.extractEd25519KeyPair(backupGpgKey);

// Get the BitGo public key based on user/enterprise feature flags;
// fall back to the hardcoded MPCv2 public key from constants.
const bitgoPublicGpgKey =
(await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise, true)) ?? this.bitgoMPCv2PublicGpgKey;
// Get the BitGo EdDSA MPCv2 public key (ed25519). Using the default mpcv2PublicKey (secp256k1)
// here would cause a WASM "Invalid Input" error, so we require the dedicated eddsaMpcv2PublicKey.
const { eddsaMpcv2PublicKey } = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(params.enterprise, true);
const bitgoPublicGpgKey = eddsaMpcv2PublicKey ?? this.bitgoEddsaMpcv2PublicGpgKey;
assert(bitgoPublicGpgKey, 'Failed to get BitGo EdDSA MPCv2 GPG public key');
const bitgoPublicGpgKeyArmored = bitgoPublicGpgKey.armor();

if (envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
assert(isBitgoMpcPubKey(bitgoPublicGpgKeyArmored, 'mpcv2'), 'Invalid BitGo GPG public key');
assert(isBitgoEddsaMpcv2PubKey(bitgoPublicGpgKeyArmored), 'Invalid BitGo GPG public key');
}

const bitgoKeyObj = await pgp.readKey({ armoredKey: bitgoPublicGpgKeyArmored });
Expand Down
Loading