Skip to content
Open
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
45 changes: 45 additions & 0 deletions modules/bitgo/test/v2/unit/keychains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,51 @@ describe('V2 Keychains', function () {
keychains.should.deepEqual(stubbedKeychainsTriplet);
});
});

['tsol'].forEach((coin) => {
it('should pass webauthnInfo to createKeychains for EDDSA TSS', async function () {
const webauthnInfo = {
otpDeviceId: 'device-1',
prfSalt: 'salt-1',
passphrase: 'prf-derived-passphrase',
};
const createKeychains = sandbox
.stub(EDDSAUtils.default.prototype, 'createKeychains')
.resolves(stubbedKeychainsTriplet);
await bitgo.coin(coin).keychains().createMpc({
multisigType: 'tss',
passphrase: 'password',
enterprise: 'enterprise',
webauthnInfo,
});
createKeychains.calledOnce.should.be.true();
createKeychains.firstCall.args[0].should.have.property('webauthnInfo', webauthnInfo);
});
});

['tbsc'].forEach((coin) => {
it('should pass webauthnInfo to createKeychains for ECDSA TSS', async function () {
nock(bgUrl).get('/api/v2/tss/settings').reply(200, {
coinSettings: {},
});
const webauthnInfo = {
otpDeviceId: 'device-1',
prfSalt: 'salt-1',
passphrase: 'prf-derived-passphrase',
};
const createKeychains = sandbox
.stub(ECDSAUtils.EcdsaUtils.prototype, 'createKeychains')
.resolves(stubbedKeychainsTriplet);
await bitgo.coin(coin).keychains().createMpc({
multisigType: 'tss',
passphrase: 'password',
enterprise: 'enterprise',
webauthnInfo,
});
createKeychains.calledOnce.should.be.true();
createKeychains.firstCall.args[0].should.have.property('webauthnInfo', webauthnInfo);
});
});
});

describe('Recreate Keychains from MPCV1 to MPCV2', async function () {
Expand Down
4 changes: 4 additions & 0 deletions modules/sdk-core/src/bitgo/keychain/iKeychains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export interface WebauthnInfo {
encryptedPrv: string;
}

import type { WebauthnKeyEncryptionInfo } from '../wallet/iWallets';
export type { WebauthnKeyEncryptionInfo };

export type SourceType = 'bitgo' | 'backup' | 'user' | 'cold';

export type WebauthnFmt = 'none' | 'packed' | 'fido-u2f';
Expand Down Expand Up @@ -189,6 +192,7 @@ export interface CreateMpcOptions {
originalPasscodeEncryptionCode?: string;
enterprise?: string;
retrofit?: DecryptedRetrofitPayload;
webauthnInfo?: WebauthnKeyEncryptionInfo;
encryptionVersion?: EncryptionVersion;
}

Expand Down
1 change: 1 addition & 0 deletions modules/sdk-core/src/bitgo/keychain/keychains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ export class Keychains implements IKeychains {
enterprise: params.enterprise,
originalPasscodeEncryptionCode: params.originalPasscodeEncryptionCode,
retrofit: params.retrofit,
webauthnInfo: params.webauthnInfo,
encryptionVersion: params.encryptionVersion,
});
}
Expand Down
3 changes: 2 additions & 1 deletion modules/sdk-core/src/bitgo/utils/mpcUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import assert from 'assert';
import { decrypt, readMessage, readPrivateKey, SerializedKeyPair } from 'openpgp';
import { IBaseCoin, KeychainsTriplet } from '../baseCoin';
import { BitGoBase } from '../bitgoBase';
import { AddKeychainOptions, Keychain, KeyType } from '../keychain';
import { AddKeychainOptions, Keychain, KeyType, WebauthnKeyEncryptionInfo } from '../keychain';
import { encryptText, getBitgoGpgPubKey } from './opengpgUtils';
import {
IntentRecipient,
Expand Down Expand Up @@ -105,6 +105,7 @@ export abstract class MpcUtils {
passphrase: string;
enterprise?: string;
originalPasscodeEncryptionCode?: string;
webauthnInfo?: WebauthnKeyEncryptionInfo;
}): Promise<KeychainsTriplet>;

/**
Expand Down
3 changes: 2 additions & 1 deletion modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as openpgp from 'openpgp';
import { Key, readKey, SerializedKeyPair } from 'openpgp';
import { IBaseCoin, KeychainsTriplet } from '../../baseCoin';
import { BitGoBase } from '../../bitgoBase';
import { Keychain, KeyIndices } from '../../keychain';
import { Keychain, KeyIndices, WebauthnKeyEncryptionInfo } from '../../keychain';
import { getTxRequest } from '../../tss';
import { IWallet } from '../../wallet';
import { MpcUtils } from '../mpcUtils';
Expand Down Expand Up @@ -216,6 +216,7 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
enterprise?: string | undefined;
originalPasscodeEncryptionCode?: string | undefined;
isThirdPartyBackup?: boolean;
webauthnInfo?: WebauthnKeyEncryptionInfo;
encryptionVersion?: EncryptionVersion;
}): Promise<KeychainsTriplet> {
throw new Error('Method not implemented.');
Expand Down
3 changes: 2 additions & 1 deletion modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Key, SerializedKeyPair } from 'openpgp';
import { EncryptionVersion, IEncryptionSession, IRequestTracer } from '../../../api';
import { KeychainsTriplet, ParsedTransaction, TransactionParams } from '../../baseCoin';
import { ApiKeyShare, Keychain } from '../../keychain';
import { ApiKeyShare, Keychain, WebauthnKeyEncryptionInfo } from '../../keychain';
import { ApiVersion, Memo, WalletType } from '../../wallet';
import { EDDSA, GShare, Signature, SignShare } from '../../../account-lib/mpc/tss';
import { Signature as EcdsaSignature } from '../../../account-lib/mpc/tss/ecdsa/types';
Expand Down Expand Up @@ -482,6 +482,7 @@ export type CreateKeychainParamsBase = {
passphrase?: string;
enterprise?: string;
originalPasscodeEncryptionCode?: string;
webauthnInfo?: WebauthnKeyEncryptionInfo;
encryptionVersion?: EncryptionVersion;
encryptionSession?: IEncryptionSession;
};
Expand Down
24 changes: 21 additions & 3 deletions modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { EcdsaPaillierProof, EcdsaRangeProof, EcdsaTypes, hexToBigInt, minModulu
import { bip32 } from '@bitgo/utxo-lib';

import { ECDSA, Ecdsa } from '../../../../account-lib/mpc/tss';
import { AddKeychainOptions, Keychain, KeyType } from '../../../keychain';
import { AddKeychainOptions, Keychain, KeyType, WebauthnKeyEncryptionInfo } from '../../../keychain';
import ECDSAMethods, { ECDSAMethodTypes } from '../../../tss/ecdsa';
import { KeychainsTriplet } from '../../../baseCoin';
import {
Expand Down Expand Up @@ -106,6 +106,7 @@ export class EcdsaUtils extends BaseEcdsaUtils {
passphrase: string;
enterprise?: string | undefined;
originalPasscodeEncryptionCode?: string | undefined;
webauthnInfo?: WebauthnKeyEncryptionInfo;
}): Promise<KeychainsTriplet> {
const MPC = new Ecdsa();
const m = 2;
Expand Down Expand Up @@ -138,6 +139,7 @@ export class EcdsaUtils extends BaseEcdsaUtils {
bitgoKeychain,
passphrase: params.passphrase,
originalPasscodeEncryptionCode: params.originalPasscodeEncryptionCode,
webauthnInfo: params.webauthnInfo,
});
const backupKeychainPromise = this.createBackupKeychain({
userGpgKey,
Expand Down Expand Up @@ -177,6 +179,7 @@ export class EcdsaUtils extends BaseEcdsaUtils {
bitgoKeychain,
passphrase,
originalPasscodeEncryptionCode,
webauthnInfo,
}: CreateEcdsaKeychainParams): Promise<Keychain> {
if (!passphrase) {
throw new Error('Please provide a wallet passphrase');
Expand All @@ -191,7 +194,8 @@ export class EcdsaUtils extends BaseEcdsaUtils {
backupKeyShare.userHeldKeyShare,
bitgoKeychain,
passphrase,
originalPasscodeEncryptionCode
originalPasscodeEncryptionCode,
webauthnInfo
);
}

Expand Down Expand Up @@ -304,7 +308,8 @@ export class EcdsaUtils extends BaseEcdsaUtils {
backupKeyShare: KeyShare,
bitgoKeychain: Keychain,
passphrase: string,
originalPasscodeEncryptionCode?: string
originalPasscodeEncryptionCode?: string,
webauthnInfo?: WebauthnKeyEncryptionInfo
): Promise<Keychain> {
const bitgoKeyShares = bitgoKeychain.keyShares;
if (!bitgoKeyShares) {
Expand Down Expand Up @@ -399,6 +404,19 @@ export class EcdsaUtils extends BaseEcdsaUtils {
password: passphrase,
}),
originalPasscodeEncryptionCode,
webauthnDevices:
webauthnInfo && recipientIndex === ShareKeyPosition.USER
? [
{
otpDeviceId: webauthnInfo.otpDeviceId,
prfSalt: webauthnInfo.prfSalt,
encryptedPrv: await this.bitgo.encryptAsync({
input: prv,
password: webauthnInfo.passphrase,
}),
},
]
: undefined,
};

const keychains = this.baseCoin.keychains();
Expand Down
27 changes: 24 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 @@ -21,7 +21,7 @@ import {
} from '@bitgo/public-types';

import { Ecdsa } from '../../../../account-lib';
import { AddKeychainOptions, Keychain, KeyType } from '../../../keychain';
import { AddKeychainOptions, Keychain, KeyType, WebauthnKeyEncryptionInfo } from '../../../keychain';
import { DecryptedRetrofitPayload } from '../../../keychain/iKeychains';
import { ECDSAMethodTypes, getTxRequest } from '../../../tss';
import { sendSignatureShareV2, sendTxRequest } from '../../../tss/common';
Expand Down Expand Up @@ -63,6 +63,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
enterprise: string;
originalPasscodeEncryptionCode?: string;
retrofit?: DecryptedRetrofitPayload;
webauthnInfo?: WebauthnKeyEncryptionInfo;
encryptionVersion?: EncryptionVersion;
}): Promise<KeychainsTriplet> {
const { userSession, backupSession } = this.getUserAndBackupSession(2, 3, params.retrofit);
Expand Down Expand Up @@ -329,6 +330,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
userReducedPrivateMaterial,
params.passphrase,
params.originalPasscodeEncryptionCode,
params.webauthnInfo,
encryptionSession
);
const backupKeychainPromise = this.addBackupKeychain(
Expand Down Expand Up @@ -366,6 +368,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
reducedPrivateMaterial?: Buffer,
passphrase?: string,
originalPasscodeEncryptionCode?: string,
webauthnInfo?: WebauthnKeyEncryptionInfo,
encryptionSession?: {
encrypt(plaintext: string): Promise<string>;
decrypt(ciphertext: string): Promise<string>;
Expand All @@ -375,21 +378,23 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
let source: string;
let encryptedPrv: string | undefined = undefined;
let reducedEncryptedPrv: string | undefined = undefined;
let privateMaterialBase64: string | undefined = undefined;
switch (participantIndex) {
case MPCv2PartiesEnum.USER:
case MPCv2PartiesEnum.BACKUP:
source = participantIndex === MPCv2PartiesEnum.USER ? 'user' : 'backup';
assert(privateMaterial, `Private material is required for ${source} keychain`);
assert(reducedPrivateMaterial, `Reduced private material is required for ${source} keychain`);
assert(passphrase, `Passphrase is required for ${source} keychain`);
privateMaterialBase64 = privateMaterial.toString('base64');
if (encryptionSession) {
encryptedPrv = await encryptionSession.encrypt(privateMaterial.toString('base64'));
encryptedPrv = await encryptionSession.encrypt(privateMaterialBase64);
reducedEncryptedPrv = await encryptionSession.encrypt(
btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(reducedPrivateMaterial))))
);
} else {
encryptedPrv = this.bitgo.encrypt({
input: privateMaterial.toString('base64'),
input: privateMaterialBase64,
password: passphrase,
});
// Encrypts the CBOR-encoded ReducedKeyShare (which contains the party's private
Expand Down Expand Up @@ -420,6 +425,19 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
isMPCv2: true,
};

if (webauthnInfo && participantIndex === MPCv2PartiesEnum.USER && privateMaterialBase64) {
recipientKeychainParams.webauthnDevices = [
{
otpDeviceId: webauthnInfo.otpDeviceId,
prfSalt: webauthnInfo.prfSalt,
encryptedPrv: await this.bitgo.encryptAsync({
input: privateMaterialBase64,
password: webauthnInfo.passphrase,
}),
},
];
}

const keychains = this.baseCoin.keychains();
return { ...(await keychains.add(recipientKeychainParams)), reducedEncryptedPrv: reducedEncryptedPrv };
}
Expand Down Expand Up @@ -540,6 +558,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
reducedPrivateMaterial: Buffer,
passphrase: string,
originalPasscodeEncryptionCode?: string,
webauthnInfo?: WebauthnKeyEncryptionInfo,
encryptionSession?: {
encrypt(plaintext: string): Promise<string>;
decrypt(ciphertext: string): Promise<string>;
Expand All @@ -553,6 +572,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
reducedPrivateMaterial,
passphrase,
originalPasscodeEncryptionCode,
webauthnInfo,
encryptionSession
);
}
Expand All @@ -576,6 +596,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils {
reducedPrivateMaterial,
passphrase,
originalPasscodeEncryptionCode,
undefined,
encryptionSession
);
}
Expand Down
17 changes: 16 additions & 1 deletion modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import assert from 'assert';
import * as openpgp from 'openpgp';
import Eddsa, { GShare, SignShare } from '../../../../account-lib/mpc/tss';
import { AddKeychainOptions, CreateBackupOptions, Keychain } from '../../../keychain';
import { AddKeychainOptions, CreateBackupOptions, Keychain, WebauthnKeyEncryptionInfo } from '../../../keychain';
import { verifyWalletSignature } from '../../../tss/eddsa/eddsa';
import { createShareProof, encryptText, generateGPGKeyPair, getBitgoGpgPubKey } from '../../opengpgUtils';
import {
Expand Down Expand Up @@ -129,6 +129,7 @@ export class EddsaUtils extends baseTSSUtils<KeyShare> {
bitgoKeychain,
passphrase,
originalPasscodeEncryptionCode,
webauthnInfo,
encryptionSession,
}: CreateEddsaKeychainParams): Promise<Keychain> {
const MPC = await Eddsa.initialize();
Expand Down Expand Up @@ -195,6 +196,18 @@ export class EddsaUtils extends baseTSSUtils<KeyShare> {
});
}
}
if (webauthnInfo && userKeychainParams.encryptedPrv) {
userKeychainParams.webauthnDevices = [
{
otpDeviceId: webauthnInfo.otpDeviceId,
prfSalt: webauthnInfo.prfSalt,
encryptedPrv: await this.bitgo.encryptAsync({
input: JSON.stringify(userSigningMaterial),
password: webauthnInfo.passphrase,
}),
},
];
}

return await this.baseCoin.keychains().add(userKeychainParams);
}
Expand Down Expand Up @@ -355,6 +368,7 @@ export class EddsaUtils extends baseTSSUtils<KeyShare> {
passphrase?: string;
enterprise?: string;
originalPasscodeEncryptionCode?: string;
webauthnInfo?: WebauthnKeyEncryptionInfo;
encryptionVersion?: EncryptionVersion;
}): Promise<KeychainsTriplet> {
const MPC = await Eddsa.initialize();
Expand Down Expand Up @@ -388,6 +402,7 @@ export class EddsaUtils extends baseTSSUtils<KeyShare> {
bitgoKeychain,
passphrase: params.passphrase,
originalPasscodeEncryptionCode: params.originalPasscodeEncryptionCode,
webauthnInfo: params.webauthnInfo,
encryptionSession,
});
const backupKeychainPromise = this.createBackupKeychain({
Expand Down
Loading
Loading