Skip to content
Draft
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
5 changes: 3 additions & 2 deletions modules/abstract-cosmos/src/cosmosCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export class CosmosCoin<CustomMessage = never> extends BaseCoin {
throw new Error('Invalid key format');
}

return await ECDSAUtils.getMpcV2RecoveryKeyShares(userKey, backupKey, walletPassphrase);
return await ECDSAUtils.getMpcV2RecoveryKeyShares(userKey, backupKey, walletPassphrase, this.bitgo);
}

/**
Expand Down Expand Up @@ -491,7 +491,8 @@ export class CosmosCoin<CustomMessage = never> extends BaseCoin {
const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares(
userKey,
backupKey,
params.walletPassphrase
params.walletPassphrase,
this.bitgo
); // baseAddress is not extracted

const MPC = new Ecdsa();
Expand Down
3 changes: 2 additions & 1 deletion modules/abstract-eth/src/abstractEthLikeNewCoins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2250,7 +2250,8 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
const { userKeyShare, backupKeyShare, commonKeyChain } = await ECDSAUtils.getMpcV2RecoveryKeyShares(
userPublicOrPrivateKeyShare,
backupPrivateOrPublicKeyShare,
params.walletPassphrase
params.walletPassphrase,
this.bitgo
);

const { gasLimit, gasPrice } = await this.getGasValues(params);
Expand Down
4 changes: 2 additions & 2 deletions modules/abstract-lightning/src/wallet/lightning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export class LightningWallet implements ILightningWallet {
}
const signature = createMessageSignature(
t.exact(LightningPaymentRequest).encode(params),
this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
await this.wallet.bitgo.decryptAsync({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
);

const paymentIntent: { intent: LightningPaymentIntent } = {
Expand Down Expand Up @@ -390,7 +390,7 @@ export class LightningWallet implements ILightningWallet {
}
const signature = createMessageSignature(
transactionRequestCreate.transactions[0].unsignedTx.serializedTxHex,
this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
await this.wallet.bitgo.decryptAsync({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
);

const transactionRequestWithSignature = (await this.wallet.bitgo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,38 @@ import { getLightningAuthKeychains, ILightningWallet, LightningWallet } from './
import { createMessageSignature, deriveLightningServiceSharedSecret, isLightningCoinName } from '../lightning';
import * as t from 'io-ts';

function encryptWalletUpdateRequest(
async function encryptWalletUpdateRequest(
wallet: sdkcore.IWallet,
params: UpdateLightningWalletClientRequest,
userAuthKeyEncryptedPrv: string
): UpdateLightningWalletEncryptedRequest {
): Promise<UpdateLightningWalletEncryptedRequest> {
const coinName = wallet.coin() as 'tlnbtc' | 'lnbtc';

const requestWithEncryption: Partial<UpdateLightningWalletClientRequest & UpdateLightningWalletEncryptedRequest> = {
...params,
};

const userAuthXprv = wallet.bitgo.decrypt({
const userAuthXprv = await wallet.bitgo.decryptAsync({
password: params.passphrase,
input: userAuthKeyEncryptedPrv,
});

if (params.signerTlsKey) {
requestWithEncryption.encryptedSignerTlsKey = wallet.bitgo.encrypt({
requestWithEncryption.encryptedSignerTlsKey = await wallet.bitgo.encryptAsync({
password: params.passphrase,
input: params.signerTlsKey,
});
}

if (params.signerAdminMacaroon) {
requestWithEncryption.encryptedSignerAdminMacaroon = wallet.bitgo.encrypt({
requestWithEncryption.encryptedSignerAdminMacaroon = await wallet.bitgo.encryptAsync({
password: params.passphrase,
input: params.signerAdminMacaroon,
});
}

if (params.signerMacaroon) {
requestWithEncryption.encryptedSignerMacaroon = wallet.bitgo.encrypt({
requestWithEncryption.encryptedSignerMacaroon = await wallet.bitgo.encryptAsync({
password: deriveLightningServiceSharedSecret(coinName, userAuthXprv).toString('hex'),
input: params.signerMacaroon,
});
Expand Down Expand Up @@ -86,10 +86,10 @@ export async function updateWalletCoinSpecific(
if (!userAuthKeyEncryptedPrv) {
throw new Error(`user auth key is missing encrypted private key`);
}
const updateRequestWithEncryption = encryptWalletUpdateRequest(wallet, params, userAuthKeyEncryptedPrv);
const updateRequestWithEncryption = await encryptWalletUpdateRequest(wallet, params, userAuthKeyEncryptedPrv);
const signature = createMessageSignature(
updateRequestWithEncryption,
wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
await wallet.bitgo.decryptAsync({ password: params.passphrase, input: userAuthKeyEncryptedPrv })
);
const coinSpecific = {
[wallet.coin()]: {
Expand Down
9 changes: 8 additions & 1 deletion modules/abstract-utxo/src/abstractUtxoCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ import {
stringToBufferTryFormats,
} from './transaction/decode';
import { fetchKeychains, toBip32Triple, UtxoKeychain } from './keychains';
import { verifyKeySignature, verifyUserPublicKey } from './verifyKey';
import { verifyKeySignature, verifyUserPublicKey, verifyUserPublicKeyAsync } from './verifyKey';
import { getPolicyForEnv } from './descriptor/validatePolicy';
import { signTransaction } from './transaction/signTransaction';
import { isUtxoWalletData, UtxoWallet } from './wallet';
Expand Down Expand Up @@ -706,6 +706,13 @@ export abstract class AbstractUtxoCoin
return verifyUserPublicKey(this.bitgo, params);
}

/**
* @deprecated - use function verifyUserPublicKeyAsync instead
*/
protected async verifyUserPublicKeyAsync(params: VerifyUserPublicKeyOptions): Promise<boolean> {
return await verifyUserPublicKeyAsync(this.bitgo, params);
}

/**
* @deprecated - use function verifyKeySignature instead
*/
Expand Down
8 changes: 4 additions & 4 deletions modules/abstract-utxo/src/recovery/backupKeyRecovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
BitGoBase,
ErrorNoInputToRecover,
getKrsProvider,
getBip32Keys as getBip32KeysFromSdkCore,
getBip32KeysAsync as getBip32KeysFromSdkCore,
isTriple,
krsProviders,
Triple,
Expand Down Expand Up @@ -410,8 +410,8 @@ export function formatBackupKeyRecoveryResult(
return txInfo;
}

function getBip32Keys(bitgo: BitGoBase, params: RecoverParams): Triple<BIP32> {
const keys = getBip32KeysFromSdkCore(bitgo, params, { requireBitGoXpub: true });
async function getBip32Keys(bitgo: BitGoBase, params: RecoverParams): Promise<Triple<BIP32>> {
const keys = await getBip32KeysFromSdkCore(bitgo, params, { requireBitGoXpub: true });
if (!isTriple(keys)) {
throw new Error(`expected key triple`);
}
Expand Down Expand Up @@ -469,7 +469,7 @@ export async function backupKeyRecovery(
}

// check whether key material and password authenticate the users and return parent keys of all three keys of the wallet
const keys = getBip32Keys(bitgo, params);
const keys = await getBip32Keys(bitgo, params);
const walletKeys = fixedScriptWallet.RootWalletKeys.from({
triple: keys,
derivationPrefixes: [params.userKeyPath || 'm/0/0', 'm/0/0', 'm/0/0'],
Expand Down
4 changes: 2 additions & 2 deletions modules/abstract-utxo/src/recovery/crossChainRecovery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BIP32, CoinName, fixedScriptWallet, address as wasmAddress } from '@bitgo/wasm-utxo';
import { decryptAsync } from '@bitgo/sdk-api';
import { BitGoBase, IWallet, Keychain, Triple, Wallet } from '@bitgo/sdk-core';
import { decrypt } from '@bitgo/sdk-api';

import { AbstractUtxoCoin, TransactionInfo } from '../abstractUtxoCoin';
import { signAndVerifyPsbt } from '../transaction/fixedScript/signTransaction';
Expand Down Expand Up @@ -313,7 +313,7 @@ async function getPrv(xprv?: string, passphrase?: string, wallet?: IWallet | Wal
encryptedPrv = (await (wallet as WalletV1).getEncryptedUserKeychain()).encryptedXprv;
}

return getPrv(decrypt(passphrase, encryptedPrv));
return getPrv(await decryptAsync(passphrase, encryptedPrv));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as utxolib from '@bitgo/utxo-lib';

import { AbstractUtxoCoin, VerifyTransactionOptions } from '../../abstractUtxoCoin';
import { Output, ParsedTransaction } from '../types';
import { verifyCustomChangeKeySignatures, verifyKeySignature, verifyUserPublicKey } from '../../verifyKey';
import { verifyCustomChangeKeySignatures, verifyKeySignature, verifyUserPublicKeyAsync } from '../../verifyKey';
import { getPsbtTxInputs, getTxInputs } from '../fetchInputs';

const debug = buildDebug('bitgo:abstract-utxo:verifyTransaction');
Expand Down Expand Up @@ -80,7 +80,7 @@ export async function verifyTransaction<TNumber extends bigint | number>(
let userPublicKeyVerified = false;
try {
// verify the user public key matches the private key - this will throw if there is no match
userPublicKeyVerified = verifyUserPublicKey(bitgo, {
userPublicKeyVerified = await verifyUserPublicKeyAsync(bitgo, {
userKeychain: keychains.user,
disableNetworking,
txParams,
Expand Down
68 changes: 47 additions & 21 deletions modules/abstract-utxo/src/verifyKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import assert from 'assert';

import buildDebug from 'debug';
import { BIP32, message } from '@bitgo/wasm-utxo';
import { BitGoBase, decryptKeychainPrivateKey, KeyIndices } from '@bitgo/sdk-core';
import { BitGoBase, decryptKeychainPrivateKey, decryptKeychainPrivateKeyAsync, KeyIndices } from '@bitgo/sdk-core';

import { VerifyKeySignaturesOptions, VerifyUserPublicKeyOptions } from './abstractUtxoCoin';
import { ParsedTransaction } from './transaction/types';
Expand Down Expand Up @@ -81,39 +81,65 @@ export function verifyCustomChangeKeySignatures<TNumber extends number | bigint>
return true;
}

function verifyUserPublicKeyWithPrv(
userKeychain: NonNullable<VerifyUserPublicKeyOptions['userKeychain']>,
userPrv: string | undefined,
disableNetworking: boolean | undefined
): boolean {
const userPub = userKeychain.pub;

if (!userPrv) {
const errorMessage = 'user private key unavailable for verification';
if (disableNetworking) {
console.log(errorMessage);
return false;
} else {
throw new Error(errorMessage);
}
}

const userPrivateKey = BIP32.fromBase58(userPrv);
if (userPrivateKey.toBase58() === userPrivateKey.neutered().toBase58()) {
throw new Error('user private key is only public');
}
if (userPrivateKey.neutered().toBase58() !== userPub) {
throw new Error('user private key does not match public key');
}

return true;
}

/**
* Decrypt the wallet's user private key and verify that the claimed public key matches
* TODO: Deprecate in favor of verifyUserPublicKeyAsync once v2 encryption is default.
* Decrypt the wallet's user private key and verify that the claimed public key matches (sync, v1 only).
*/
export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): boolean {
const { userKeychain, txParams, disableNetworking } = params;
if (!userKeychain) {
throw new Error('user keychain is required');
}

const userPub = userKeychain.pub;

let userPrv = userKeychain.prv;
if (!userPrv && txParams.walletPassphrase) {
userPrv = decryptKeychainPrivateKey(bitgo, userKeychain, txParams.walletPassphrase);
}

if (!userPrv) {
const errorMessage = 'user private key unavailable for verification';
if (disableNetworking) {
console.log(errorMessage);
return false;
} else {
throw new Error(errorMessage);
}
} else {
const userPrivateKey = BIP32.fromBase58(userPrv);
if (userPrivateKey.toBase58() === userPrivateKey.neutered().toBase58()) {
throw new Error('user private key is only public');
}
if (userPrivateKey.neutered().toBase58() !== userPub) {
throw new Error('user private key does not match public key');
}
return verifyUserPublicKeyWithPrv(userKeychain, userPrv, disableNetworking);
}

/**
* Async version of verifyUserPublicKey with v2 encrypt/decrypt support.
*/
export async function verifyUserPublicKeyAsync(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): Promise<boolean> {
const { userKeychain, txParams, disableNetworking } = params;
if (!userKeychain) {
throw new Error('user keychain is required');
}

return true;
let userPrv = userKeychain.prv;
if (!userPrv && txParams.walletPassphrase) {
userPrv = await decryptKeychainPrivateKeyAsync(bitgo, userKeychain, txParams.walletPassphrase);
}

return verifyUserPublicKeyWithPrv(userKeychain, userPrv, disableNetworking);
}
4 changes: 2 additions & 2 deletions modules/bitgo/test/v2/unit/keychains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ describe('V2 Keychains', function () {
updateKeychainStub = sandbox.stub().returns({ result: sandbox.stub().resolves() });
sandbox.stub(BitGo.prototype, 'put').returns({ send: updateKeychainStub });
createKeypairStub = sandbox.stub(ofcKeychains, 'create').returns(mockNewKeypair);
encryptionStub = sandbox.stub(BitGo.prototype, 'encrypt').returns('newEncryptedPrv');
encryptionStub = sandbox.stub(BitGo.prototype, 'encryptAsync').resolves('newEncryptedPrv');
});

afterEach(function () {
Expand All @@ -1088,7 +1088,7 @@ describe('V2 Keychains', function () {

await ofcKeychains.rotateKeychain({ id: mockOfcKeychain.id, password: '1234' });
sinon.assert.called(createKeypairStub);
sinon.assert.calledWith(encryptionStub, { input: mockNewKeypair.prv, password: '1234' });
sinon.assert.calledWith(encryptionStub, { input: mockNewKeypair.prv, password: '1234', encryptionVersion: 2 });
sinon.assert.calledWith(updateKeychainStub, {
pub: mockNewKeypair.pub,
encryptedPrv: 'newEncryptedPrv',
Expand Down
18 changes: 9 additions & 9 deletions modules/express/src/clientRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ function handleLogin(req: ExpressApiRouteRequest<'express.v1.login' | 'express.l
return req.bitgo.authenticate(body);
}

function handleDecrypt(req: ExpressApiRouteRequest<'express.v1.decrypt' | 'express.decrypt', 'post'>) {
async function handleDecrypt(req: ExpressApiRouteRequest<'express.v1.decrypt' | 'express.decrypt', 'post'>) {
return {
decrypted: req.bitgo.decrypt(req.body),
decrypted: await req.bitgo.decryptAsync(req.body),
};
}

function handleEncrypt(req: ExpressApiRouteRequest<'express.v1.encrypt' | 'express.encrypt', 'post'>) {
async function handleEncrypt(req: ExpressApiRouteRequest<'express.v1.encrypt' | 'express.encrypt', 'post'>) {
return {
encrypted: req.bitgo.encrypt(req.body),
encrypted: await req.bitgo.encryptAsync(req.body),
};
}

Expand Down Expand Up @@ -424,9 +424,9 @@ async function getEncryptedPrivKey(path: string, walletId: string): Promise<stri
return encryptedPrivKey[walletId];
}

function decryptPrivKey(bg: BitGo, encryptedPrivKey: string, walletPw: string): string {
async function decryptPrivKey(bg: BitGo, encryptedPrivKey: string, walletPw: string): Promise<string> {
try {
return bg.decrypt({ password: walletPw, input: encryptedPrivKey });
return await bg.decryptAsync({ password: walletPw, input: encryptedPrivKey });
} catch (e) {
throw new Error(`Error when trying to decrypt private key: ${e}`);
}
Expand All @@ -449,7 +449,7 @@ export async function handleV2GenerateShareTSS(

const encryptedPrivKey = await getEncryptedPrivKey(signerFileSystemPath, walletId);
const bitgo = req.bitgo;
const privKey = decryptPrivKey(bitgo, encryptedPrivKey, walletPw);
const privKey = await decryptPrivKey(bitgo, encryptedPrivKey, walletPw);
const coin = bitgo.coin(req.decoded.coin);
req.body.prv = privKey;
req.body.walletPassphrase = walletPw;
Expand Down Expand Up @@ -547,7 +547,7 @@ export async function handleV2Sign(req: ExpressApiRouteRequest<'express.v2.coin.

const encryptedPrivKey = await getEncryptedPrivKey(signerFileSystemPath, walletId);
const bitgo = req.bitgo;
let privKey = decryptPrivKey(bitgo, encryptedPrivKey, walletPw);
let privKey = await decryptPrivKey(bitgo, encryptedPrivKey, walletPw);
const coin = bitgo.coin(req.decoded.coin);
if (req.body.derivationSeed) {
privKey = coin.deriveKeyWithSeed({ key: privKey, seed: req.body.derivationSeed }).key;
Expand Down Expand Up @@ -589,7 +589,7 @@ export async function handleV2OFCSignPayloadInExtSigningMode(
const bitgo = req.bitgo;

// decrypt the encrypted private key using the wallet pwd
const privKey = decryptPrivKey(bitgo, encryptedPrivKey, walletPw);
const privKey = await decryptPrivKey(bitgo, encryptedPrivKey, walletPw);

// create a BaseCoin instance for 'ofc'
const coin = bitgo.coin(ofcCoinName);
Expand Down
2 changes: 1 addition & 1 deletion modules/express/src/fetchEncryptedPrivKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function fetchKeys(ids: WalletIds, token: string, accessToken?: str

if (keychain.encryptedPrv === undefined) {
if (typeof credential === 'object') {
const encryptedPrv = bg.encrypt({ password: credential.walletPassword, input: credential.secret });
const encryptedPrv = await bg.encryptAsync({ password: credential.walletPassword, input: credential.secret });
output[id] = encryptedPrv;
} else {
console.warn(`could not find a ${coinName} encrypted user private key for wallet id ${id}, skipping`);
Expand Down
Loading
Loading