From 910a4ba5ad4d927c535bb9395b3e836e3c5d8ed2 Mon Sep 17 00:00:00 2001 From: Bahaa Desoky Date: Thu, 2 Jul 2026 09:39:27 -0400 Subject: [PATCH] feat: make encrypt/decrypt async Ticket: WCN-174 --- .../src/abstractEthLikeNewCoins.ts | 10 +- modules/abstract-eth/src/ethLikeToken.ts | 4 +- .../src/wallet/lightning.ts | 4 +- .../src/wallet/selfCustodialLightning.ts | 10 +- .../src/abstractSubstrateCoin.ts | 4 +- modules/abstract-utxo/src/abstractUtxoCoin.ts | 15 +- .../src/impl/btc/inscriptionBuilder.ts | 4 +- .../src/recovery/backupKeyRecovery.ts | 2 +- .../src/recovery/crossChainRecovery.ts | 4 +- .../fixedScript/verifyTransaction.ts | 4 +- modules/abstract-utxo/src/verifyKey.ts | 27 +- .../test/unit/buildSignSendLegacyFormat.ts | 9 +- modules/abstract-utxo/test/unit/keychains.ts | 12 +- .../backupKeyRecoveryUnspentGathering.ts | 18 +- .../test/unit/testSpoofTransaction.ts | 4 +- .../abstract-utxo/test/unit/util/keychains.ts | 26 +- .../abstract-utxo/test/unit/util/nockBitGo.ts | 4 +- .../test/unit/verifyTransaction.ts | 12 +- modules/abstract-utxo/test/unit/wallet.ts | 2 +- modules/abstract-utxo/test/unit/webauthn.ts | 21 +- modules/bitgo/test/encrypt.ts | 33 ++- modules/bitgo/test/unit/bitgo.ts | 52 ++-- modules/bitgo/test/unit/decryptKeychain.ts | 87 ++---- modules/bitgo/test/unit/keychains.ts | 16 +- .../test/v2/unit/internal/tssUtils/ecdsa.ts | 34 ++- .../tssUtils/ecdsaMPCv2/createKeychains.ts | 6 +- .../tssUtils/ecdsaMPCv2/signTxRequest.ts | 6 +- .../test/v2/unit/internal/tssUtils/eddsa.ts | 6 +- .../tssUtils/eddsaMPCv2/createKeychains.ts | 10 +- modules/bitgo/test/v2/unit/keychains.ts | 115 ++++---- .../v2/unit/lightning/lightningWallets.ts | 2 +- .../v2/unit/staking/goStakingWalletCommon.ts | 65 ++--- modules/bitgo/test/v2/unit/wallet.ts | 62 ++-- modules/bitgo/test/v2/unit/wallets.ts | 200 ++++++------- modules/express/encryptedPrivKeys.json | 2 +- modules/express/src/clientRoutes.ts | 8 +- modules/express/src/fetchEncryptedPrivKeys.ts | 2 +- .../src/lightning/lightningSignerRoutes.ts | 6 +- .../clientRoutes/changeKeychainPassword.ts | 10 +- .../test/unit/clientRoutes/externalSign.ts | 8 +- .../express/test/unit/typedRoutes/coinSign.ts | 25 +- .../express/test/unit/typedRoutes/decrypt.ts | 18 +- .../express/test/unit/typedRoutes/encrypt.ts | 24 +- .../unit/typedRoutes/expressWalletUpdate.ts | 16 +- .../test/unit/typedRoutes/generateShareTSS.ts | 18 +- .../test/unit/typedRoutes/lightningPayment.ts | 30 -- .../unit/typedRoutes/ofcExtSignPayload.ts | 14 +- .../test/unit/typedRoutes/signerMacaroon.ts | 18 +- modules/key-card/src/generateQrData.ts | 49 +--- modules/key-card/src/index.ts | 8 +- modules/key-card/test/unit/generateQrData.ts | 71 +++-- .../src/attachPasskeyToWallet.ts | 4 +- .../src/removePasskeyFromWallet.ts | 4 +- .../test/unit/attachPasskeyToWallet.test.ts | 30 +- .../test/unit/removePasskeyFromWallet.test.ts | 4 +- modules/sdk-api/src/bitgoAPI.ts | 220 ++------------ modules/sdk-api/src/encrypt.ts | 37 ++- modules/sdk-api/src/v1/keychains.ts | 4 +- modules/sdk-api/src/v1/travelRule.ts | 54 +--- modules/sdk-api/src/v1/wallet.ts | 14 +- modules/sdk-api/src/v1/wallets.ts | 8 +- modules/sdk-api/test/unit/bitgoAPI.ts | 140 ++++----- modules/sdk-api/test/unit/encrypt.ts | 275 +++++++----------- modules/sdk-api/test/unit/v1/travelRule.ts | 92 +----- modules/sdk-api/test/unit/v1/wallet.ts | 4 +- modules/sdk-coin-ada/src/ada.ts | 4 +- modules/sdk-coin-ada/test/unit/ada.ts | 2 +- modules/sdk-coin-algo/src/algo.ts | 4 +- modules/sdk-coin-algo/test/unit/algo.ts | 39 ++- modules/sdk-coin-avaxc/src/avaxc.ts | 4 +- modules/sdk-coin-dot/src/dot.ts | 4 +- modules/sdk-coin-eos/src/eos.ts | 4 +- modules/sdk-coin-etc/src/etc.ts | 4 +- modules/sdk-coin-eth/src/erc20Token.ts | 4 +- modules/sdk-coin-eth/src/erc721Token.ts | 4 +- modules/sdk-coin-eth/src/eth.ts | 4 +- modules/sdk-coin-eth/test/unit/eth.ts | 28 +- modules/sdk-coin-eth/test/unit/ethWallet.ts | 6 +- modules/sdk-coin-hbar/src/hbar.ts | 4 +- modules/sdk-coin-hbar/test/unit/hbar.ts | 18 +- modules/sdk-coin-iota/src/iota.ts | 4 +- modules/sdk-coin-near/src/near.ts | 4 +- .../test/unit/tokenEnablementValidation.ts | 2 +- modules/sdk-coin-polyx/src/polyx.ts | 4 +- modules/sdk-coin-sol/src/sol.ts | 4 +- modules/sdk-coin-sol/test/unit/sol.ts | 34 +-- modules/sdk-coin-stx/src/stx.ts | 4 +- modules/sdk-coin-sui/src/sui.ts | 4 +- modules/sdk-coin-ton/src/ton.ts | 4 +- modules/sdk-coin-ton/test/unit/ton.ts | 2 +- modules/sdk-coin-trx/src/trx.ts | 10 +- modules/sdk-coin-trx/test/resources.ts | 6 +- modules/sdk-coin-trx/test/unit/trx.ts | 4 +- modules/sdk-coin-xlm/src/getStellarKeys.ts | 4 +- modules/sdk-coin-xlm/test/unit/xlm.ts | 18 +- modules/sdk-coin-xrp/src/xrp.ts | 4 +- modules/sdk-coin-xtz/src/xtz.ts | 2 +- .../sdk-core/src/bitgo/baseCoin/baseCoin.ts | 19 +- .../sdk-core/src/bitgo/baseCoin/iBaseCoin.ts | 3 +- modules/sdk-core/src/bitgo/bitgoBase.ts | 9 +- .../sdk-core/src/bitgo/internal/keycard.ts | 101 +------ .../src/bitgo/keychain/decryptKeychain.ts | 45 +-- .../sdk-core/src/bitgo/keychain/iKeychains.ts | 3 +- .../sdk-core/src/bitgo/keychain/keychains.ts | 63 ++-- .../sdk-core/src/bitgo/recovery/initiate.ts | 61 +--- .../src/bitgo/trading/tradingAccount.ts | 2 +- .../src/bitgo/utils/tss/baseTSSUtils.ts | 8 +- .../src/bitgo/utils/tss/ecdsa/ecdsa.ts | 12 +- .../src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | 71 ++--- .../src/bitgo/utils/tss/eddsa/eddsa.ts | 24 +- .../src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts | 52 ++-- modules/sdk-core/src/bitgo/wallet/iWallet.ts | 6 +- modules/sdk-core/src/bitgo/wallet/wallet.ts | 166 ++--------- modules/sdk-core/src/bitgo/wallet/wallets.ts | 46 +-- .../test/unit/bitgo/recovery/initiate.ts | 128 ++------ .../test/unit/bitgo/trading/tradingAccount.ts | 8 +- .../test/unit/bitgo/utils/tss/baseTSSUtils.ts | 28 +- .../unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts | 18 +- .../unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts | 69 ++--- .../bitgo/wallet/ofcWalletSignTransaction.ts | 6 +- .../bitgo/wallet/walletsEncryptionVersion.ts | 30 +- .../test/unit/bitgo/wallet/walletsWebauthn.ts | 5 +- 122 files changed, 1157 insertions(+), 2180 deletions(-) diff --git a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts index ea3126a6fe..a1adeef45b 100644 --- a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts +++ b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts @@ -1482,7 +1482,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userKey = await this.bitgo.decryptAsync({ + userKey = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -1501,7 +1501,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); @@ -1674,7 +1674,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { let userKeyPrv; try { - userKeyPrv = await this.bitgo.decryptAsync({ + userKeyPrv = await this.bitgo.decrypt({ input: params.encryptedPrv, password: params.walletPassphrase, }); @@ -1753,7 +1753,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { if (params.walletPassphrase) { if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userKeyPrv = await this.bitgo.decryptAsync({ + userKeyPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -2559,7 +2559,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { const walletPassphrase = buildParams.walletPassphrase; const userKeychain = await this.keychains().get({ id: wallet.keyIds()[0] }); - const userPrv = await wallet.getUserPrvAsync({ keychain: userKeychain, walletPassphrase }); + const userPrv = await wallet.getUserPrv({ keychain: userKeychain, walletPassphrase }); const userPrvBuffer = bip32.fromBase58(userPrv).privateKey; if (!userPrvBuffer) { throw new Error('invalid userPrv'); diff --git a/modules/abstract-eth/src/ethLikeToken.ts b/modules/abstract-eth/src/ethLikeToken.ts index 29a17fb4db..997dfe1095 100644 --- a/modules/abstract-eth/src/ethLikeToken.ts +++ b/modules/abstract-eth/src/ethLikeToken.ts @@ -219,7 +219,7 @@ export class EthLikeToken extends AbstractEthLikeNewCoins { // Decrypt private keys from KeyCard values if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userKey = await this.bitgo.decryptAsync({ + userKey = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -239,7 +239,7 @@ export class EthLikeToken extends AbstractEthLikeNewCoins { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/abstract-lightning/src/wallet/lightning.ts b/modules/abstract-lightning/src/wallet/lightning.ts index 60b892bc4c..8198b37533 100644 --- a/modules/abstract-lightning/src/wallet/lightning.ts +++ b/modules/abstract-lightning/src/wallet/lightning.ts @@ -265,7 +265,7 @@ export class LightningWallet implements ILightningWallet { } const signature = createMessageSignature( t.exact(LightningPaymentRequest).encode(params), - await this.wallet.bitgo.decryptAsync({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) + await this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) ); const paymentIntent: { intent: LightningPaymentIntent } = { @@ -390,7 +390,7 @@ export class LightningWallet implements ILightningWallet { } const signature = createMessageSignature( transactionRequestCreate.transactions[0].unsignedTx.serializedTxHex, - await this.wallet.bitgo.decryptAsync({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) + await this.wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) ); const transactionRequestWithSignature = (await this.wallet.bitgo diff --git a/modules/abstract-lightning/src/wallet/selfCustodialLightning.ts b/modules/abstract-lightning/src/wallet/selfCustodialLightning.ts index a8419074ef..88d3739ef5 100644 --- a/modules/abstract-lightning/src/wallet/selfCustodialLightning.ts +++ b/modules/abstract-lightning/src/wallet/selfCustodialLightning.ts @@ -15,13 +15,13 @@ async function encryptWalletUpdateRequest( ...params, }; - const userAuthXprv = await wallet.bitgo.decryptAsync({ + const userAuthXprv = await wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv, }); if (params.signerTlsKey) { - requestWithEncryption.encryptedSignerTlsKey = await wallet.bitgo.encryptAsync({ + requestWithEncryption.encryptedSignerTlsKey = await wallet.bitgo.encrypt({ password: params.passphrase, input: params.signerTlsKey, encryptionVersion: params.encryptionVersion, @@ -29,7 +29,7 @@ async function encryptWalletUpdateRequest( } if (params.signerAdminMacaroon) { - requestWithEncryption.encryptedSignerAdminMacaroon = await wallet.bitgo.encryptAsync({ + requestWithEncryption.encryptedSignerAdminMacaroon = await wallet.bitgo.encrypt({ password: params.passphrase, input: params.signerAdminMacaroon, encryptionVersion: params.encryptionVersion, @@ -37,7 +37,7 @@ async function encryptWalletUpdateRequest( } if (params.signerMacaroon) { - requestWithEncryption.encryptedSignerMacaroon = await wallet.bitgo.encryptAsync({ + requestWithEncryption.encryptedSignerMacaroon = await wallet.bitgo.encrypt({ password: deriveLightningServiceSharedSecret(coinName, userAuthXprv).toString('hex'), input: params.signerMacaroon, encryptionVersion: params.encryptionVersion, @@ -92,7 +92,7 @@ export async function updateWalletCoinSpecific( const updateRequestWithEncryption = await encryptWalletUpdateRequest(wallet, params, userAuthKeyEncryptedPrv); const signature = createMessageSignature( updateRequestWithEncryption, - await wallet.bitgo.decryptAsync({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) + await wallet.bitgo.decrypt({ password: params.passphrase, input: userAuthKeyEncryptedPrv }) ); const coinSpecific = { [wallet.coin()]: { diff --git a/modules/abstract-substrate/src/abstractSubstrateCoin.ts b/modules/abstract-substrate/src/abstractSubstrateCoin.ts index da96e0061c..27de5e020d 100644 --- a/modules/abstract-substrate/src/abstractSubstrateCoin.ts +++ b/modules/abstract-substrate/src/abstractSubstrateCoin.ts @@ -345,7 +345,7 @@ export class SubstrateCoin extends BaseCoin { // Decrypt private keys from KeyCard values let userPrv; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -356,7 +356,7 @@ export class SubstrateCoin extends BaseCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/abstract-utxo/src/abstractUtxoCoin.ts b/modules/abstract-utxo/src/abstractUtxoCoin.ts index d3be6293e1..8c9430ecf4 100644 --- a/modules/abstract-utxo/src/abstractUtxoCoin.ts +++ b/modules/abstract-utxo/src/abstractUtxoCoin.ts @@ -81,7 +81,7 @@ import { assertFixedScriptWalletAddress, generateAddress } from './address/fixed import { ParsedTransaction } from './transaction/types'; import { decodeDescriptorPsbt, decodePsbt, encodeTransaction, stringToBufferTryFormats } from './transaction/decode'; import { fetchKeychains, toBip32Triple, UtxoKeychain } from './keychains'; -import { verifyKeySignature, verifyUserPublicKey, verifyUserPublicKeyAsync } from './verifyKey'; +import { verifyKeySignature, verifyUserPublicKey } from './verifyKey'; import { getPolicyForEnv } from './descriptor/validatePolicy'; import { signTransaction } from './transaction/signTransaction'; import { isUtxoWalletData, UtxoWallet } from './wallet'; @@ -604,17 +604,10 @@ export abstract class AbstractUtxoCoin extends BaseCoin implements Musig2Partici } /** - * @deprecated - use function verifyUserPublicKey instead + * @deprecated - use the exported `verifyUserPublicKey` function instead */ - protected verifyUserPublicKey(params: VerifyUserPublicKeyOptions): boolean { - return verifyUserPublicKey(this.bitgo, params); - } - - /** - * @deprecated - use function verifyUserPublicKeyAsync instead - */ - protected async verifyUserPublicKeyAsync(params: VerifyUserPublicKeyOptions): Promise { - return await verifyUserPublicKeyAsync(this.bitgo, params); + protected async verifyUserPublicKey(params: VerifyUserPublicKeyOptions): Promise { + return await verifyUserPublicKey(this.bitgo, params); } /** diff --git a/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts b/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts index b164d30c6d..2262066d1b 100644 --- a/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts +++ b/modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts @@ -260,7 +260,7 @@ export class InscriptionBuilder implements IInscriptionBuilder { inscriptionData: Buffer ): Promise { const userKeychain = await this.wallet.baseCoin.keychains().get({ id: this.wallet.keyIds()[KeyIndices.USER] }); - const xprv = await this.wallet.getUserPrvAsync({ keychain: userKeychain, walletPassphrase }); + const xprv = await this.wallet.getUserPrv({ keychain: userKeychain, walletPassphrase }); const halfSignedCommitTransaction = (await this.wallet.signTransaction({ prv: xprv, @@ -300,7 +300,7 @@ export class InscriptionBuilder implements IInscriptionBuilder { txPrebuild: PrebuildTransactionResult ): Promise { const userKeychain = await this.wallet.baseCoin.keychains().get({ id: this.wallet.keyIds()[KeyIndices.USER] }); - const prv = await this.wallet.getUserPrvAsync({ keychain: userKeychain, walletPassphrase }); + const prv = await this.wallet.getUserPrv({ keychain: userKeychain, walletPassphrase }); const halfSigned = (await this.wallet.signTransaction({ prv, txPrebuild })) as HalfSignedUtxoTransaction; return this.wallet.submitTransaction({ halfSigned }); diff --git a/modules/abstract-utxo/src/recovery/backupKeyRecovery.ts b/modules/abstract-utxo/src/recovery/backupKeyRecovery.ts index d041049896..e7f0436006 100644 --- a/modules/abstract-utxo/src/recovery/backupKeyRecovery.ts +++ b/modules/abstract-utxo/src/recovery/backupKeyRecovery.ts @@ -3,7 +3,7 @@ import { BitGoBase, ErrorNoInputToRecover, getKrsProvider, - getBip32KeysAsync as getBip32KeysFromSdkCore, + getBip32Keys as getBip32KeysFromSdkCore, isTriple, krsProviders, Triple, diff --git a/modules/abstract-utxo/src/recovery/crossChainRecovery.ts b/modules/abstract-utxo/src/recovery/crossChainRecovery.ts index dc6c5b3a3f..95485453f7 100644 --- a/modules/abstract-utxo/src/recovery/crossChainRecovery.ts +++ b/modules/abstract-utxo/src/recovery/crossChainRecovery.ts @@ -1,5 +1,5 @@ import { BIP32, CoinName, fixedScriptWallet, address as wasmAddress } from '@bitgo/wasm-utxo'; -import { decryptAsync } from '@bitgo/sdk-api'; +import { decrypt } from '@bitgo/sdk-api'; import { BitGoBase, IWallet, Keychain, Triple, Wallet } from '@bitgo/sdk-core'; import { AbstractUtxoCoin, TransactionInfo } from '../abstractUtxoCoin'; @@ -313,7 +313,7 @@ async function getPrv(xprv?: string, passphrase?: string, wallet?: IWallet | Wal encryptedPrv = (await (wallet as WalletV1).getEncryptedUserKeychain()).encryptedXprv; } - return getPrv(await decryptAsync(passphrase, encryptedPrv)); + return getPrv(await decrypt(passphrase, encryptedPrv)); } /** diff --git a/modules/abstract-utxo/src/transaction/fixedScript/verifyTransaction.ts b/modules/abstract-utxo/src/transaction/fixedScript/verifyTransaction.ts index eff4f13df3..0b33787585 100644 --- a/modules/abstract-utxo/src/transaction/fixedScript/verifyTransaction.ts +++ b/modules/abstract-utxo/src/transaction/fixedScript/verifyTransaction.ts @@ -7,7 +7,7 @@ import { hasPsbtMagic } from '@bitgo/wasm-utxo'; import { AbstractUtxoCoin, VerifyTransactionOptions } from '../../abstractUtxoCoin'; import { ParsedTransaction } from '../types'; import { stringToBufferTryFormats } from '../decode'; -import { verifyCustomChangeKeySignatures, verifyKeySignature, verifyUserPublicKeyAsync } from '../../verifyKey'; +import { verifyCustomChangeKeySignatures, verifyKeySignature, verifyUserPublicKey } from '../../verifyKey'; const debug = buildDebug('bitgo:abstract-utxo:verifyTransaction'); @@ -80,7 +80,7 @@ export async function verifyTransaction( let userPublicKeyVerified = false; try { // verify the user public key matches the private key - this will throw if there is no match - userPublicKeyVerified = await verifyUserPublicKeyAsync(bitgo, { + userPublicKeyVerified = await verifyUserPublicKey(bitgo, { userKeychain: keychains.user, disableNetworking, txParams, diff --git a/modules/abstract-utxo/src/verifyKey.ts b/modules/abstract-utxo/src/verifyKey.ts index 6f9ce94b0e..0c9d0352c0 100644 --- a/modules/abstract-utxo/src/verifyKey.ts +++ b/modules/abstract-utxo/src/verifyKey.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import buildDebug from 'debug'; import { BIP32, message } from '@bitgo/wasm-utxo'; -import { BitGoBase, decryptKeychainPrivateKey, decryptKeychainPrivateKeyAsync, KeyIndices } from '@bitgo/sdk-core'; +import { BitGoBase, decryptKeychainPrivateKey, KeyIndices } from '@bitgo/sdk-core'; import { VerifyKeySignaturesOptions, VerifyUserPublicKeyOptions } from './abstractUtxoCoin'; import { ParsedTransaction } from './transaction/types'; @@ -110,10 +110,10 @@ function verifyUserPublicKeyWithPrv( } /** - * 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). + * Decrypt the wallet's user private key and verify that the claimed public key matches. + * Supports both v1 (SJCL) and v2 (Argon2id) envelopes. */ -export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): boolean { +export async function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): Promise { const { userKeychain, txParams, disableNetworking } = params; if (!userKeychain) { throw new Error('user keychain is required'); @@ -121,24 +121,7 @@ export function verifyUserPublicKey(bitgo: BitGoBase, params: VerifyUserPublicKe let userPrv = userKeychain.prv; if (!userPrv && txParams.walletPassphrase) { - userPrv = decryptKeychainPrivateKey(bitgo, userKeychain, txParams.walletPassphrase); - } - - return verifyUserPublicKeyWithPrv(userKeychain, userPrv, disableNetworking); -} - -/** - * Async version of verifyUserPublicKey with v2 encrypt/decrypt support. - */ -export async function verifyUserPublicKeyAsync(bitgo: BitGoBase, params: VerifyUserPublicKeyOptions): Promise { - const { userKeychain, txParams, disableNetworking } = params; - if (!userKeychain) { - throw new Error('user keychain is required'); - } - - let userPrv = userKeychain.prv; - if (!userPrv && txParams.walletPassphrase) { - userPrv = await decryptKeychainPrivateKeyAsync(bitgo, userKeychain, txParams.walletPassphrase); + userPrv = await decryptKeychainPrivateKey(bitgo, userKeychain, txParams.walletPassphrase); } return verifyUserPublicKeyWithPrv(userKeychain, userPrv, disableNetworking); diff --git a/modules/abstract-utxo/test/unit/buildSignSendLegacyFormat.ts b/modules/abstract-utxo/test/unit/buildSignSendLegacyFormat.ts index a6b064904b..8205874161 100644 --- a/modules/abstract-utxo/test/unit/buildSignSendLegacyFormat.ts +++ b/modules/abstract-utxo/test/unit/buildSignSendLegacyFormat.ts @@ -24,7 +24,7 @@ const keyDocumentObjects = rootWalletKeys.triple.map((bip32, keyIdx) => ({ id: getSeed(keychainsBase58[keyIdx].pub).toString('hex'), pub: bip32.neutered().toBase58(), source: ['user', 'backup', 'bitgo'][keyIdx], - encryptedPrv: encryptKeychain(walletPassphrase, keychainsBase58[keyIdx]), + encryptedPrv: '', coinSpecific: {}, })); @@ -43,7 +43,12 @@ describe('prebuildAndSign-returnLegacyFormat', function () { let recipient: { address: string; amount: string }; const fee = BigInt(10000); - before(function () { + before(async function () { + await Promise.all( + keyDocumentObjects.map(async (doc, keyIdx) => { + doc.encryptedPrv = await encryptKeychain(walletPassphrase, keychainsBase58[keyIdx]); + }) + ); const outputAmount = BigInt(inputScripts.length) * BigInt(1e8) - fee; const outputScriptType: utxolib.bitgo.outputScripts.ScriptType = 'p2sh'; const outputChain = utxolib.bitgo.getExternalChainCode(outputScriptType); diff --git a/modules/abstract-utxo/test/unit/keychains.ts b/modules/abstract-utxo/test/unit/keychains.ts index 6bfaaa95cf..9ec7e8b680 100644 --- a/modules/abstract-utxo/test/unit/keychains.ts +++ b/modules/abstract-utxo/test/unit/keychains.ts @@ -47,26 +47,26 @@ describe('Audit Key', function () { }); }); - it('should throw error if the walletPassphrase is incorrect', function () { - assert.throws( + it('should throw error if the walletPassphrase is incorrect', async function () { + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: btcBackupKey.key, walletPassphrase: 'foo', }), - { message: "failed to decrypt prv: password error - ccm: tag doesn't match" } + { message: 'failed to decrypt prv: incorrect password' } ); }); - it('should throw if the key is altered', function () { + it('should throw if the key is altered', async function () { const alteredKey = btcBackupKey.key.replace(/[0-9]/g, '0'); - assert.throws( + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: alteredKey, walletPassphrase: 'kAm[EFQ6o=SxlcLFDw%,', }), - { message: 'failed to decrypt prv: json decrypt: invalid parameters' } + { message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON' } ); }); }); diff --git a/modules/abstract-utxo/test/unit/recovery/backupKeyRecoveryUnspentGathering.ts b/modules/abstract-utxo/test/unit/recovery/backupKeyRecoveryUnspentGathering.ts index 6d915f1086..a217228b8e 100644 --- a/modules/abstract-utxo/test/unit/recovery/backupKeyRecoveryUnspentGathering.ts +++ b/modules/abstract-utxo/test/unit/recovery/backupKeyRecoveryUnspentGathering.ts @@ -41,8 +41,8 @@ type NamedKeys = { // Get default wasm wallet keys (xpubs, xprivs, and walletKeys) const { walletKeys: wasmWalletKeys, xpubs, xprivs } = getDefaultWasmWalletKeys(); -function getNamedKeys(keys: Triple, password: string): NamedKeys { - function encode(k: BIP32): string { +async function getNamedKeys(keys: Triple, password: string): Promise { + async function encode(k: BIP32): Promise { const base58 = k.toBase58(); // Check if it's a public key const pubKeyMatch = keychainsBase58.find((kc) => kc.pub === base58); @@ -57,17 +57,17 @@ function getNamedKeys(keys: Triple, password: string): NamedKeys { return encryptKeychain(password, keyBase58); } return { - userKey: encode(keys[0]), - backupKey: encode(keys[1]), - bitgoKey: encode(keys[2]), + userKey: await encode(keys[0]), + backupKey: await encode(keys[1]), + bitgoKey: await encode(keys[2]), }; } -function getKeysForFullSignedRecovery(password: string): NamedKeys { +function getKeysForFullSignedRecovery(password: string): Promise { return getNamedKeys([xprivs[0], xprivs[1], xpubs[2]], password); } -const keysFullSignedRecovery = getKeysForFullSignedRecovery(walletPassphrase); +let keysFullSignedRecovery: NamedKeys; /** * Tests for unspent gathering via backupKeyRecovery with MockRecoveryProvider. @@ -77,6 +77,10 @@ const keysFullSignedRecovery = getKeysForFullSignedRecovery(walletPassphrase); describe('Backup Key Recovery - Unspent Gathering', function () { const defaultFeeRateSatB = 100; + before(async function () { + keysFullSignedRecovery = await getKeysForFullSignedRecovery(walletPassphrase); + }); + getMinUtxoCoins().forEach((coin) => { describe(`Unspent Gathering [${coin.getChain()}]`, function () { const { walletKeys: externalWallet } = createWasmWalletKeys('external'); diff --git a/modules/abstract-utxo/test/unit/testSpoofTransaction.ts b/modules/abstract-utxo/test/unit/testSpoofTransaction.ts index ac51335553..9924847214 100644 --- a/modules/abstract-utxo/test/unit/testSpoofTransaction.ts +++ b/modules/abstract-utxo/test/unit/testSpoofTransaction.ts @@ -44,7 +44,7 @@ describe('Transaction Spoofability Tests', function () { return [200, { txid: 'test-txid-123', status: 'signed' }]; }); - nockWalletKeys(wallet, keyTriple, 'pass'); + await nockWalletKeys(wallet, keyTriple, 'pass'); await assert.rejects( wallet.consolidateUnspents({ walletPassphrase: 'pass' }), @@ -92,7 +92,7 @@ describe('Transaction Spoofability Tests', function () { return [200, { txid: 'test-txid-123', status: 'signed' }]; }); - nockWalletKeys(wallet, keyTriple, 'pass'); + await nockWalletKeys(wallet, keyTriple, 'pass'); await assert.rejects( wallet.fanoutUnspents({ walletPassphrase: 'pass' }), diff --git a/modules/abstract-utxo/test/unit/util/keychains.ts b/modules/abstract-utxo/test/unit/util/keychains.ts index a7f9ad5ee4..be852ee97c 100644 --- a/modules/abstract-utxo/test/unit/util/keychains.ts +++ b/modules/abstract-utxo/test/unit/util/keychains.ts @@ -29,17 +29,19 @@ export function toKeychainBase58(k: BIP32Interface): KeychainBase58 { }; } -export function toKeychainObjects(rootWalletKeys: RootWalletKeys, walletPassphrase: string): KeyDoc[] { - return rootWalletKeys.triple.map((bip32, keyIdx) => { - const pub = bip32.neutered().toBase58(); - return { - id: getSeed(pub).toString('hex'), - pub, - source: KeyNames[keyIdx], - encryptedPrv: encrypt(walletPassphrase, bip32.toBase58()), - coinSpecific: {}, - }; - }); +export async function toKeychainObjects(rootWalletKeys: RootWalletKeys, walletPassphrase: string): Promise { + return Promise.all( + rootWalletKeys.triple.map(async (bip32, keyIdx) => { + const pub = bip32.neutered().toBase58(); + return { + id: getSeed(pub).toString('hex'), + pub, + source: KeyNames[keyIdx], + encryptedPrv: await encrypt(walletPassphrase, bip32.toBase58()), + coinSpecific: {}, + }; + }) + ); } export const KeyNames = ['user', 'backup', 'bitgo']; @@ -81,7 +83,7 @@ export function getDefaultWalletUnspentSigner(): bitgo.WalletUnspentSigner { return encrypt(password, keychain.prv); } diff --git a/modules/abstract-utxo/test/unit/util/nockBitGo.ts b/modules/abstract-utxo/test/unit/util/nockBitGo.ts index eec60d50d5..8a32d879ae 100644 --- a/modules/abstract-utxo/test/unit/util/nockBitGo.ts +++ b/modules/abstract-utxo/test/unit/util/nockBitGo.ts @@ -14,11 +14,11 @@ export function nockBitGo(bitgo = defaultBitGo): nock.Scope { * Mock the key fetching endpoints for a wallet. * Sets up nock to return the key triple with the user key encrypted. */ -export function nockWalletKeys(wallet: Wallet, keyTriple: Triple, userPassphrase: string): void { +export async function nockWalletKeys(wallet: Wallet, keyTriple: Triple, userPassphrase: string): Promise { const [user] = keyTriple; const pubs = keyTriple.map((k) => k.neutered().toBase58()); const responses = [ - { pub: pubs[0], encryptedPrv: encrypt(userPassphrase, user.toBase58()) }, + { pub: pubs[0], encryptedPrv: await encrypt(userPassphrase, user.toBase58()) }, { pub: pubs[1] }, { pub: pubs[2] }, ]; diff --git a/modules/abstract-utxo/test/unit/verifyTransaction.ts b/modules/abstract-utxo/test/unit/verifyTransaction.ts index 70c0ef9946..9437beb1fd 100644 --- a/modules/abstract-utxo/test/unit/verifyTransaction.ts +++ b/modules/abstract-utxo/test/unit/verifyTransaction.ts @@ -33,10 +33,7 @@ describe('Verify Transaction', function () { // user public key swapped out user: { pub: otherKeychain.pub, - encryptedPrv: defaultBitGo.encrypt({ - input: userKeychain.prv, - password: passphrase, - }), + encryptedPrv: '' as string, // set in before() }, }, needsCustomChangeKeySignatureVerification: true, @@ -79,6 +76,13 @@ describe('Verify Transaction', function () { }, }; + before(async () => { + stubData.parseTransactionData.badKey.keychains.user.encryptedPrv = await defaultBitGo.encrypt({ + input: userKeychain.prv, + password: passphrase, + }); + }); + const unsignedSendingWallet = sinon.createStubInstance(Wallet, stubData.unsignedSendingWallet as any); it('should fail if the user private key cannot be verified to match the user public key', async () => { diff --git a/modules/abstract-utxo/test/unit/wallet.ts b/modules/abstract-utxo/test/unit/wallet.ts index 4313a70237..7e51a59dcf 100644 --- a/modules/abstract-utxo/test/unit/wallet.ts +++ b/modules/abstract-utxo/test/unit/wallet.ts @@ -20,7 +20,7 @@ describe('manage unspents', function () { before(async function () { rootWalletKey = getDefaultWalletKeys(); walletPassphrase = 'fixthemoneyfixtheworld'; - keysObj = toKeychainObjects(rootWalletKey, walletPassphrase); + keysObj = await toKeychainObjects(rootWalletKey, walletPassphrase); basecoin = getUtxoCoin('tbtc'); const walletData = { id: '5b34252f1bf349930e34020a', diff --git a/modules/abstract-utxo/test/unit/webauthn.ts b/modules/abstract-utxo/test/unit/webauthn.ts index ec6b47c581..3dd4f8e50f 100644 --- a/modules/abstract-utxo/test/unit/webauthn.ts +++ b/modules/abstract-utxo/test/unit/webauthn.ts @@ -17,29 +17,34 @@ describe('webauthn passphrase decryption', function () { id: getSeed(keychainsBase58[0].pub).toString('hex'), pub: keychainsBase58[0].pub, type: 'independent', - encryptedPrv: encryptKeychain(regularPassphrase, keychainsBase58[0]), + encryptedPrv: '', webauthnDevices: [ { otpDeviceId: '123', authenticatorInfo: { credID: 'credID', fmt: 'packed', publicKey: 'some value' }, prfSalt: '456', - encryptedPrv: encryptKeychain(webauthnPassphrase, keychainsBase58[0]), + encryptedPrv: '', }, ], }; - it('should decrypt with the regular passphrase', function () { - const prv = wallet.getUserPrv({ keychain, walletPassphrase: regularPassphrase }); + before(async function () { + keychain.encryptedPrv = await encryptKeychain(regularPassphrase, keychainsBase58[0]); + keychain.webauthnDevices![0].encryptedPrv = await encryptKeychain(webauthnPassphrase, keychainsBase58[0]); + }); + + it('should decrypt with the regular passphrase', async function () { + const prv = await wallet.getUserPrv({ keychain, walletPassphrase: regularPassphrase }); assert.strictEqual(prv, keychainsBase58[0].prv); }); - it('should fall back to webauthn device when the regular passphrase fails', function () { - const prv = wallet.getUserPrv({ keychain, walletPassphrase: webauthnPassphrase }); + it('should fall back to webauthn device when the regular passphrase fails', async function () { + const prv = await wallet.getUserPrv({ keychain, walletPassphrase: webauthnPassphrase }); assert.strictEqual(prv, keychainsBase58[0].prv); }); - it('should throw when all passphrases are wrong', function () { - assert.throws(() => wallet.getUserPrv({ keychain, walletPassphrase: 'wrong' }), { + it('should throw when all passphrases are wrong', async function () { + await assert.rejects(() => wallet.getUserPrv({ keychain, walletPassphrase: 'wrong' }), { message: 'failed to decrypt user keychain', }); }); diff --git a/modules/bitgo/test/encrypt.ts b/modules/bitgo/test/encrypt.ts index c43300e0a8..43b8b32dc8 100644 --- a/modules/bitgo/test/encrypt.ts +++ b/modules/bitgo/test/encrypt.ts @@ -1,7 +1,7 @@ /** * @prettier */ -import should = require('should'); +import 'should'; import { randomBytes } from 'crypto'; import { decrypt, encrypt, bytesToWord } from '@bitgo/sdk-api'; @@ -62,10 +62,11 @@ describe('encrypt, decrypt', function () { const passwords = Array.from({ length: 2 }).map((_, i) => `key/${i}`); const plaintexts = Array.from({ length: 2 }).map((_, i) => `plaintext/${i}`); - it('matches fixture', function () { - const ciphertext = encrypt(passwords[0], plaintexts[0], { + it('matches fixture', async function () { + const ciphertext = await encrypt(passwords[0], plaintexts[0], { salt: getSeed(`randomSalt`).slice(0, 8), iv: getSeed(`randomIV`).slice(0, 16), + encryptionVersion: 1, }); ciphertext.should.eql( '{"iv":"BVDN1IpOeJ6E5kSV88MsHA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"aJjlH+mKW1E=","ct":"loJEsFuypKZMZ+igqCUmbwQfMw=="}' @@ -84,23 +85,23 @@ describe('encrypt, decrypt', function () { }); }); - it('encrypts and decrypts', function () { - passwords.forEach((password) => { - plaintexts.forEach((plaintext) => { - const ciphertext1 = encrypt(password, plaintext); - const ciphertext2 = encrypt(password, plaintext); + it('encrypts and decrypts', async function () { + for (const password of passwords) { + for (const plaintext of plaintexts) { + const ciphertext1 = await encrypt(password, plaintext); + const ciphertext2 = await encrypt(password, plaintext); (ciphertext1 === ciphertext2).should.eql(false); - [ciphertext1, ciphertext2].forEach((ct) => { - passwords.forEach((otherPassword) => { + for (const ct of [ciphertext1, ciphertext2]) { + for (const otherPassword of passwords) { if (password === otherPassword) { - decrypt(otherPassword, ct).should.eql(plaintext); + (await decrypt(otherPassword, ct)).should.eql(plaintext); } else { - should.throws(() => decrypt(otherPassword, ct), /ccm: tag doesn't match/); + await decrypt(otherPassword, ct).should.be.rejected(); } - }); - }); - }); - }); + } + } + } + } }); }); diff --git a/modules/bitgo/test/unit/bitgo.ts b/modules/bitgo/test/unit/bitgo.ts index d8e0251cbf..c668521987 100644 --- a/modules/bitgo/test/unit/bitgo.ts +++ b/modules/bitgo/test/unit/bitgo.ts @@ -193,18 +193,18 @@ describe('BitGo Prototype Methods', function () { const password = 'mickey mouse'; const secret = 'this is a secret'; - it('invalid password', () => { + it('invalid password', async () => { const bitgo = TestBitGo.decorate(BitGo); bitgo.initializeTestVars(); - const opaque = bitgo.encrypt({ password: password, input: secret }); - (() => bitgo.decrypt({ password: 'hack hack', input: opaque })).should.throw(); + const opaque = await bitgo.encrypt({ password: password, input: secret }); + await bitgo.decrypt({ password: 'hack hack', input: opaque }).should.be.rejected(); }); - it('valid password', () => { + it('valid password', async () => { const bitgo = TestBitGo.decorate(BitGo); bitgo.initializeTestVars(); - const opaque = bitgo.encrypt({ password: password, input: secret }); - bitgo.decrypt({ password: password, input: opaque }).should.equal(secret); + const opaque = await bitgo.encrypt({ password: password, input: secret }); + (await bitgo.decrypt({ password: password, input: opaque })).should.equal(secret); }); }); @@ -239,7 +239,7 @@ describe('BitGo Prototype Methods', function () { it('should fail to split secret with wrong m', async () => { await bitgo - .splitSecretAsync({ + .splitSecret({ seed, passwords: ['abc'], m: 0, @@ -249,7 +249,7 @@ describe('BitGo Prototype Methods', function () { it('should fail to split secret with bad password count', async () => { await bitgo - .splitSecretAsync({ + .splitSecret({ seed, passwords: ['abc'], m: 2, @@ -258,11 +258,11 @@ describe('BitGo Prototype Methods', function () { }); it('should split and fail to reconstitute secret with bad passwords', async () => { - const splitSecret = await bitgo.splitSecretAsync({ seed, passwords: passwords, m: 3 }); + const splitSecret = await bitgo.splitSecret({ seed, passwords: passwords, m: 3 }); const shards = _.at(splitSecret.seedShares, [0, 2]); const subsetPasswords = _.at(passwords, [0, 3]); await bitgo - .reconstituteSecretAsync({ + .reconstituteSecret({ shards, passwords: subsetPasswords, xpub, @@ -312,10 +312,10 @@ describe('BitGo Prototype Methods', function () { const passwords = ['mickey', 'mouse', 'donald', 'duck']; it('should split and reconstitute secret using async methods', async () => { - const splitSecret = await bitgo.splitSecretAsync({ seed, passwords: passwords, m: 2 }); + const splitSecret = await bitgo.splitSecret({ seed, passwords: passwords, m: 2 }); const shards = _.at(splitSecret.seedShares, [0, 2]); const subsetPasswords = _.at(passwords, [0, 2]); - const reconstitutedSeed = await bitgo.reconstituteSecretAsync({ shards, passwords: subsetPasswords }); + const reconstitutedSeed = await bitgo.reconstituteSecret({ shards, passwords: subsetPasswords }); reconstitutedSeed.seed.should.equal(seed); reconstitutedSeed.xpub.should.equal( 'xpub661MyMwAqRbcEusRjkJ64BXgR8ddYsXbuDJfbRc3eZcZVEa2ygswDiFZQpHFsA5N211YDvi2N898h4KrcXcfsR8PLhjJaPUwCUqg1ptBBHN' @@ -326,21 +326,21 @@ describe('BitGo Prototype Methods', function () { }); it('should split and incorrectly verify secret using async methods', async () => { - const splitSecret = await bitgo.splitSecretAsync({ seed, passwords: passwords, m: 3 }); - const isValid = await bitgo.verifyShardsAsync({ shards: splitSecret.seedShares, passwords, m: 2 } as any); + const splitSecret = await bitgo.splitSecret({ seed, passwords: passwords, m: 3 }); + const isValid = await bitgo.verifyShards({ shards: splitSecret.seedShares, passwords, m: 2 } as any); isValid.should.equal(false); }); it('should split and verify secret using async methods', async () => { - const splitSecret = await bitgo.splitSecretAsync({ seed, passwords: passwords, m: 2 }); - const isValid = await bitgo.verifyShardsAsync({ shards: splitSecret.seedShares, passwords, m: 2, xpub }); + const splitSecret = await bitgo.splitSecret({ seed, passwords: passwords, m: 2 }); + const isValid = await bitgo.verifyShards({ shards: splitSecret.seedShares, passwords, m: 2, xpub }); isValid.should.equal(true); }); it('should split and verify secret with many parts using async methods', async () => { const allPws = ['0', '1', '2', '3', '4', '5', '6', '7']; - const splitSecret = await bitgo.splitSecretAsync({ seed, passwords: allPws, m: 3 }); - const isValid = await bitgo.verifyShardsAsync({ shards: splitSecret.seedShares, passwords: allPws, m: 3, xpub }); + const splitSecret = await bitgo.splitSecret({ seed, passwords: allPws, m: 3 }); + const isValid = await bitgo.verifyShards({ shards: splitSecret.seedShares, passwords: allPws, m: 3, xpub }); isValid.should.equal(true); }); }); @@ -426,10 +426,10 @@ describe('BitGo Prototype Methods', function () { .reply(200, { version: 1, keychains: { - xpub11: bitgo.encrypt({ input: 'xprv11', password: oldPassword }), - xpub12: bitgo.encrypt({ input: 'xprv12', password: oldPassword }), - xpub13: bitgo.encrypt({ input: 'xprv13', password: otherPassword }), - xpub14: bitgo.encrypt({ input: 'xprv14', password: oldPassword }), + xpub11: await bitgo.encrypt({ input: 'xprv11', password: oldPassword }), + xpub12: await bitgo.encrypt({ input: 'xprv12', password: oldPassword }), + xpub13: await bitgo.encrypt({ input: 'xprv13', password: otherPassword }), + xpub14: await bitgo.encrypt({ input: 'xprv14', password: oldPassword }), }, }); @@ -440,11 +440,11 @@ describe('BitGo Prototype Methods', function () { keys: [ { pub: 'xpub21', - encryptedPrv: bitgo.encrypt({ input: 'xprv21', password: oldPassword }), + encryptedPrv: await bitgo.encrypt({ input: 'xprv21', password: oldPassword }), }, { pub: 'xpub22', - encryptedPrv: bitgo.encrypt({ input: 'xprv22', password: otherPassword }), + encryptedPrv: await bitgo.encrypt({ input: 'xprv22', password: otherPassword }), }, ], }); @@ -495,7 +495,7 @@ describe('BitGo Prototype Methods', function () { ); }); - it('should correctly handle authentication response using handleTokenIssuanceAsync', async () => { + it('should correctly handle authentication response using handleTokenIssuance', async () => { const responseJson = { encryptedToken: '{"iv":"EqxVaGTLY4naAYkuBaTz0w==","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"4S4dBYcgL4s=","ct":"FgBRJljb8iSYxnAjMi4Qotr7sTKbSmWnlfHZShMSi8YeeE3kiS8bpHNUwAPhY8tgouh3UsEwrJnY+54MvqFD7yd19pG1V4CVssr8"}', @@ -503,7 +503,7 @@ describe('BitGo Prototype Methods', function () { encryptedECDHXprv: '{"iv":"QKHEF2GNcwOJwy6+pwANRA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"W2sVFvXDlOw=","ct":"8BTCqS25X37kLzmzQdGenhXH6znn9qEmkszAeS8kLnRdqKSiUiC7bTAVgg/Np5yrV7F7Jyiq+MTpVT76EoUT+PMJzArv0gUQKC2JPB3JuVKeAAVWBQmhWfkEwRfyv4hq4WMxwZtocwBqThvd2pJm9HE51GX4/Wo="}', }; - const parsedAuthenticationData = await bitgo.handleTokenIssuanceAsync(responseJson, 'test@bitgo.com'); + const parsedAuthenticationData = await bitgo.handleTokenIssuance(responseJson, 'test@bitgo.com'); parsedAuthenticationData.token.should.equal(token); parsedAuthenticationData.ecdhXprv.should.equal( 'xprv9s21ZrQH143K3si1bKGp7KqgCQv39ttQ7aUwWzVdytgHd8HtDCHyEp14mxfhiT3qHTq4BaSrA7uUkG6AJTfPJBsRu63drvBqYuMZyTxepH7' diff --git a/modules/bitgo/test/unit/decryptKeychain.ts b/modules/bitgo/test/unit/decryptKeychain.ts index fed109039c..ebb193acc1 100644 --- a/modules/bitgo/test/unit/decryptKeychain.ts +++ b/modules/bitgo/test/unit/decryptKeychain.ts @@ -1,9 +1,5 @@ import 'should'; -import { - decryptKeychainPrivateKey, - decryptKeychainPrivateKeyAsync, - OptionalKeychainEncryptedKey, -} from '@bitgo/sdk-core'; +import { decryptKeychainPrivateKey, OptionalKeychainEncryptedKey } from '@bitgo/sdk-core'; import { BitGoAPI } from '@bitgo/sdk-api'; describe('decryptKeychainPrivateKey', () => { @@ -15,34 +11,16 @@ describe('decryptKeychainPrivateKey', () => { const prv2 = Math.random().toString(); const password2 = Math.random().toString(); - it('should decrypt encryptedPrv', () => { + it('should decrypt encryptedPrv', async () => { const keychain: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: password1 }), + encryptedPrv: await bitgo.encrypt({ input: prv1, password: password1 }), }; - decryptKeychainPrivateKey(bitgo, keychain, password1)!.should.equal(prv1); - }); - - it('should decrypt webauthnDevices encryptedPrv', () => { - const keychain: OptionalKeychainEncryptedKey = { - webauthnDevices: [ - { - otpDeviceId: '123', - authenticatorInfo: { - credID: 'credID', - fmt: 'packed', - publicKey: 'some value', - }, - prfSalt: '456', - encryptedPrv: bitgo.encrypt({ input: prv2, password: password2 }), - }, - ], - }; - decryptKeychainPrivateKey(bitgo, keychain, password2)!.should.equal(prv2); + const result = await decryptKeychainPrivateKey(bitgo, keychain, password1); + result!.should.equal(prv1); }); - it('should try and decrypt all encryptedPrvs', () => { + it('should decrypt webauthnDevices encryptedPrv', async () => { const keychain: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: password1 }), webauthnDevices: [ { otpDeviceId: '123', @@ -52,16 +30,17 @@ describe('decryptKeychainPrivateKey', () => { publicKey: 'some value', }, prfSalt: '456', - encryptedPrv: bitgo.encrypt({ input: prv2, password: password2 }), + encryptedPrv: await bitgo.encrypt({ input: prv2, password: password2 }), }, ], }; - decryptKeychainPrivateKey(bitgo, keychain, password2)!.should.equal(prv2); + const result = await decryptKeychainPrivateKey(bitgo, keychain, password2); + result!.should.equal(prv2); }); - it('should return undefined if no encryptedPrv can be decrypted', () => { + it('should try and decrypt all encryptedPrvs', async () => { const keychain: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: password1 }), + encryptedPrv: await bitgo.encrypt({ input: prv1, password: password1 }), webauthnDevices: [ { otpDeviceId: '123', @@ -71,37 +50,17 @@ describe('decryptKeychainPrivateKey', () => { publicKey: 'some value', }, prfSalt: '456', - encryptedPrv: bitgo.encrypt({ input: prv2, password: password2 }), + encryptedPrv: await bitgo.encrypt({ input: prv2, password: password2 }), }, ], }; - (decryptKeychainPrivateKey(bitgo, keychain, Math.random().toString()) === undefined).should.equal(true); - }); - - it('should return undefined if no encryptedPrv is present', () => { - (decryptKeychainPrivateKey(bitgo, {}, 'password') === undefined).should.be.true(); - }); -}); - -describe('decryptKeychainPrivateKeyAsync', () => { - const bitgo = new BitGoAPI(); - - const prv1 = Math.random().toString(); - const password1 = Math.random().toString(); - - const prv2 = Math.random().toString(); - const password2 = Math.random().toString(); - - it('should decrypt encryptedPrv (v1)', async () => { - const keychain: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: password1 }), - }; - const result = await decryptKeychainPrivateKeyAsync(bitgo, keychain, password1); - result!.should.equal(prv1); + const result = await decryptKeychainPrivateKey(bitgo, keychain, password2); + result!.should.equal(prv2); }); - it('should decrypt webauthnDevices encryptedPrv (v1)', async () => { + it('should return undefined if no encryptedPrv can be decrypted', async () => { const keychain: OptionalKeychainEncryptedKey = { + encryptedPrv: await bitgo.encrypt({ input: prv1, password: password1 }), webauthnDevices: [ { otpDeviceId: '123', @@ -111,24 +70,16 @@ describe('decryptKeychainPrivateKeyAsync', () => { publicKey: 'some value', }, prfSalt: '456', - encryptedPrv: bitgo.encrypt({ input: prv2, password: password2 }), + encryptedPrv: await bitgo.encrypt({ input: prv2, password: password2 }), }, ], }; - const result = await decryptKeychainPrivateKeyAsync(bitgo, keychain, password2); - result!.should.equal(prv2); - }); - - it('should return undefined if no encryptedPrv can be decrypted', async () => { - const keychain: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: password1 }), - }; - const result = await decryptKeychainPrivateKeyAsync(bitgo, keychain, Math.random().toString()); + const result = await decryptKeychainPrivateKey(bitgo, keychain, Math.random().toString()); (result === undefined).should.equal(true); }); it('should return undefined if no encryptedPrv is present', async () => { - const result = await decryptKeychainPrivateKeyAsync(bitgo, {}, 'password'); + const result = await decryptKeychainPrivateKey(bitgo, {}, 'password'); (result === undefined).should.be.true(); }); }); diff --git a/modules/bitgo/test/unit/keychains.ts b/modules/bitgo/test/unit/keychains.ts index ddff248879..a817dbfd1b 100644 --- a/modules/bitgo/test/unit/keychains.ts +++ b/modules/bitgo/test/unit/keychains.ts @@ -2,7 +2,6 @@ // Test for Keychains // -import * as _ from 'lodash'; import 'should'; import nock = require('nock'); @@ -47,28 +46,31 @@ describe('Keychains', function v2keychains() { const newPassword = 'newPassword'; const otherPassword = 'otherPassword'; + const encryptedXprv1 = await bitgo.encrypt({ input: 'xprv1', password: oldPassword }); + const encryptedXprv2 = await bitgo.encrypt({ input: 'xprv2', password: otherPassword }); + nock(bgUrl) .post('/api/v1/user/encrypted') .reply(200, { keychains: { - xpub1: bitgo.encrypt({ input: 'xprv1', password: oldPassword }), - xpub2: bitgo.encrypt({ input: 'xprv2', password: otherPassword }), + xpub1: encryptedXprv1, + xpub2: encryptedXprv2, }, version: 1, }); const result = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword }); - _.forOwn(result.keychains, function (encryptedXprv, xpub) { + for (const [xpub, encryptedXprv] of Object.entries(result.keychains as Record)) { xpub.should.startWith('xpub'); try { - const decryptedPrv = bitgo.decrypt({ input: encryptedXprv, password: newPassword }); + const decryptedPrv = await bitgo.decrypt({ input: encryptedXprv, password: newPassword }); decryptedPrv.should.startWith('xprv'); } catch (e) { // the decryption didn't work because of the wrong password, this is one of the keychains that didn't match // the old password - e.message.should.startWith('password error'); + e.message.should.equal('incorrect password'); } - }); + } result.should.hasOwnProperty('version'); }); }); diff --git a/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsa.ts b/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsa.ts index cb3cc68216..b0ac951a8e 100644 --- a/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsa.ts +++ b/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsa.ts @@ -348,7 +348,7 @@ describe('TSS Ecdsa Utils:', async function () { assert.equal(body.webauthnInfo.prfSalt, webauthnInfo.prfSalt); assert.equal(body.webauthnInfo.enterpriseId, enterpriseId); assert.ok(body.webauthnInfo.encryptedPrv, 'encryptedPrv should be set'); - assert.ok(bitgo.decrypt({ input: body.webauthnInfo.encryptedPrv, password: webauthnInfo.passphrase })); + assert.ok(await bitgo.decrypt({ input: body.webauthnInfo.encryptedPrv, password: webauthnInfo.passphrase })); assert.strictEqual(body.webauthnDevices, undefined, 'deprecated webauthnDevices should not be sent'); }); @@ -1036,7 +1036,7 @@ describe('TSS Ecdsa Utils:', async function () { const step2SigningMaterial = await tssUtils.createOfflineMuDeltaShare({ aShareFromBitgo: aShare, bitgoChallenge: bitgoChallenges, - encryptedWShare: bitgo.encrypt({ input: JSON.stringify(wShare), password: mockPassword }), + encryptedWShare: await bitgo.encrypt({ input: JSON.stringify(wShare), password: mockPassword }), walletPassphrase: mockPassword, }); step2SigningMaterial.muDShare.muShare.alpha.length.should.equal(alphaLength); @@ -1051,7 +1051,7 @@ describe('TSS Ecdsa Utils:', async function () { .createOfflineMuDeltaShare({ aShareFromBitgo: aShare, bitgoChallenge: bitgoChallenges, - encryptedWShare: bitgo.encrypt({ input: JSON.stringify(wShare), password: mockPassword }), + encryptedWShare: await bitgo.encrypt({ input: JSON.stringify(wShare), password: mockPassword }), walletPassphrase: 'password1', }) .should.be.rejectedWith('incorrect password'); @@ -1062,7 +1062,7 @@ describe('TSS Ecdsa Utils:', async function () { const alphaLength = 1536; const deltaLength = 64; const bitgo = TestBitGo.decorate(BitGo, { env: 'mock' }); - const encryptedWShare = await bitgo.encryptAsync({ + const encryptedWShare = await bitgo.encrypt({ input: JSON.stringify(wShare), password: mockPassword, encryptionVersion: 2, @@ -1089,7 +1089,7 @@ describe('TSS Ecdsa Utils:', async function () { reqId: reqId, }, dShareFromBitgo: dShare, - encryptedOShare: bitgo.encrypt({ input: JSON.stringify(oShare), password: mockPassword }), + encryptedOShare: await bitgo.encrypt({ input: JSON.stringify(oShare), password: mockPassword }), walletPassphrase: mockPassword, requestType: RequestType.tx, }); @@ -1103,7 +1103,7 @@ describe('TSS Ecdsa Utils:', async function () { const pubKeyLength = 66; const privKeyLength = 64; const bitgo = TestBitGo.decorate(BitGo, { env: 'mock' }); - const encryptedOShare = await bitgo.encryptAsync({ + const encryptedOShare = await bitgo.encrypt({ input: JSON.stringify(oShare), password: mockPassword, encryptionVersion: 2, @@ -1134,7 +1134,7 @@ describe('TSS Ecdsa Utils:', async function () { reqId: reqId, }, dShareFromBitgo: dShare, - encryptedOShare: bitgo.encrypt({ input: JSON.stringify(oShare), password: mockPassword }), + encryptedOShare: await bitgo.encrypt({ input: JSON.stringify(oShare), password: mockPassword }), walletPassphrase: mockPassword, requestType: RequestType.tx, }) @@ -1446,9 +1446,13 @@ describe('TSS Ecdsa Utils:', async function () { const bitgoInstChallenge = mockChallengeA; const bitgoNitroChallenge = mockChallengeB; const userPassword = 'password123'; - const encryptedXprv = bitgo.encrypt({ - password: userPassword, - input: adminEcdhKey.xprv, + let encryptedXprv: string; + + before(async function () { + encryptedXprv = await bitgo.encrypt({ + password: userPassword, + input: adminEcdhKey.xprv, + }); }); beforeEach(async function () { @@ -1632,9 +1636,13 @@ describe('TSS Ecdsa Utils:', async function () { const bitgoNitroChallenge = mockChallengeB; const serializedEntChallenge = mockChallengeC; const userPassword = 'password123'; - const encryptedXprv = bitgo.encrypt({ - password: userPassword, - input: adminEcdhKey.xprv, + let encryptedXprv: string; + + before(async function () { + encryptedXprv = await bitgo.encrypt({ + password: userPassword, + input: adminEcdhKey.xprv, + }); }); beforeEach(async function () { diff --git a/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/createKeychains.ts b/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/createKeychains.ts index 94e6d08830..f3a402e17f 100644 --- a/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/createKeychains.ts +++ b/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/createKeychains.ts @@ -275,9 +275,9 @@ describe('TSS Ecdsa MPCv2 Utils:', async function () { const reducedEncryptedPrvParsed: { v: number } = JSON.parse(userKeychain.reducedEncryptedPrv); assert.equal(reducedEncryptedPrvParsed.v, 2, 'reducedEncryptedPrv should be a v2 envelope'); - // Verify v2 envelope is decryptable via decryptAsync - const decrypted = await bitgo.decryptAsync({ input: userKeychain.encryptedPrv, password: params.passphrase }); - assert.ok(decrypted, 'decryptAsync should successfully decrypt v2 envelope'); + // Verify v2 envelope is decryptable via decrypt + const decrypted = await bitgo.decrypt({ input: userKeychain.encryptedPrv, password: params.passphrase }); + assert.ok(decrypted, 'decrypt should successfully decrypt v2 envelope'); // Verify backup keychain also uses v2 envelopes assert.ok(backupKeychain); diff --git a/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/signTxRequest.ts b/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/signTxRequest.ts index 1ec4f59f2e..74c1b427b6 100644 --- a/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/signTxRequest.ts +++ b/modules/bitgo/test/v2/unit/internal/tssUtils/ecdsaMPCv2/signTxRequest.ts @@ -310,7 +310,7 @@ describe('signTxRequest:', function () { const userShare = fs.readFileSync(shareFiles[vector.party1]); const userPrvBase64 = Buffer.from(userShare).toString('base64'); - const encryptedPrv = await bitgo.encryptAsync({ + const encryptedPrv = await bitgo.encrypt({ input: userPrvBase64, password: walletPassphrase, encryptionVersion: 2, @@ -403,7 +403,7 @@ describe('signTxRequest:', function () { it('validateAdata accepts v2 envelopes with matching adata and domain separator', async function () { const adata = 'txhash:m/0/1'; const domainSep = 'DKLS23_SIGNING_ROUND1_STATE'; - const ct = await bitgo.encryptAsync({ + const ct = await bitgo.encrypt({ input: 'test-data', password: 'testpass', encryptionVersion: 2, @@ -415,7 +415,7 @@ describe('signTxRequest:', function () { it('validateAdata rejects v2 envelopes with mismatched adata', async function () { const domainSep = 'DKLS23_SIGNING_ROUND1_STATE'; - const ct = await bitgo.encryptAsync({ + const ct = await bitgo.encrypt({ input: 'test-data', password: 'testpass', encryptionVersion: 2, diff --git a/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts b/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts index f1daae1f91..59d55cb7e5 100644 --- a/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts +++ b/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts @@ -323,7 +323,7 @@ describe('TSS Utils:', async function () { assert.equal(body.webauthnInfo.prfSalt, webauthnInfo.prfSalt); assert.equal(body.webauthnInfo.enterpriseId, enterpriseId); assert.ok(body.webauthnInfo.encryptedPrv, 'encryptedPrv should be set'); - assert.ok(bitgo.decrypt({ input: body.webauthnInfo.encryptedPrv, password: webauthnInfo.passphrase })); + assert.ok(await bitgo.decrypt({ input: body.webauthnInfo.encryptedPrv, password: webauthnInfo.passphrase })); assert.strictEqual(body.webauthnDevices, undefined, 'deprecated webauthnDevices should not be sent'); }); @@ -644,7 +644,7 @@ describe('TSS Utils:', async function () { const envelope = JSON.parse(userKeychain.encryptedPrv!); envelope.v.should.equal(2); - const decrypted = await bitgo.decryptAsync({ input: userKeychain.encryptedPrv!, password: passphrase }); + const decrypted = await bitgo.decrypt({ input: userKeychain.encryptedPrv!, password: passphrase }); should.exist(decrypted); const parsed: Record = JSON.parse(decrypted); should.exist(parsed.uShare); @@ -677,7 +677,7 @@ describe('TSS Utils:', async function () { const prv = JSON.stringify(validUserSigningMaterial); // 1. Encrypt prv with v2 - const encryptedPrv = await bitgo.encryptAsync({ + const encryptedPrv = await bitgo.encrypt({ input: prv, password: passphrase, encryptionVersion: 2, diff --git a/modules/bitgo/test/v2/unit/internal/tssUtils/eddsaMPCv2/createKeychains.ts b/modules/bitgo/test/v2/unit/internal/tssUtils/eddsaMPCv2/createKeychains.ts index 53ae6fe795..85cf4064c1 100644 --- a/modules/bitgo/test/v2/unit/internal/tssUtils/eddsaMPCv2/createKeychains.ts +++ b/modules/bitgo/test/v2/unit/internal/tssUtils/eddsaMPCv2/createKeychains.ts @@ -93,13 +93,13 @@ describe('TSS EdDSA MPCv2 Utils:', async function () { assert.equal(userKeychain.source, 'user'); assert.ok(userKeychain.commonKeychain); assert.ok(userKeychain.encryptedPrv); - assert.ok(bitgo.decrypt({ input: userKeychain.encryptedPrv, password: params.passphrase })); + assert.ok(await bitgo.decrypt({ input: userKeychain.encryptedPrv, password: params.passphrase })); assert.ok(backupKeychain); assert.equal(backupKeychain.source, 'backup'); assert.ok(backupKeychain.commonKeychain); assert.ok(backupKeychain.encryptedPrv); - assert.ok(bitgo.decrypt({ input: backupKeychain.encryptedPrv, password: params.passphrase })); + assert.ok(await bitgo.decrypt({ input: backupKeychain.encryptedPrv, password: params.passphrase })); assert.ok(bitgoKeychain); assert.equal(bitgoKeychain.source, 'bitgo'); @@ -151,7 +151,7 @@ describe('TSS EdDSA MPCv2 Utils:', async function () { assert.equal(userBody.webauthnInfo.prfSalt, webauthnInfo.prfSalt); assert.equal(userBody.webauthnInfo.enterpriseId, enterpriseId); assert.ok(userBody.webauthnInfo.encryptedPrv, 'encryptedPrv should be set'); - assert.ok(bitgo.decrypt({ input: userBody.webauthnInfo.encryptedPrv, password: webauthnInfo.passphrase })); + assert.ok(await bitgo.decrypt({ input: userBody.webauthnInfo.encryptedPrv, password: webauthnInfo.passphrase })); assert.strictEqual(userBody.webauthnDevices, undefined, 'deprecated webauthnDevices should not be sent'); }); @@ -212,12 +212,12 @@ describe('TSS EdDSA MPCv2 Utils:', async function () { assert.ok(userKeychain.reducedEncryptedPrv); assert.equal( - bitgo.decrypt({ input: userKeychain.reducedEncryptedPrv, password: 'passphrase' }), + await bitgo.decrypt({ input: userKeychain.reducedEncryptedPrv, password: 'passphrase' }), encodeReduced(Buffer.from('userReduced')) ); assert.ok(backupKeychain.reducedEncryptedPrv); assert.equal( - bitgo.decrypt({ input: backupKeychain.reducedEncryptedPrv, password: 'passphrase' }), + await bitgo.decrypt({ input: backupKeychain.reducedEncryptedPrv, password: 'passphrase' }), encodeReduced(Buffer.from('backupReduced')) ); }); diff --git a/modules/bitgo/test/v2/unit/keychains.ts b/modules/bitgo/test/v2/unit/keychains.ts index c2d95e03ae..79bd92a001 100644 --- a/modules/bitgo/test/v2/unit/keychains.ts +++ b/modules/bitgo/test/v2/unit/keychains.ts @@ -173,7 +173,7 @@ describe('V2 Keychains', function () { .should.be.rejectedWith('Expecting parameter string: newPassword but found number'); }); - it('to update the password for a single keychain', function () { + it('to update the password for a single keychain', async function () { (() => keychains.updateSingleKeychainPassword({ newPassword: '5678' })).should.throw( 'expected old password to be a string' ); @@ -206,7 +206,7 @@ describe('V2 Keychains', function () { keychain: { encryptedPrv: 123 }, })).should.throw('expected keychain to be an object with an encryptedPrv property'); - const keychain = { encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: otherPassword }) }; + const keychain = { encryptedPrv: await bitgo.encrypt({ input: 'xprv1', password: otherPassword }) }; (() => keychains.updateSingleKeychainPassword({ oldPassword, newPassword, keychain })).should.throw( 'password used to decrypt keychain private key is incorrect' ); @@ -214,31 +214,31 @@ describe('V2 Keychains', function () { it('to update the password for a single keychain (async)', async function () { await keychains - .updateSingleKeychainPasswordAsync({ newPassword: '5678' }) + .updateSingleKeychainPassword({ newPassword: '5678' }) .should.be.rejectedWith('expected old password to be a string'); await keychains - .updateSingleKeychainPasswordAsync({ oldPassword: 1234, newPassword: '5678' }) + .updateSingleKeychainPassword({ oldPassword: 1234, newPassword: '5678' }) .should.be.rejectedWith('expected old password to be a string'); await keychains - .updateSingleKeychainPasswordAsync({ oldPassword: '1234' }) + .updateSingleKeychainPassword({ oldPassword: '1234' }) .should.be.rejectedWith('expected new password to be a string'); await keychains - .updateSingleKeychainPasswordAsync({ oldPassword: '1234', newPassword: 5678 }) + .updateSingleKeychainPassword({ oldPassword: '1234', newPassword: 5678 }) .should.be.rejectedWith('expected new password to be a string'); await keychains - .updateSingleKeychainPasswordAsync({ oldPassword: '1234', newPassword: '5678' }) + .updateSingleKeychainPassword({ oldPassword: '1234', newPassword: '5678' }) .should.be.rejectedWith('expected keychain to be an object with an encryptedPrv property'); await keychains - .updateSingleKeychainPasswordAsync({ oldPassword: '1234', newPassword: '5678', keychain: {} }) + .updateSingleKeychainPassword({ oldPassword: '1234', newPassword: '5678', keychain: {} }) .should.be.rejectedWith('expected keychain to be an object with an encryptedPrv property'); await keychains - .updateSingleKeychainPasswordAsync({ + .updateSingleKeychainPassword({ oldPassword: '1234', newPassword: '5678', keychain: { encryptedPrv: 123 }, @@ -246,20 +246,20 @@ describe('V2 Keychains', function () { .should.be.rejectedWith('expected keychain to be an object with an encryptedPrv property'); // wrong password — decrypt fails - const keychain = { encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: otherPassword }) }; + const keychain = { encryptedPrv: await bitgo.encrypt({ input: 'xprv1', password: otherPassword }) }; await keychains - .updateSingleKeychainPasswordAsync({ oldPassword, newPassword, keychain }) + .updateSingleKeychainPassword({ oldPassword, newPassword, keychain }) .should.be.rejectedWith('failed to update keychain password: incorrect password'); // invalid JSON in encryptedPrv await keychains - .updateSingleKeychainPasswordAsync({ oldPassword, newPassword, keychain: { encryptedPrv: 'not-valid-json' } }) + .updateSingleKeychainPassword({ oldPassword, newPassword, keychain: { encryptedPrv: 'not-valid-json' } }) .should.be.rejectedWith('failed to update keychain password: decrypt: ciphertext is not valid JSON'); // unknown envelope version const unknownVersionEnvelope = JSON.stringify({ v: 99, ct: 'abc' }); await keychains - .updateSingleKeychainPasswordAsync({ + .updateSingleKeychainPassword({ oldPassword, newPassword, keychain: { encryptedPrv: unknownVersionEnvelope }, @@ -268,6 +268,7 @@ describe('V2 Keychains', function () { }); it('on any other error', async function () { + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: oldPassword }); nock(bgUrl) .get('/api/v2/tltc/key') .query(true) @@ -275,28 +276,31 @@ describe('V2 Keychains', function () { keys: [ { pub: 'xpub1', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }), + encryptedPrv: encXprv1, }, ], }); - sandbox.stub(keychains, 'updateSingleKeychainPasswordAsync').throws('error', 'some random error'); + sandbox.stub(keychains, 'updateSingleKeychainPassword').throws('error', 'some random error'); await keychains.updatePassword({ oldPassword, newPassword }).should.be.rejectedWith('some random error'); }); }); describe('successful password update', function () { - const validateKeys = function (keys, newPassword, expectedLength) { + const validateKeys = async function (keys, newPassword, expectedLength) { assert.ok(Object.keys(keys).length === expectedLength, 'should have the expected number of keys'); - _.each(keys, function (value, key) { + for (const [key, value] of Object.entries(keys)) { assert.ok(key.includes('xpub') || key.includes('randomid'), 'key should be xpub or randomid'); - const decryptedPrv = bitgo.decrypt({ input: value, password: newPassword }); + const decryptedPrv = await bitgo.decrypt({ input: value, password: newPassword }); decryptedPrv.should.startWith('xprv'); - }); + } }; it('receive only one page when listing keychains', async function () { + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: oldPassword }); + const encXprv2 = await bitgo.encrypt({ input: 'xprv2', password: otherPassword }); + const encXprv3 = await bitgo.encrypt({ input: 'xprv3', password: oldPassword }); nock(bgUrl) .get('/api/v2/tltc/key') .query(true) @@ -304,26 +308,31 @@ describe('V2 Keychains', function () { keys: [ { pub: 'xpub1', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }), + encryptedPrv: encXprv1, }, { pub: 'xpub2', - encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }), + encryptedPrv: encXprv2, }, { id: 'randomid1', type: 'tss', - encryptedPrv: bitgo.encrypt({ input: 'xprv3', password: oldPassword }), + encryptedPrv: encXprv3, }, ], }); const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword }); - validateKeys(keys, newPassword, 2); + await validateKeys(keys, newPassword, 2); }); it('receive multiple pages when listing keychains', async function () { const prevId = 'prevId'; + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: oldPassword }); + const encXprv2 = await bitgo.encrypt({ input: 'xprv2', password: otherPassword }); + const encXprv3a = await bitgo.encrypt({ input: 'xprv3', password: oldPassword }); + const encXprv3b = await bitgo.encrypt({ input: 'xprv3', password: oldPassword }); + const encXprv4 = await bitgo.encrypt({ input: 'xprv4', password: otherPassword }); nock(bgUrl) .get('/api/v2/tltc/key') .query(true) @@ -332,16 +341,16 @@ describe('V2 Keychains', function () { keys: [ { pub: 'xpub1', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }), + encryptedPrv: encXprv1, }, { pub: 'xpub2', - encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }), + encryptedPrv: encXprv2, }, { id: 'randomid1', type: 'tss', - encryptedPrv: bitgo.encrypt({ input: 'xprv3', password: oldPassword }), + encryptedPrv: encXprv3a, }, ], }); @@ -355,54 +364,58 @@ describe('V2 Keychains', function () { keys: [ { pub: 'xpub3', - encryptedPrv: bitgo.encrypt({ input: 'xprv3', password: oldPassword }), + encryptedPrv: encXprv3b, }, { pub: 'xpub4', - encryptedPrv: bitgo.encrypt({ input: 'xprv4', password: otherPassword }), + encryptedPrv: encXprv4, }, ], }); const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword }); - validateKeys(keys, newPassword, 3); + await validateKeys(keys, newPassword, 3); }); it('single keychain password update', async () => { const prv = 'xprvtest'; const keychain = { xpub: 'xpub123', - encryptedPrv: bitgo.encrypt({ input: prv, password: oldPassword }), + encryptedPrv: await bitgo.encrypt({ input: prv, password: oldPassword }), }; const newKeychain = await keychains.updateSingleKeychainPassword({ keychain, oldPassword, newPassword }); - const decryptedPrv = bitgo.decrypt({ input: newKeychain.encryptedPrv, password: newPassword }); + const decryptedPrv = await bitgo.decrypt({ input: newKeychain.encryptedPrv, password: newPassword }); decryptedPrv.should.equal(prv); }); it('single keychain password update preserves v2 (Argon2id) envelope', async () => { const prv = 'xprvtest-v2'; - const encryptedPrv = await bitgo.encryptAsync({ input: prv, password: oldPassword, encryptionVersion: 2 }); + const encryptedPrv = await bitgo.encrypt({ input: prv, password: oldPassword, encryptionVersion: 2 }); const envelope = JSON.parse(encryptedPrv); envelope.v.should.equal(2, 'pre-condition: keychain must be v2-encrypted'); const keychain = { xpub: 'xpub123', encryptedPrv }; - const newKeychain = await keychains.updateSingleKeychainPasswordAsync({ keychain, oldPassword, newPassword }); + const newKeychain = await keychains.updateSingleKeychainPassword({ keychain, oldPassword, newPassword }); const newEnvelope = JSON.parse(newKeychain.encryptedPrv); newEnvelope.v.should.equal(2, 're-encrypted keychain must still be v2'); - const decryptedPrv = await bitgo.decryptAsync({ input: newKeychain.encryptedPrv, password: newPassword }); + const decryptedPrv = await bitgo.decrypt({ input: newKeychain.encryptedPrv, password: newPassword }); decryptedPrv.should.equal(prv, 'new password must decrypt to original prv'); - await bitgo.decryptAsync({ input: newKeychain.encryptedPrv, password: oldPassword }).should.be.rejected(); + await bitgo.decrypt({ input: newKeychain.encryptedPrv, password: oldPassword }).should.be.rejected(); }); it('updatePassword handles a mix of v1 and v2 keychains', async function () { const v1Prv = 'xprv-v1'; const v2Prv = 'xprv-v2'; + const encV1 = await bitgo.encrypt({ input: v1Prv, password: oldPassword }); + const encV2 = await bitgo.encrypt({ input: v2Prv, password: oldPassword, encryptionVersion: 2 }); + const encOther = await bitgo.encrypt({ input: 'xprv-other', password: 'different-password' }); + nock(bgUrl) .get('/api/v2/tltc/key') .query(true) @@ -410,15 +423,15 @@ describe('V2 Keychains', function () { keys: [ { pub: 'xpub-v1', - encryptedPrv: bitgo.encrypt({ input: v1Prv, password: oldPassword }), + encryptedPrv: encV1, }, { pub: 'xpub-v2', - encryptedPrv: await bitgo.encryptAsync({ input: v2Prv, password: oldPassword, encryptionVersion: 2 }), + encryptedPrv: encV2, }, { pub: 'xpub-other', - encryptedPrv: bitgo.encrypt({ input: 'xprv-other', password: 'different-password' }), + encryptedPrv: encOther, }, ], }); @@ -432,15 +445,17 @@ describe('V2 Keychains', function () { assert.ok(updatedV1, 'v1 keychain must be in the result'); assert.ok(updatedV2, 'v2 keychain must be in the result'); - bitgo.decrypt({ input: updatedV1, password: newPassword }).should.equal(v1Prv); + (await bitgo.decrypt({ input: updatedV1, password: newPassword })).should.equal(v1Prv); const updatedV2Envelope = JSON.parse(updatedV2); updatedV2Envelope.v.should.equal(2, 'v2 keychain must remain v2 after password change'); - const decryptedV2 = await bitgo.decryptAsync({ input: updatedV2, password: newPassword }); + const decryptedV2 = await bitgo.decrypt({ input: updatedV2, password: newPassword }); decryptedV2.should.equal(v2Prv); }); it('should return the updated keys with ids', async function () { + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: oldPassword }); + const encXprv2 = await bitgo.encrypt({ input: 'xprv2', password: otherPassword }); nock(bgUrl) .get('/api/v2/tltc/key') .query(true) @@ -449,21 +464,23 @@ describe('V2 Keychains', function () { { id: 'randomid1', type: 'tss', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }), + encryptedPrv: encXprv1, }, { id: 'randomid2', type: 'tss', - encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }), + encryptedPrv: encXprv2, }, ], }); const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword }); - validateKeys(keys, newPassword, 1); + await validateKeys(keys, newPassword, 1); }); it('should update multi-user-ofc keys', async function () { + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: oldPassword }); + const encXprv2 = await bitgo.encrypt({ input: 'xprv2', password: otherPassword }); nock(bgUrl) .get('/api/v2/tltc/key') .query(true) @@ -471,7 +488,7 @@ describe('V2 Keychains', function () { keys: [ { id: 'randomid1', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }), + encryptedPrv: encXprv1, coinSpecific: { ofc: { features: ['multi-user-key'], @@ -480,7 +497,7 @@ describe('V2 Keychains', function () { }, { id: 'randomid2', - encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }), + encryptedPrv: encXprv2, coinSpecific: { ofc: { features: ['multi-user-key'], @@ -491,7 +508,7 @@ describe('V2 Keychains', function () { }); const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword }); - validateKeys(keys, newPassword, 1); + await validateKeys(keys, newPassword, 1); }); }); @@ -616,7 +633,7 @@ describe('V2 Keychains', function () { }, }); - sandbox.stub(bitgo, 'decryptAsync').resolves(decryptResult); + sandbox.stub(bitgo, 'decrypt').resolves(decryptResult); }); afterEach(function () { @@ -1027,7 +1044,7 @@ describe('V2 Keychains', function () { const backup = await keychains.createBackup({ passphrase: 't3stSicretly!' }); scope.isDone().should.be.true(); backup.should.have.property('encryptedPrv'); - const decryptedPrv = bitgo.decrypt({ input: backup.encryptedPrv, password: 't3stSicretly!' }); + const decryptedPrv = await bitgo.decrypt({ input: backup.encryptedPrv, password: 't3stSicretly!' }); decryptedPrv.should.startWith('xprv'); }); @@ -1076,7 +1093,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, 'encryptAsync').resolves('newEncryptedPrv'); + encryptionStub = sandbox.stub(BitGo.prototype, 'encrypt').resolves('newEncryptedPrv'); }); afterEach(function () { diff --git a/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts b/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts index dbb1a38040..df4a0a8cc6 100644 --- a/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts +++ b/modules/bitgo/test/v2/unit/lightning/lightningWallets.ts @@ -227,7 +227,7 @@ describe('Lightning wallets', function () { assert.ok(response.wallet); assert.ok(response.encryptedWalletPassphrase); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); diff --git a/modules/bitgo/test/v2/unit/staking/goStakingWalletCommon.ts b/modules/bitgo/test/v2/unit/staking/goStakingWalletCommon.ts index 24fa219720..703fc3426a 100644 --- a/modules/bitgo/test/v2/unit/staking/goStakingWalletCommon.ts +++ b/modules/bitgo/test/v2/unit/staking/goStakingWalletCommon.ts @@ -36,47 +36,34 @@ describe('Go Staking Wallet Common', function () { }; const wallet = new Wallet(bitgo, baseCoin, walletData); stakingWallet = wallet.toGoStakingWallet(); - nock(microservicesUri) - .get(`/api/v2/${ofcCoin}/key/${stakingWallet.wallet.keyIds()[0]}`) - .reply(200, { - id: stakingWallet.wallet.keyIds()[0], - pub: 'xpub661MyMwAqRbcFq65dvGMeEVb81KKDRRkWkawSVesWcyevGc5gr8V27LjNfkktaMuKtM362jhgKy2eu35RdArcmmEAoULzAvgKkJpWQPvLXM', - source: 'user', - encryptedPrv: bitgo.encrypt({ - input: - 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', - password: 'passphrase', - }), - coinSpecific: {}, - }); + const encryptedStakingPrv = await bitgo.encrypt({ + input: + 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', + password: 'passphrase', + }); + nock(microservicesUri).get(`/api/v2/${ofcCoin}/key/${stakingWallet.wallet.keyIds()[0]}`).reply(200, { + id: stakingWallet.wallet.keyIds()[0], + pub: 'xpub661MyMwAqRbcFq65dvGMeEVb81KKDRRkWkawSVesWcyevGc5gr8V27LjNfkktaMuKtM362jhgKy2eu35RdArcmmEAoULzAvgKkJpWQPvLXM', + source: 'user', + encryptedPrv: encryptedStakingPrv, + coinSpecific: {}, + }); - nock(microservicesUri) - .get(`/api/v2/${ofcCoin}/key/${stakingWallet.wallet.keyIds()[1]}`) - .reply(200, { - id: stakingWallet.wallet.keyIds()[1], - pub: 'xpub661MyMwAqRbcFq65dvGMeEVb81KKDRRkWkawSVesWcyevGc5gr8V27LjNfkktaMuKtM362jhgKy2eu35RdArcmmEAoULzAvgKkJpWQPvLXM', - source: 'user', - encryptedPrv: bitgo.encrypt({ - input: - 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', - password: 'passphrase', - }), - coinSpecific: {}, - }); + nock(microservicesUri).get(`/api/v2/${ofcCoin}/key/${stakingWallet.wallet.keyIds()[1]}`).reply(200, { + id: stakingWallet.wallet.keyIds()[1], + pub: 'xpub661MyMwAqRbcFq65dvGMeEVb81KKDRRkWkawSVesWcyevGc5gr8V27LjNfkktaMuKtM362jhgKy2eu35RdArcmmEAoULzAvgKkJpWQPvLXM', + source: 'user', + encryptedPrv: encryptedStakingPrv, + coinSpecific: {}, + }); - nock(microservicesUri) - .get(`/api/v2/${ofcCoin}/key/${stakingWallet.wallet.keyIds()[2]}`) - .reply(200, { - id: stakingWallet.wallet.keyIds()[2], - pub: 'xpub661MyMwAqRbcFq65dvGMeEVb81KKDRRkWkawSVesWcyevGc5gr8V27LjNfkktaMuKtM362jhgKy2eu35RdArcmmEAoULzAvgKkJpWQPvLXM', - source: 'user', - encryptedPrv: bitgo.encrypt({ - input: - 'xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76', - password: 'passphrase', - }), - coinSpecific: {}, - }); + nock(microservicesUri).get(`/api/v2/${ofcCoin}/key/${stakingWallet.wallet.keyIds()[2]}`).reply(200, { + id: stakingWallet.wallet.keyIds()[2], + pub: 'xpub661MyMwAqRbcFq65dvGMeEVb81KKDRRkWkawSVesWcyevGc5gr8V27LjNfkktaMuKtM362jhgKy2eu35RdArcmmEAoULzAvgKkJpWQPvLXM', + source: 'user', + encryptedPrv: encryptedStakingPrv, + coinSpecific: {}, + }); }); const sandbox = sinon.createSandbox(); diff --git a/modules/bitgo/test/v2/unit/wallet.ts b/modules/bitgo/test/v2/unit/wallet.ts index 97c81926bc..761fb25056 100644 --- a/modules/bitgo/test/v2/unit/wallet.ts +++ b/modules/bitgo/test/v2/unit/wallet.ts @@ -2022,6 +2022,9 @@ describe('V2 Wallet:', function () { const getKeyNocks: nock.Scope[] = []; if (hotWallet.baseCoin.getFamily() === 'lnbtc') { + const encXprv0 = await bitgo.encrypt({ input: 'xprv0', password: walletPassphrase }); + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }); + const encryptedPrvs = [encXprv0, encXprv1]; for (let i = 0; i < 2; i++) { const keyId = lightningWalletData.coinSpecific.keys[i]; const getKeyNock = nock(bgUrl) @@ -2030,7 +2033,7 @@ describe('V2 Wallet:', function () { id: keyId, pub: i === 0 ? pub : 'Zo1ggzTUKMY5bYnDvT5mtVeZxzf2FaLTbKkmvGUhUQm', source: 'user', - encryptedPrv: bitgo.encrypt({ input: 'xprv' + i, password: walletPassphrase }), + encryptedPrv: encryptedPrvs[i], coinSpecific: i === 0 ? { [hotWallet.baseCoin.getChain()]: { purpose: 'userAuth' } } @@ -2039,15 +2042,14 @@ describe('V2 Wallet:', function () { getKeyNocks.push(getKeyNock); } } else { - const getKeyNock = nock(bgUrl) - .get(`/api/v2/tbtc/key/${wallet.keyIds()[0]}`) - .reply(200, { - id: wallet.keyIds()[0], - pub, - source: 'user', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), - coinSpecific: {}, - }); + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }); + const getKeyNock = nock(bgUrl).get(`/api/v2/tbtc/key/${wallet.keyIds()[0]}`).reply(200, { + id: wallet.keyIds()[0], + pub, + source: 'user', + encryptedPrv: encXprv1, + coinSpecific: {}, + }); getKeyNocks.push(getKeyNock); } @@ -2108,13 +2110,15 @@ describe('V2 Wallet:', function () { .reply(200, { userId, pubkey, path }); const pub = 'Zo1ggzTUKMY5bYnDvT5mtVeZxzf2FaLTbKkmvGUhUQk'; + const encPrv1 = await bitgo.encrypt({ input: privateKey, password: walletPassphrase1 }); + const encPrv2 = await bitgo.encrypt({ input: privateKey, password: walletPassphrase2 }); const getKeyNock = nock(bgUrl) .get(`/api/v2/tbtc/key/${wallet.keyIds()[0]}`) .reply(200, { id: wallet.keyIds()[0], pub, source: 'user', - encryptedPrv: bitgo.encrypt({ input: privateKey, password: walletPassphrase1 }), + encryptedPrv: encPrv1, webauthnDevices: [ { otpDeviceId: '123', @@ -2124,7 +2128,7 @@ describe('V2 Wallet:', function () { publicKey: 'some value', }, prfSalt: '456', - encryptedPrv: bitgo.encrypt({ input: privateKey, password: walletPassphrase2 }), + encryptedPrv: encPrv2, }, ], coinSpecific: {}, @@ -2362,15 +2366,14 @@ describe('V2 Wallet:', function () { .post('/api/v1/user/sharingkey', { email }) .reply(200, { userId, pubkey, path }); - const getKeyNock = nock(bgUrl) - .get(`/api/v2/ofc/key/${nonMultiUserKeyWallet.keyIds()[0]}`) - .reply(200, { - id: nonMultiUserKeyWallet.keyIds()[0], - pub, - source: 'user', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), - coinSpecific: {}, - }); + const encXprv1 = await bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }); + const getKeyNock = nock(bgUrl).get(`/api/v2/ofc/key/${nonMultiUserKeyWallet.keyIds()[0]}`).reply(200, { + id: nonMultiUserKeyWallet.keyIds()[0], + pub, + source: 'user', + encryptedPrv: encXprv1, + coinSpecific: {}, + }); const createShareStub = sinon.stub(nonMultiUserKeyWallet, 'createShare').callsFake(async (options) => { // For non-multi-key-user-key wallets, keychain should be present when spend permissions are included @@ -5195,15 +5198,14 @@ describe('V2 Wallet:', function () { // commonPub + commonChaincode const commonKeychain = randomBytes(32).toString('hex') + randomBytes(32).toString('hex'); - const getKeyNock = nock(bgUrl) - .get(`/api/v2/tsol/key/${tssSolWallet.keyIds()[0]}`) - .reply(200, { - id: tssSolWallet.keyIds()[0], - commonKeychain: commonKeychain, - source: 'user', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), - coinSpecific: {}, - }); + const encXprv1Tss = await bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }); + const getKeyNock = nock(bgUrl).get(`/api/v2/tsol/key/${tssSolWallet.keyIds()[0]}`).reply(200, { + id: tssSolWallet.keyIds()[0], + commonKeychain: commonKeychain, + source: 'user', + encryptedPrv: encXprv1Tss, + coinSpecific: {}, + }); const stub = sinon.stub(tssSolWallet, 'createShare').callsFake(async (options) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/modules/bitgo/test/v2/unit/wallets.ts b/modules/bitgo/test/v2/unit/wallets.ts index ae41d528c1..c9e2c3999f 100644 --- a/modules/bitgo/test/v2/unit/wallets.ts +++ b/modules/bitgo/test/v2/unit/wallets.ts @@ -19,7 +19,7 @@ import { Wallet, isWalletWithKeychains, OptionalKeychainEncryptedKey, - decryptKeychainPrivateKeyAsync, + decryptKeychainPrivateKey, makeRandomKey, getSharedSecret, BulkWalletShareOptions, @@ -478,7 +478,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -514,7 +514,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -633,7 +633,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -685,7 +685,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -1108,7 +1108,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -1164,7 +1164,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -1348,7 +1348,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -1405,7 +1405,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -1478,7 +1478,7 @@ describe('V2 Wallets:', function () { assert.ok(response.encryptedWalletPassphrase); assert.ok(response.wallet); assert.equal( - bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), + await bitgo.decrypt({ input: response.encryptedWalletPassphrase, password: params.passcodeEncryptionCode }), params.passphrase ); }); @@ -1957,8 +1957,8 @@ describe('V2 Wallets:', function () { const keychainsInstance = ofcWallets.baseCoin.keychains(); const keychainsStub = sandbox.stub(keychainsInstance, 'create').returns(keychain); - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: userPassword }); - const encryptStub = sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: userPassword }); + const encryptStub = sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); // Mock the updateShare API call const acceptShareNock = nock(bgUrl) @@ -2006,7 +2006,7 @@ describe('V2 Wallets:', function () { prv: testKeychain.prv, pub: testKeychain.pub, }; - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: userPassword }); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: userPassword }); const acceptShareNock = nock(bgUrl) .post(`/api/v2/ofc/walletshare/${shareId}`) @@ -2014,7 +2014,7 @@ describe('V2 Wallets:', function () { const keychainsInstance = ofcWallets.baseCoin.keychains(); sandbox.stub(keychainsInstance, 'create').returns(keychain); - sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); // Stub reshareWalletWithSpenders to verify it's NOT called const reshareStub = sandbox.stub(Wallets.prototype, 'reshareWalletWithSpenders'); @@ -2065,8 +2065,8 @@ describe('V2 Wallets:', function () { const keychainsInstance = ofcWallets.baseCoin.keychains(); const keychainsStub = sandbox.stub(keychainsInstance, 'create').returns(keychain); - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: userPassword }); - const encryptStub = sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: userPassword }); + const encryptStub = sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); // Mock bulk update API - this is called via bulkUpdateWalletShare nock(bgUrl) @@ -2138,8 +2138,8 @@ describe('V2 Wallets:', function () { const keychainsStub = sandbox.stub(keychainsInstance, 'create').returns(keychain); // Should encrypt with newWalletPassphrase if provided (as per implementation) - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: newWalletPassphrase }); - const encryptStub = sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: newWalletPassphrase }); + const encryptStub = sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); // Mock bulk update API - this is called via bulkUpdateWalletShare nock(bgUrl) @@ -2248,8 +2248,8 @@ describe('V2 Wallets:', function () { sandbox.stub(keychainsInstance, 'create').returns(keychain); // Should encrypt with newWalletPassphrase if provided (as per implementation) - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: newWalletPassphrase }); - const encryptStub = sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: newWalletPassphrase }); + const encryptStub = sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); const acceptShareNock = nock(bgUrl) .post(`/api/v2/ofc/walletshare/${shareId}`, (body: any) => { @@ -2301,8 +2301,8 @@ describe('V2 Wallets:', function () { const keychainsInstance = ofcWallets.baseCoin.keychains(); sandbox.stub(keychainsInstance, 'create').returns(keychain); - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: userPassword }); - sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: userPassword }); + sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); // Should use the multi-key-user-key flow (no signature/payload/keyId) const acceptShareNock = nock(bgUrl) @@ -2404,8 +2404,8 @@ describe('V2 Wallets:', function () { const keychainsInstance = ofcWallets.baseCoin.keychains(); sandbox.stub(keychainsInstance, 'create').returns(keychain); - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: userPassword }); - sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: userPassword }); + sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); const acceptShareNock = nock(bgUrl) .post(`/api/v2/ofc/walletshare/${shareId}`, (body: any) => { @@ -2450,8 +2450,8 @@ describe('V2 Wallets:', function () { const keychainsInstance = ofcWallets.baseCoin.keychains(); sandbox.stub(keychainsInstance, 'create').returns(keychain); - const encryptedPrv = bitgo.encrypt({ input: keychain.prv, password: userPassword }); - sandbox.stub(bitgo, 'encrypt').returns(encryptedPrv); + const encryptedPrv = await bitgo.encrypt({ input: keychain.prv, password: userPassword }); + sandbox.stub(bitgo, 'encrypt').resolves(encryptedPrv); let capturedBody: any; const acceptShareNock = nock(bgUrl) @@ -2504,7 +2504,7 @@ describe('V2 Wallets:', function () { const decryptedWalletPrv = 'walletPrivKeyPlaintext'; const eckey = makeRandomKey(); - const v2EncryptedPrv = bitgo.encrypt({ password: fakeSecret, input: decryptedWalletPrv }); + const v2EncryptedPrv = await bitgo.encrypt({ password: fakeSecret, input: decryptedWalletPrv }); nock(bgUrl) .get(`/api/v2/tbtc/walletshare/${shareId}`) @@ -2520,7 +2520,7 @@ describe('V2 Wallets:', function () { nock(bgUrl).post(`/api/v2/tbtc/walletshare/${shareId}`).reply(200, { changed: true, state: 'accepted' }); sandbox.stub(bitgo, 'getECDHKeychain').resolves({ encryptedXprv: v2EncryptedXprv }); - const decryptAsyncStub = sandbox.stub(bitgo, 'decryptAsync'); + const decryptAsyncStub = sandbox.stub(bitgo, 'decrypt'); // First call decrypts the ECDH keychain (v2), second decrypts the shared wallet prv decryptAsyncStub.onFirstCall().resolves(fakeXprv); decryptAsyncStub.onSecondCall().resolves(decryptedWalletPrv); @@ -2529,7 +2529,7 @@ describe('V2 Wallets:', function () { const res = await wallets.acceptShare({ walletShareId: shareId, userPassword }); should.equal(res.changed, true); should.equal(res.state, 'accepted'); - // Must have called decryptAsync (not the sync decrypt) for both the ECDH key and the wallet prv + // Must have called decrypt (not the sync decrypt) for both the ECDH key and the wallet prv assert.equal(decryptAsyncStub.callCount, 2); }); @@ -2558,7 +2558,7 @@ describe('V2 Wallets:', function () { nock(bgUrl).post(`/api/v2/tbtc/walletshare/${shareId}`).reply(200, { changed: true, state: 'accepted' }); sandbox.stub(bitgo, 'getECDHKeychain').resolves({ encryptedXprv: v2EncryptedXprv }); - const decryptAsyncStub = sandbox.stub(bitgo, 'decryptAsync'); + const decryptAsyncStub = sandbox.stub(bitgo, 'decrypt'); decryptAsyncStub.onFirstCall().resolves(fakeXprv); decryptAsyncStub.onSecondCall().resolves(decryptedWalletPrv); sandbox.stub(moduleBitgo, 'getSharedSecret').returns(Buffer.from(fakeSecret)); @@ -2682,9 +2682,9 @@ describe('V2 Wallets:', function () { const fromUserPrv = Math.random(); const walletPassphrase = 'bitgo1234'; const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), }; - const userPrv = await decryptKeychainPrivateKeyAsync(bitgo, keychainTest, walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(bitgo, keychainTest, walletPassphrase); if (!userPrv) { throw new Error('Unable to decrypt user keychain'); } @@ -2695,7 +2695,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: userPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: userPrv }); nock(bgUrl) .get('/api/v2/walletshares') .reply(200, { @@ -2725,14 +2725,14 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - const prvKey = bitgo.decrypt({ + const prvKey = await bitgo.decrypt({ password: walletPassphrase, - input: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + input: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - sinon.stub(bitgo, 'decryptAsync').resolves(prvKey); + sinon.stub(bitgo, 'decrypt').resolves(prvKey); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); const share = await wallets.bulkAcceptShare({ @@ -2758,7 +2758,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); const decryptedPrv = 'someWalletPrivateKey'; - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: decryptedPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: decryptedPrv }); // Simulate v2-encrypted ECDH keychain xprv (envelope with v:2 marker) const v2EncryptedXprv = JSON.stringify({ v: 2, iv: 'aaa', ct: 'bbb', adata: 'ccc' }); @@ -2786,8 +2786,8 @@ describe('V2 Wallets:', function () { .reply(200, { acceptedWalletShares: [{ walletShareId: shareId }] }); sinon.stub(bitgo, 'getECDHKeychain').resolves({ encryptedXprv: v2EncryptedXprv }); - // decryptAsync resolves with the xprv regardless of whether the envelope is v1 or v2 - sinon.stub(bitgo, 'decryptAsync').resolves(myEcdhXprv); + // decrypt resolves with the xprv regardless of whether the envelope is v1 or v2 + sinon.stub(bitgo, 'decrypt').resolves(myEcdhXprv); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); const share = await wallets.bulkAcceptShare({ @@ -2835,7 +2835,7 @@ describe('V2 Wallets:', function () { sinon.stub(bitgo, 'getECDHKeychain').resolves({ encryptedXprv: v2EncryptedXprv }); // First call: decrypt ECDH keychain xprv; second call: decrypt v2 wallet share prv - const decryptAsyncStub = sinon.stub(bitgo, 'decryptAsync'); + const decryptAsyncStub = sinon.stub(bitgo, 'decrypt'); decryptAsyncStub.onFirstCall().resolves(myEcdhXprv); decryptAsyncStub.onSecondCall().resolves(decryptedWalletPrv); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); @@ -2845,7 +2845,7 @@ describe('V2 Wallets:', function () { userLoginPassword: walletPassphrase, }); assert.deepEqual(share, { acceptedWalletShares: [{ walletShareId: shareId }] }); - // Both decrypt calls must use decryptAsync (not the sync decrypt) + // Both decrypt calls must use decrypt (not the sync decrypt) assert.equal(decryptAsyncStub.callCount, 2); }); @@ -2855,9 +2855,9 @@ describe('V2 Wallets:', function () { const webauthnPassphrase = 'prf-derived-secret'; const shareId = '66a229dbdccdcfb95b44fc2745a60bd4'; const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), }; - const userPrv = await decryptKeychainPrivateKeyAsync(bitgo, keychainTest, walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(bitgo, keychainTest, walletPassphrase); if (!userPrv) { throw new Error('Unable to decrypt user keychain'); } @@ -2868,7 +2868,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: userPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: userPrv }); let capturedBody: any; nock(bgUrl) @@ -2899,14 +2899,14 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - const prvKey = bitgo.decrypt({ + const prvKey = await bitgo.decrypt({ password: walletPassphrase, - input: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + input: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - sinon.stub(bitgo, 'decryptAsync').resolves(prvKey); + sinon.stub(bitgo, 'decrypt').resolves(prvKey); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); await wallets.bulkAcceptShare({ @@ -2934,9 +2934,9 @@ describe('V2 Wallets:', function () { const walletPassphrase = 'bitgo1234'; const shareId = '66a229dbdccdcfb95b44fc2745a60bd4'; const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), }; - const userPrv = await decryptKeychainPrivateKeyAsync(bitgo, keychainTest, walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(bitgo, keychainTest, walletPassphrase); if (!userPrv) { throw new Error('Unable to decrypt user keychain'); } @@ -2947,7 +2947,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: userPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: userPrv }); let capturedBody: any; nock(bgUrl) @@ -2978,13 +2978,13 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - const prvKey = bitgo.decrypt({ + const prvKey = await bitgo.decrypt({ password: walletPassphrase, - input: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + input: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - sinon.stub(bitgo, 'decryptAsync').resolves(prvKey); + sinon.stub(bitgo, 'decrypt').resolves(prvKey); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); await wallets.bulkAcceptShare({ @@ -3002,9 +3002,9 @@ describe('V2 Wallets:', function () { const walletPassphrase = 'bitgo1234'; const fromUserPrv = Math.random(); const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), }; - const userPrv = await decryptKeychainPrivateKeyAsync(bitgo, keychainTest, walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(bitgo, keychainTest, walletPassphrase); if (!userPrv) { throw new Error('Unable to decrypt user keychain'); } @@ -3016,7 +3016,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); // Pad the private key with additional data to make it larger before encrypting - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: userPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: userPrv }); const keychain = { path: path, fromPubKey: eckey.publicKey.toString('hex'), @@ -3046,15 +3046,15 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - const prvKey = bitgo.decrypt({ + const prvKey = await bitgo.decrypt({ password: walletPassphrase, - input: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + input: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - sinon.stub(bitgo, 'decryptAsync').resolves(prvKey); - sinon.stub(bitgo, 'encrypt').returns(userPrv + 'X'.repeat(100000)); + sinon.stub(bitgo, 'decrypt').resolves(prvKey); + sinon.stub(bitgo, 'encrypt').resolves(userPrv + 'X'.repeat(100000)); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); // Mock bulkAcceptShareRequestWithRetry to track batch sizes @@ -3103,9 +3103,9 @@ describe('V2 Wallets:', function () { const walletPassphrase = 'bitgo1234'; const fromUserPrv = Math.random(); const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), }; - const userPrv = await decryptKeychainPrivateKeyAsync(bitgo, keychainTest, walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(bitgo, keychainTest, walletPassphrase); if (!userPrv) { throw new Error('Unable to decrypt user keychain'); } @@ -3116,7 +3116,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: userPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: userPrv }); const keychain = { path: path, fromPubKey: eckey.publicKey.toString('hex'), @@ -3146,15 +3146,15 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - const prvKey = bitgo.decrypt({ + const prvKey = await bitgo.decrypt({ password: walletPassphrase, - input: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + input: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - sinon.stub(bitgo, 'decryptAsync').resolves(prvKey); - sinon.stub(bitgo, 'encrypt').returns(userPrv + 'X'.repeat(100000)); + sinon.stub(bitgo, 'decrypt').resolves(prvKey); + sinon.stub(bitgo, 'encrypt').resolves(userPrv + 'X'.repeat(100000)); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); // Track the sequence of batch sizes attempted @@ -3205,9 +3205,9 @@ describe('V2 Wallets:', function () { const walletPassphrase = 'bitgo1234'; const fromUserPrv = Math.random(); const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: fromUserPrv.toString(), password: walletPassphrase }), }; - const userPrv = await decryptKeychainPrivateKeyAsync(bitgo, keychainTest, walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(bitgo, keychainTest, walletPassphrase); if (!userPrv) { throw new Error('Unable to decrypt user keychain'); } @@ -3218,7 +3218,7 @@ describe('V2 Wallets:', function () { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(pubkey, 'hex')).toString('hex'); - const newEncryptedPrv = bitgo.encrypt({ password: secret, input: userPrv }); + const newEncryptedPrv = await bitgo.encrypt({ password: secret, input: userPrv }); const keychain = { path: path, fromPubKey: eckey.publicKey.toString('hex'), @@ -3248,14 +3248,14 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - const prvKey = bitgo.decrypt({ + const prvKey = await bitgo.decrypt({ password: walletPassphrase, - input: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + input: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); - sinon.stub(bitgo, 'decryptAsync').resolves(prvKey); + sinon.stub(bitgo, 'decrypt').resolves(prvKey); sinon.stub(moduleBitgo, 'getSharedSecret').resolves('fakeSharedSecret'); // Always throw 413 error, even for batch size 1 @@ -3503,7 +3503,7 @@ describe('V2 Wallets:', function () { .resolves(userKeychain); // Mock decrypt and signMessage - sinon.stub(bitgo, 'decryptAsync').resolves('decryptedPrivateKey'); + sinon.stub(bitgo, 'decrypt').resolves('decryptedPrivateKey'); sinon.stub(wallets.baseCoin, 'signMessage').resolves(Buffer.from('signature')); // Mock bulkUpdateWalletShareRequest @@ -3567,7 +3567,7 @@ describe('V2 Wallets:', function () { sinon.stub(ofcWallets.baseCoin.keychains(), 'createUserKeychain').resolves(userKeychain); // Mock decrypt and signMessage - sinon.stub(bitgo, 'decryptAsync').resolves('decryptedPrivateKey'); + sinon.stub(bitgo, 'decrypt').resolves('decryptedPrivateKey'); sinon.stub(ofcWallets.baseCoin, 'signMessage').resolves(Buffer.from('signature')); // Mock getECDHKeychain @@ -3674,7 +3674,7 @@ describe('V2 Wallets:', function () { const originalPrivKey = 'originalPrivateKey'; const sharedSecret = getSharedSecret(fromKeychain, Buffer.from(toPubKey, 'hex')).toString('hex'); - const encryptedPrv = bitgo.encrypt({ + const encryptedPrv = await bitgo.encrypt({ password: sharedSecret, input: originalPrivKey, }); @@ -3706,15 +3706,15 @@ describe('V2 Wallets:', function () { // Mock getECDHKeychain const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ - encryptedXprv: bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), + encryptedXprv: await bitgo.encrypt({ input: myEcdhKeychain.xprv, password: walletPassphrase }), }); // Setup decrypt and encrypt stubs - const decryptStub = sinon.stub(bitgo, 'decryptAsync'); + const decryptStub = sinon.stub(bitgo, 'decrypt'); decryptStub.onFirstCall().resolves(myEcdhKeychain.xprv); // For sharing keychain decryptStub.onSecondCall().resolves(originalPrivKey); // For wallet keychain - const encryptStub = sinon.stub(bitgo, 'encrypt').returns('newEncryptedPrv'); + const encryptStub = sinon.stub(bitgo, 'encrypt').resolves('newEncryptedPrv'); // Mock getSharedSecret sinon.stub(moduleBitgo, 'getSharedSecret').returns(Buffer.from(sharedSecret)); @@ -3856,7 +3856,7 @@ describe('V2 Wallets:', function () { const originalPrivKey = 'originalPrivateKey'; const sharedSecret = getSharedSecret(fromKeychain, Buffer.from(toPubKey, 'hex')).toString('hex'); - const encryptedPrv = bitgo.encrypt({ password: sharedSecret, input: originalPrivKey }); + const encryptedPrv = await bitgo.encrypt({ password: sharedSecret, input: originalPrivKey }); // Both the ECDH keychain and share prv are v2-encrypted in this scenario const v2EncryptedXprv = JSON.stringify({ v: 2, iv: 'aabbcc', ct: 'ddeeff', adata: '00' }); @@ -3881,12 +3881,12 @@ describe('V2 Wallets:', function () { const myEcdhKeychain = await bitgo.keychains().create(); sinon.stub(bitgo, 'getECDHKeychain').resolves({ encryptedXprv: v2EncryptedXprv }); - // decryptAsync handles v2 transparently; stub to return expected values - const decryptAsyncStub = sinon.stub(bitgo, 'decryptAsync'); + // decrypt handles v2 transparently; stub to return expected values + const decryptAsyncStub = sinon.stub(bitgo, 'decrypt'); decryptAsyncStub.onFirstCall().resolves(myEcdhKeychain.xprv); // ECDH keychain decryptAsyncStub.onSecondCall().resolves(originalPrivKey); // wallet share prv - const encryptStub = sinon.stub(bitgo, 'encrypt').returns('newEncryptedPrv'); + const encryptStub = sinon.stub(bitgo, 'encrypt').resolves('newEncryptedPrv'); sinon.stub(moduleBitgo, 'getSharedSecret').returns(Buffer.from(sharedSecret)); const bulkUpdateStub = sinon.stub(Wallets.prototype, 'bulkUpdateWalletShareRequest').resolves({ @@ -3907,7 +3907,7 @@ describe('V2 Wallets:', function () { walletShareUpdateErrors: [], }); - // Both decrypt calls must have gone through decryptAsync + // Both decrypt calls must have gone through decrypt assert.equal(decryptAsyncStub.callCount, 2); bulkUpdateStub.calledOnce.should.be.true(); encryptStub.calledOnce.should.be.true(); @@ -4156,7 +4156,7 @@ describe('V2 Wallets:', function () { id: wallet.keyIds()[0], pub, source: 'user', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), coinSpecific: {}, }); const params: BulkWalletShareOptions = { @@ -4173,7 +4173,7 @@ describe('V2 Wallets:', function () { const prv1 = Math.random().toString(); const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: prv1, password: walletPassphrase }), }; sinon.stub(wallet, 'getEncryptedUserKeychain').resolves({ @@ -4238,7 +4238,7 @@ describe('V2 Wallets:', function () { id: wallet.keyIds()[0], pub, source: 'user', - encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: 'xprv1', password: walletPassphrase }), coinSpecific: {}, }); const params: BulkWalletShareOptions = { @@ -4255,7 +4255,7 @@ describe('V2 Wallets:', function () { const prv1 = Math.random().toString(); const keychainTest: OptionalKeychainEncryptedKey = { - encryptedPrv: bitgo.encrypt({ input: prv1, password: walletPassphrase }), + encryptedPrv: await bitgo.encrypt({ input: prv1, password: walletPassphrase }), }; sinon.stub(wallet, 'getEncryptedUserKeychain').resolves({ @@ -4310,7 +4310,7 @@ describe('V2 Wallets:', function () { }); const encryptPrvForUserStub = sinon - .stub(wallet, 'encryptPrvForUserAsync') + .stub(wallet, 'encryptPrvForUser') .callsFake(async (prv, pubKey, userPubKey, path) => { return { pub: pubKey, @@ -4366,7 +4366,7 @@ describe('V2 Wallets:', function () { }); }); - describe('downloadKeycardAsync', () => { + describe('downloadKeycard', () => { const localBitgo = TestBitGo.decorate(BitGo, { env: 'mock' }); const walletData = { id: '5b34252f1bf349930e34020a00000002', @@ -4385,7 +4385,7 @@ describe('V2 Wallets:', function () { it('should throw when called in Node.js (no browser window)', async () => { // In Node.js, accessing `window` throws ReferenceError; the method rejects. - await wallet.downloadKeycardAsync().should.be.rejected(); + await wallet.downloadKeycard().should.be.rejected(); }); it('downloadKeycard (sync) should throw when called in Node.js (no browser window)', () => { @@ -4393,7 +4393,7 @@ describe('V2 Wallets:', function () { }); }); - describe('encryptPrvForUserAsync', () => { + describe('encryptPrvForUser', () => { const localBitgo = TestBitGo.decorate(BitGo, { env: 'mock' }); const walletData = { id: '5b34252f1bf349930e34020a00000001', @@ -4428,9 +4428,9 @@ describe('V2 Wallets:', function () { const userPubkey = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'; const path = 'm/999999/0/1'; - sinon.stub(localBitgo, 'encryptAsync').resolves('encryptedPrvForUser'); + sinon.stub(localBitgo, 'encrypt').resolves('encryptedPrvForUser'); - const result = await wallet.encryptPrvForUserAsync(decryptedPrv, pub, userPubkey, path); + const result = await wallet.encryptPrvForUser(decryptedPrv, pub, userPubkey, path); result.should.have.property('pub', pub); result.should.have.property('encryptedPrv', 'encryptedPrvForUser'); diff --git a/modules/express/encryptedPrivKeys.json b/modules/express/encryptedPrivKeys.json index fa9742e4d4..eda0c3adfe 100644 --- a/modules/express/encryptedPrivKeys.json +++ b/modules/express/encryptedPrivKeys.json @@ -1,3 +1,3 @@ { - "61f039aad587c2000745c687373e0fa9": "{\"iv\":\"/Gnh+Ip1G+IOhy+Cms+umQ==\",\"v\":1,\"iter\":10000,\"ks\":256,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"FYnd1xwReTw=\",\"ct\":\"vgnCvdJ1Z9sqeV6urYxNsscwnkB/6eSPsZhzaW4Cuc7RKEY1uWNlleR0Tjtd8nlQuhsA5UXFpOID3lHHHjPDvB+jWtRm08I2F+HNGYuklWG12vIiSrY2KnkYRJkyCghn5Pq3iEimQb9M2kkwj5wf4EtfAiz9jsY=\"}" + "61f039aad587c2000745c687373e0fa9": "{\"iv\":\"+1u1Y9cvsYuRMeyH2slnXQ==\",\"v\":1,\"iter\":10000,\"ks\":256,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"54kOXTqJ9mc=\",\"ct\":\"JF5wQ82wa1dYyFxFlbHCvK4a+A6MTHdhOqc5uXsz2icWhkY2Lin/3Ab8ZwvwDaR1JYKmC/g1gXIGwVZEOl1M/bRHY420h7sDtmTS6Ebse5NWbF0ItfUJlk6HVATGa+C6mkbaVxJ4kQW/ehnT3riqzU069ATPz8E=\"}" } \ No newline at end of file diff --git a/modules/express/src/clientRoutes.ts b/modules/express/src/clientRoutes.ts index 9e10658849..dd5fdc99a2 100755 --- a/modules/express/src/clientRoutes.ts +++ b/modules/express/src/clientRoutes.ts @@ -104,13 +104,13 @@ function handleLogin(req: ExpressApiRouteRequest<'express.v1.login' | 'express.l async function handleDecrypt(req: ExpressApiRouteRequest<'express.v1.decrypt' | 'express.decrypt', 'post'>) { return { - decrypted: await req.bitgo.decryptAsync(req.body), + decrypted: await req.bitgo.decrypt(req.body), }; } async function handleEncrypt(req: ExpressApiRouteRequest<'express.v1.encrypt' | 'express.encrypt', 'post'>) { return { - encrypted: await req.bitgo.encryptAsync(req.body), + encrypted: await req.bitgo.encrypt(req.body), }; } @@ -431,7 +431,7 @@ async function getEncryptedPrivKey(path: string, walletId: string): Promise { try { - return await bg.decryptAsync({ password: walletPw, input: encryptedPrivKey }); + return await bg.decrypt({ password: walletPw, input: encryptedPrivKey }); } catch (e) { throw new Error(`Error when trying to decrypt private key: ${e}`); } @@ -1387,7 +1387,7 @@ export async function handleKeychainChangePassword( ); } - const updatedKeychain = await coin.keychains().updateSingleKeychainPasswordAsync({ + const updatedKeychain = await coin.keychains().updateSingleKeychainPassword({ keychain, oldPassword, newPassword, diff --git a/modules/express/src/fetchEncryptedPrivKeys.ts b/modules/express/src/fetchEncryptedPrivKeys.ts index 5edfcdd179..f3760a9f8f 100644 --- a/modules/express/src/fetchEncryptedPrivKeys.ts +++ b/modules/express/src/fetchEncryptedPrivKeys.ts @@ -78,7 +78,7 @@ export async function fetchKeys(ids: WalletIds, token: string, accessToken?: str if (keychain.encryptedPrv === undefined) { if (typeof credential === 'object') { - const encryptedPrv = await bg.encryptAsync({ + const encryptedPrv = await bg.encrypt({ password: credential.walletPassword, input: credential.secret, encryptionVersion: credential.encryptionVersion, diff --git a/modules/express/src/lightning/lightningSignerRoutes.ts b/modules/express/src/lightning/lightningSignerRoutes.ts index f74dc4c273..29a9c77936 100644 --- a/modules/express/src/lightning/lightningSignerRoutes.ts +++ b/modules/express/src/lightning/lightningSignerRoutes.ts @@ -82,8 +82,8 @@ export async function handleInitLightningWallet( throw new ApiResponseError('Missing encryptedPrv in node auth keychain', 400); } const network = getUtxolibNetwork(coin.getChain()); - const signerRootKey = await getSignerRootKey(passphrase, userKeyEncryptedPrv, network, (p) => bitgo.decryptAsync(p)); - const macaroonRootKey = await getMacaroonRootKey(passphrase, nodeAuthKeyEncryptedPrv, (p) => bitgo.decryptAsync(p)); + const signerRootKey = await getSignerRootKey(passphrase, userKeyEncryptedPrv, network, (p) => bitgo.decrypt(p)); + const macaroonRootKey = await getMacaroonRootKey(passphrase, nodeAuthKeyEncryptedPrv, (p) => bitgo.decrypt(p)); const { admin_macaroon: adminMacaroon } = await lndSignerClient.initWallet({ // The passphrase at LND can only accommodate a base64 character set @@ -139,7 +139,7 @@ export async function handleCreateSignerMacaroon( if (!encryptedSignerAdminMacaroon) { throw new ApiResponseError('Missing encryptedSignerAdminMacaroon in wallet', 400); } - const adminMacaroon = await bitgo.decryptAsync({ + const adminMacaroon = await bitgo.decrypt({ password: passphrase, input: encryptedSignerAdminMacaroon, }); diff --git a/modules/express/test/unit/clientRoutes/changeKeychainPassword.ts b/modules/express/test/unit/clientRoutes/changeKeychainPassword.ts index f7523854ca..372fd5e5a8 100644 --- a/modules/express/test/unit/clientRoutes/changeKeychainPassword.ts +++ b/modules/express/test/unit/clientRoutes/changeKeychainPassword.ts @@ -16,7 +16,7 @@ describe('Change Wallet Password', function () { const newPassword = 'newPasswordString'; const keychainBaseCoinStub = { - keychains: () => ({ updateSingleKeychainPasswordAsync: () => Promise.resolve({ result: 'stubbed' }) }), + keychains: () => ({ updateSingleKeychainPassword: () => Promise.resolve({ result: 'stubbed' }) }), }; it('should change wallet password', async function () { @@ -27,7 +27,7 @@ describe('Change Wallet Password', function () { const coinStub = { keychains: () => ({ get: () => Promise.resolve(keychainStub), - updateSingleKeychainPasswordAsync: () => Promise.resolve({ result: 'stubbed' }), + updateSingleKeychainPassword: () => Promise.resolve({ result: 'stubbed' }), }), url: () => 'url', }; @@ -82,7 +82,7 @@ describe('Change Wallet Password', function () { const coinStub = { keychains: () => ({ get: () => Promise.resolve(keychainStub), - updateSingleKeychainPasswordAsync: () => Promise.resolve({ result: 'stubbed' }), + updateSingleKeychainPassword: () => Promise.resolve({ result: 'stubbed' }), }), url: () => 'url', }; @@ -136,7 +136,7 @@ describe('Change Wallet Password', function () { const coinStub = { keychains: () => ({ get: () => Promise.resolve(keychainStub), - updateSingleKeychainPasswordAsync: () => Promise.resolve({ result: 'stubbed' }), + updateSingleKeychainPassword: () => Promise.resolve({ result: 'stubbed' }), }), url: () => 'url', }; @@ -189,7 +189,7 @@ describe('Change Wallet Password', function () { const coinStub = { keychains: () => ({ get: () => Promise.resolve(keychainStub), - updateSingleKeychainPasswordAsync: () => Promise.resolve({ result: 'stubbed' }), + updateSingleKeychainPassword: () => Promise.resolve({ result: 'stubbed' }), }), url: () => 'url', }; diff --git a/modules/express/test/unit/clientRoutes/externalSign.ts b/modules/express/test/unit/clientRoutes/externalSign.ts index 2eb1113264..746d7c047b 100644 --- a/modules/express/test/unit/clientRoutes/externalSign.ts +++ b/modules/express/test/unit/clientRoutes/externalSign.ts @@ -183,7 +183,7 @@ describe('External signer', () => { }; const bg = new BitGo({ env: 'test' }); const walletPassphrase = 'testPass'; - const validPrv = bg.encrypt({ input: JSON.stringify(userSigningMaterial), password: walletPassphrase }); + const validPrv = await bg.encrypt({ input: JSON.stringify(userSigningMaterial), password: walletPassphrase }); const output: Output = {}; output[walletID] = validPrv; const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(JSON.stringify(output)); @@ -478,7 +478,7 @@ describe('External signer', () => { }; const bg = new BitGo({ env: 'test' }); const walletPassphrase = 'testPass'; - const validPrv = bg.encrypt({ input: JSON.stringify(userSigningMaterial), password: walletPassphrase }); + const validPrv = await bg.encrypt({ input: JSON.stringify(userSigningMaterial), password: walletPassphrase }); const output: Output = {}; output[walletID] = validPrv; const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(JSON.stringify(output)); @@ -896,7 +896,7 @@ describe('External signer', () => { const bgTest = new BitGo({ env: 'test' }); const userKeyShare = userShare.getKeyShare().toString('base64'); - const validPrv = bgTest.encrypt({ input: userKeyShare, password: walletPassphrase }); + const validPrv = await bgTest.encrypt({ input: userKeyShare, password: walletPassphrase }); const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(JSON.stringify({ [walletID]: validPrv })); const envStub = sinon.stub(process, 'env').value({ ['WALLET_' + walletID + '_PASSPHRASE']: walletPassphrase }); @@ -1095,7 +1095,7 @@ describe('External signer', () => { const bgTest = new BitGo({ env: 'test' }); const userKeyShareB64 = userKeyShareBuffer.toString('base64'); - const validPrvEdDSA = bgTest.encrypt({ input: userKeyShareB64, password: walletPassphrase }); + const validPrvEdDSA = await bgTest.encrypt({ input: userKeyShareB64, password: walletPassphrase }); const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(JSON.stringify({ [walletID]: validPrvEdDSA })); const envStub = sinon.stub(process, 'env').value({ ['WALLET_' + walletID + '_PASSPHRASE']: walletPassphrase }); diff --git a/modules/express/test/unit/typedRoutes/coinSign.ts b/modules/express/test/unit/typedRoutes/coinSign.ts index 565360e08b..3ab2dd9e56 100644 --- a/modules/express/test/unit/typedRoutes/coinSign.ts +++ b/modules/express/test/unit/typedRoutes/coinSign.ts @@ -104,7 +104,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { // Create mock BitGo with decrypt method const mockBitGo = { - decryptAsync: sinon.stub().resolves(decryptedPrivKey), + decrypt: sinon.stub().resolves(decryptedPrivKey), coin: sinon.stub(), }; @@ -117,7 +117,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { // Stub BitGo constructor sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').callsFake(mockBitGo.decryptAsync); + sinon.stub(BitGo.prototype, 'decrypt').callsFake(mockBitGo.decrypt); // Make the request to Express const result = await agent @@ -139,11 +139,8 @@ describe('CoinSign codec tests (External Signer Mode)', function () { assert.strictEqual(fsReadFileStub.calledOnce, true); // Verify private key was decrypted - assert.strictEqual(mockBitGo.decryptAsync.calledOnce, true); - assert.strictEqual( - mockBitGo.decryptAsync.calledWith({ password: walletPassphrase, input: encryptedPrivKey }), - true - ); + assert.strictEqual(mockBitGo.decrypt.calledOnce, true); + assert.strictEqual(mockBitGo.decrypt.calledWith({ password: walletPassphrase, input: encryptedPrivKey }), true); // Verify signTransaction was called with decrypted key assert.strictEqual(mockCoin.signTransaction.calledOnce, true); @@ -184,7 +181,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { // Stub BitGo methods sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent @@ -241,7 +238,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent @@ -287,7 +284,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent @@ -331,7 +328,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent @@ -402,7 +399,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent @@ -575,7 +572,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { ); // Mock decrypt to throw error - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error('Invalid passphrase')); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error('Invalid passphrase')); // Make the request const result = await agent @@ -610,7 +607,7 @@ describe('CoinSign codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent diff --git a/modules/express/test/unit/typedRoutes/decrypt.ts b/modules/express/test/unit/typedRoutes/decrypt.ts index fae8c74815..4f42204594 100644 --- a/modules/express/test/unit/typedRoutes/decrypt.ts +++ b/modules/express/test/unit/typedRoutes/decrypt.ts @@ -170,7 +170,7 @@ describe('Decrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(mockDecryptResponse); + sinon.stub(BitGo.prototype, 'decrypt').resolves(mockDecryptResponse); const result = await agent .post('/api/v1/decrypt') @@ -192,7 +192,7 @@ describe('Decrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(mockDecryptResponse); + sinon.stub(BitGo.prototype, 'decrypt').resolves(mockDecryptResponse); const result = await agent .post('/api/v2/decrypt') @@ -215,7 +215,7 @@ describe('Decrypt codec tests', function () { }; const mockLongDecrypted = 'b'.repeat(500); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(mockLongDecrypted); + sinon.stub(BitGo.prototype, 'decrypt').resolves(mockLongDecrypted); const result = await agent .post('/api/v1/decrypt') @@ -236,7 +236,7 @@ describe('Decrypt codec tests', function () { password: 'p@ssw0rd!#$%^&*()', }; - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(mockDecryptResponse); + sinon.stub(BitGo.prototype, 'decrypt').resolves(mockDecryptResponse); const result = await agent .post('/api/v2/decrypt') @@ -269,7 +269,7 @@ describe('Decrypt codec tests', function () { password: 'wrongPassword', }; - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error("password error - ccm: tag doesn't match")); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error("password error - ccm: tag doesn't match")); const result = await agent .post('/api/v1/decrypt') @@ -287,7 +287,7 @@ describe('Decrypt codec tests', function () { password: 'wrongPassword', }; - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error("password error - ccm: tag doesn't match")); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error("password error - ccm: tag doesn't match")); const result = await agent .post('/api/v2/decrypt') @@ -305,7 +305,7 @@ describe('Decrypt codec tests', function () { password: '', }; - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error('cannot decrypt without password')); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error('cannot decrypt without password')); const result = await agent .post('/api/v1/decrypt') @@ -323,7 +323,7 @@ describe('Decrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error('Invalid encrypted input format')); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error('Invalid encrypted input format')); const result = await agent .post('/api/v2/decrypt') @@ -341,7 +341,7 @@ describe('Decrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error('Decrypt method not available')); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error('Decrypt method not available')); const result = await agent .post('/api/v1/decrypt') diff --git a/modules/express/test/unit/typedRoutes/encrypt.ts b/modules/express/test/unit/typedRoutes/encrypt.ts index 98d949ac6c..de8f8d9102 100644 --- a/modules/express/test/unit/typedRoutes/encrypt.ts +++ b/modules/express/test/unit/typedRoutes/encrypt.ts @@ -197,7 +197,7 @@ describe('Encrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockEncryptResponse); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockEncryptResponse); const result = await agent .post('/api/v1/encrypt') @@ -219,7 +219,7 @@ describe('Encrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockEncryptResponse); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockEncryptResponse); const result = await agent .post('/api/v2/encrypt') @@ -242,7 +242,7 @@ describe('Encrypt codec tests', function () { adata: 'additionalAuthData', }; - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockEncryptResponse); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockEncryptResponse); const result = await agent .post('/api/v1/encrypt') @@ -264,7 +264,7 @@ describe('Encrypt codec tests', function () { adata: 'additionalAuthData', }; - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockEncryptResponse); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockEncryptResponse); const result = await agent .post('/api/v2/encrypt') @@ -286,7 +286,7 @@ describe('Encrypt codec tests', function () { }; const mockLongEncrypted = 'b'.repeat(1500); - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockLongEncrypted); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockLongEncrypted); const result = await agent .post('/api/v1/encrypt') @@ -307,7 +307,7 @@ describe('Encrypt codec tests', function () { password: 'p@ssw0rd!#$%^&*()', }; - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockEncryptResponse); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockEncryptResponse); const result = await agent .post('/api/v2/encrypt') @@ -328,7 +328,7 @@ describe('Encrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'encryptAsync').resolves(mockEncryptResponse); + sinon.stub(BitGo.prototype, 'encrypt').resolves(mockEncryptResponse); const result = await agent .post('/api/v1/encrypt') @@ -361,7 +361,7 @@ describe('Encrypt codec tests', function () { password: '', }; - sinon.stub(BitGo.prototype, 'encryptAsync').rejects(new Error('cannot encrypt without password')); + sinon.stub(BitGo.prototype, 'encrypt').rejects(new Error('cannot encrypt without password')); const result = await agent .post('/api/v1/encrypt') @@ -379,7 +379,7 @@ describe('Encrypt codec tests', function () { password: '', }; - sinon.stub(BitGo.prototype, 'encryptAsync').rejects(new Error('cannot encrypt without password')); + sinon.stub(BitGo.prototype, 'encrypt').rejects(new Error('cannot encrypt without password')); const result = await agent .post('/api/v2/encrypt') @@ -397,7 +397,7 @@ describe('Encrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'encryptAsync').rejects(new Error('Invalid input format')); + sinon.stub(BitGo.prototype, 'encrypt').rejects(new Error('Invalid input format')); const result = await agent .post('/api/v1/encrypt') @@ -415,7 +415,7 @@ describe('Encrypt codec tests', function () { password: 'mySecurePassword123', }; - sinon.stub(BitGo.prototype, 'encryptAsync').rejects(new Error('Encrypt method not available')); + sinon.stub(BitGo.prototype, 'encrypt').rejects(new Error('Encrypt method not available')); const result = await agent .post('/api/v2/encrypt') @@ -434,7 +434,7 @@ describe('Encrypt codec tests', function () { adata: 'invalidAdataFormat', }; - sinon.stub(BitGo.prototype, 'encryptAsync').rejects(new Error('Invalid adata format')); + sinon.stub(BitGo.prototype, 'encrypt').rejects(new Error('Invalid adata format')); const result = await agent .post('/api/v1/encrypt') diff --git a/modules/express/test/unit/typedRoutes/expressWalletUpdate.ts b/modules/express/test/unit/typedRoutes/expressWalletUpdate.ts index 0301dff87f..11a366c48e 100644 --- a/modules/express/test/unit/typedRoutes/expressWalletUpdate.ts +++ b/modules/express/test/unit/typedRoutes/expressWalletUpdate.ts @@ -110,17 +110,11 @@ describe('Express Wallet Update Typed Routes Tests', function () { }), bitgo: { decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' ), - encrypt: sinon.stub().callsFake(({ input }: { input: string }) => `encrypted_${input}`), - encryptAsync: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), + encrypt: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), put: putStub, }, } as any; @@ -233,17 +227,11 @@ describe('Express Wallet Update Typed Routes Tests', function () { }), bitgo: { decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' ), - encrypt: sinon.stub().callsFake(({ input }: { input: string }) => `encrypted_${input}`), - encryptAsync: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), + encrypt: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), put: putStub, }, } as any; diff --git a/modules/express/test/unit/typedRoutes/generateShareTSS.ts b/modules/express/test/unit/typedRoutes/generateShareTSS.ts index eed5274189..b8b1f464e3 100644 --- a/modules/express/test/unit/typedRoutes/generateShareTSS.ts +++ b/modules/express/test/unit/typedRoutes/generateShareTSS.ts @@ -472,7 +472,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); sinon .stub(EddsaUtils.prototype, 'createCommitmentShareFromTxRequest') .callsFake(mockEddsaUtils.createCommitmentShareFromTxRequest); @@ -550,7 +550,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); sinon .stub(EddsaUtils.prototype, 'createRShareFromTxRequest') .callsFake(mockEddsaUtils.createRShareFromTxRequest); @@ -626,7 +626,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); sinon .stub(EddsaUtils.prototype, 'createGShareFromTxRequest') .callsFake(mockEddsaUtils.createGShareFromTxRequest); @@ -679,7 +679,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); sinon .stub(EcdsaUtils.prototype, 'getOfflineSignerPaillierModulus') .callsFake(mockEcdsaUtils.getOfflineSignerPaillierModulus); @@ -757,7 +757,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); sinon.stub(EcdsaUtils.prototype, 'createOfflineKShare').callsFake(mockEcdsaUtils.createOfflineKShare); // Call API via supertest @@ -926,7 +926,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { ); // Mock decrypt to throw error - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error('Invalid passphrase')); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error('Invalid passphrase')); // Make the request const result = await agent @@ -959,7 +959,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request const result = await agent @@ -992,7 +992,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request with invalid share type for EDDSA (K is ECDSA only) const result = await agent @@ -1025,7 +1025,7 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); // Make the request with invalid share type const result = await agent diff --git a/modules/express/test/unit/typedRoutes/lightningPayment.ts b/modules/express/test/unit/typedRoutes/lightningPayment.ts index 54d882a021..9582e1733a 100644 --- a/modules/express/test/unit/typedRoutes/lightningPayment.ts +++ b/modules/express/test/unit/typedRoutes/lightningPayment.ts @@ -334,11 +334,6 @@ describe('Lightning Payment API Tests', function () { const mockBitgo: any = { setRequestTracer: sinon.stub(), decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' @@ -510,11 +505,6 @@ describe('Lightning Payment API Tests', function () { const mockBitgo: any = { setRequestTracer: sinon.stub(), decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' @@ -646,11 +636,6 @@ describe('Lightning Payment API Tests', function () { const mockBitgo: any = { setRequestTracer: sinon.stub(), decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' @@ -808,11 +793,6 @@ describe('Lightning Payment API Tests', function () { const mockBitgo: any = { setRequestTracer: sinon.stub(), decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' @@ -892,11 +872,6 @@ describe('Lightning Payment API Tests', function () { const mockBitgo: any = { setRequestTracer: sinon.stub(), decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' @@ -977,11 +952,6 @@ describe('Lightning Payment API Tests', function () { const mockBitgo: any = { setRequestTracer: sinon.stub(), decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' diff --git a/modules/express/test/unit/typedRoutes/ofcExtSignPayload.ts b/modules/express/test/unit/typedRoutes/ofcExtSignPayload.ts index f197558378..f59570bfc1 100644 --- a/modules/express/test/unit/typedRoutes/ofcExtSignPayload.ts +++ b/modules/express/test/unit/typedRoutes/ofcExtSignPayload.ts @@ -82,7 +82,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); const result = await agent .post('/api/v2/ofc/signPayload') @@ -100,7 +100,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { // Verify external signing mode operations assert.strictEqual(fsReadFileStub.calledOnce, true); - const decryptStub = BitGo.prototype.decryptAsync as sinon.SinonStub; + const decryptStub = BitGo.prototype.decrypt as sinon.SinonStub; assert.strictEqual(decryptStub.calledOnce, true); assert.strictEqual(decryptStub.calledWith({ password: walletPassphrase, input: encryptedPrivKey }), true); assert.strictEqual(mockCoin.signMessage.calledOnce, true); @@ -126,7 +126,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); const result = await agent .post('/api/v2/ofc/signPayload') @@ -161,7 +161,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); const result = await agent .post('/api/v2/ofc/signPayload') @@ -174,7 +174,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { assert.ok(decodedResponse.signature); // Verify decrypt was called with env passphrase - const decryptStub = BitGo.prototype.decryptAsync as sinon.SinonStub; + const decryptStub = BitGo.prototype.decrypt as sinon.SinonStub; assert.strictEqual(decryptStub.calledWith({ password: walletPassphrase, input: encryptedPrivKey }), true); }); @@ -258,7 +258,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { }) ); - sinon.stub(BitGo.prototype, 'decryptAsync').rejects(new Error('Invalid passphrase')); + sinon.stub(BitGo.prototype, 'decrypt').rejects(new Error('Invalid passphrase')); const result = await agent .post('/api/v2/ofc/signPayload') @@ -288,7 +288,7 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () { }; sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any); - sinon.stub(BitGo.prototype, 'decryptAsync').resolves(decryptedPrivKey); + sinon.stub(BitGo.prototype, 'decrypt').resolves(decryptedPrivKey); const result = await agent .post('/api/v2/ofc/signPayload') diff --git a/modules/express/test/unit/typedRoutes/signerMacaroon.ts b/modules/express/test/unit/typedRoutes/signerMacaroon.ts index e51ae31db6..781eb7a359 100644 --- a/modules/express/test/unit/typedRoutes/signerMacaroon.ts +++ b/modules/express/test/unit/typedRoutes/signerMacaroon.ts @@ -115,17 +115,11 @@ describe('Signer Macaroon Typed Routes Tests', function () { url: sinon.stub().returns(`/api/v2/${coin}/wallet/${walletId}`), bitgo: { decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' ), - encrypt: sinon.stub().callsFake(({ input }: { input: string }) => `encrypted_${input}`), - encryptAsync: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), + encrypt: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), put: putStub, }, baseCoin: { @@ -144,7 +138,6 @@ describe('Signer Macaroon Typed Routes Tests', function () { sinon.stub(BitGo.prototype, 'coin').returns(coinStub); sinon.stub(BitGo.prototype, 'decrypt').callsFake(walletStub.bitgo.decrypt); - sinon.stub(BitGo.prototype, 'decryptAsync').callsFake(walletStub.bitgo.decryptAsync); sinon.stub(BitGo.prototype, 'put').callsFake(putStub as any); const res = await agent.post(`/api/v2/${coin}/wallet/${walletId}/signermacaroon`).send({ @@ -237,17 +230,11 @@ describe('Signer Macaroon Typed Routes Tests', function () { url: sinon.stub().returns(`/api/v2/${coin}/wallet/${walletId}`), bitgo: { decrypt: sinon - .stub() - .returns( - 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' - ), - decryptAsync: sinon .stub() .resolves( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' ), - encrypt: sinon.stub().callsFake(({ input }: { input: string }) => `encrypted_${input}`), - encryptAsync: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), + encrypt: sinon.stub().callsFake(async ({ input }: { input: string }) => `encrypted_${input}`), put: putStub, }, baseCoin: { @@ -266,7 +253,6 @@ describe('Signer Macaroon Typed Routes Tests', function () { sinon.stub(BitGo.prototype, 'coin').returns(coinStub); sinon.stub(BitGo.prototype, 'decrypt').callsFake(walletStub.bitgo.decrypt); - sinon.stub(BitGo.prototype, 'decryptAsync').callsFake(walletStub.bitgo.decryptAsync); sinon.stub(BitGo.prototype, 'put').callsFake(putStub as any); const res = await agent.post(`/api/v2/${coin}/wallet/${walletId}/signermacaroon`).send({ diff --git a/modules/key-card/src/generateQrData.ts b/modules/key-card/src/generateQrData.ts index 697c63acda..c01232a2cc 100644 --- a/modules/key-card/src/generateQrData.ts +++ b/modules/key-card/src/generateQrData.ts @@ -1,6 +1,6 @@ import { BaseCoin } from '@bitgo/statics'; import { Keychain } from '@bitgo/sdk-core'; -import { encrypt, encryptAsync } from '@bitgo/sdk-api'; +import { encrypt } from '@bitgo/sdk-api'; import * as assert from 'assert'; import { GenerateLightningQrDataParams, @@ -128,21 +128,12 @@ function generateUserMasterPublicKeyQRData(publicKey: string): MasterPublicKeyQr }; } -function generatePasscodeQrData(passphrase: string, passcodeEncryptionCode: string): QrDataEntry { - const encryptedWalletPasscode = encrypt(passcodeEncryptionCode, passphrase); - return { - title: 'D: Encrypted Wallet Password', - description: 'This is the wallet password, encrypted client-side with a key held by BitGo.', - data: encryptedWalletPasscode, - }; -} - -async function generatePasscodeQrDataAsync( +async function generatePasscodeQrData( passphrase: string, passcodeEncryptionCode: string, encryptionVersion?: 1 | 2 ): Promise { - const encryptedWalletPasscode = await encryptAsync(passcodeEncryptionCode, passphrase, { encryptionVersion }); + const encryptedWalletPasscode = await encrypt(passcodeEncryptionCode, passphrase, { encryptionVersion }); return { title: 'D: Encrypted Wallet Password', description: 'This is the wallet password, encrypted client-side with a key held by BitGo.', @@ -197,24 +188,11 @@ function buildLightningQrData({ userAuthKeychain }: GenerateLightningQrDataParam }; } -export function generateQrData(params: GenerateQrDataParams): QrData { +export async function generateQrData(params: GenerateQrDataParams): Promise { const qrData = buildWalletQrData(params); if (params.passphrase && params.passcodeEncryptionCode) { - qrData.passcode = generatePasscodeQrData(params.passphrase, params.passcodeEncryptionCode); - } - - return qrData; -} - -/** - * Async version of {@link generateQrData} with v1/v2 auto-detect encrypt for Box D via `encryptAsync`. - */ -export async function generateQrDataAsync(params: GenerateQrDataParams): Promise { - const qrData = buildWalletQrData(params); - - if (params.passphrase && params.passcodeEncryptionCode) { - qrData.passcode = await generatePasscodeQrDataAsync( + qrData.passcode = await generatePasscodeQrData( params.passphrase, params.passcodeEncryptionCode, params.encryptionVersion @@ -224,24 +202,11 @@ export async function generateQrDataAsync(params: GenerateQrDataParams): Promise return qrData; } -export function generateLightningQrData(params: GenerateLightningQrDataParams): QrData { - const qrData = buildLightningQrData(params); - - if (params.passphrase && params.passcodeEncryptionCode) { - qrData.passcode = generatePasscodeQrData(params.passphrase, params.passcodeEncryptionCode); - } - - return qrData; -} - -/** - * Async version of {@link generateLightningQrData} with v1/v2 auto-detect encrypt for Box D via `encryptAsync`. - */ -export async function generateLightningQrDataAsync(params: GenerateLightningQrDataParams): Promise { +export async function generateLightningQrData(params: GenerateLightningQrDataParams): Promise { const qrData = buildLightningQrData(params); if (params.passphrase && params.passcodeEncryptionCode) { - qrData.passcode = await generatePasscodeQrDataAsync( + qrData.passcode = await generatePasscodeQrData( params.passphrase, params.passcodeEncryptionCode, params.encryptionVersion diff --git a/modules/key-card/src/index.ts b/modules/key-card/src/index.ts index c4b627a96b..6409632a0c 100644 --- a/modules/key-card/src/index.ts +++ b/modules/key-card/src/index.ts @@ -1,4 +1,4 @@ -import { generateLightningQrDataAsync, generateQrDataAsync } from './generateQrData'; +import { generateLightningQrData, generateQrData } from './generateQrData'; import { generateFaq, generateLightningFaq } from './faq'; import { drawKeycard } from './drawKeycard'; import { generateParamsForKeyCreation } from './generateParamsForKeyCreation'; @@ -15,13 +15,13 @@ export * from './types'; export async function generateKeycard(params: GenerateKeycardParams): Promise { if ('userAuthKeychain' in params) { const questions = generateLightningFaq(params.coin.fullName); - const qrData = await generateLightningQrDataAsync(params); + const qrData = await generateLightningQrData(params); const keycard = await drawKeycard({ ...params, questions, qrData }); const label = params.walletLabel || params.coin.fullName; keycard.save(`BitGo Keycard for ${label}.pdf`); } else if ('coin' in params) { const questions = generateFaq(params.coin.fullName); - const qrData = await generateQrDataAsync(params); + const qrData = await generateQrData(params); const keycard = await drawKeycard({ ...params, questions, qrData }); const label = params.walletLabel || params.coin.fullName; keycard.save(`BitGo Keycard for ${label}.pdf`); @@ -39,7 +39,7 @@ export async function generateLightningKeycard( params: GenerateQrDataBaseParams & GenerateLightningQrDataParams ): Promise { const questions = generateLightningFaq(params.coin.fullName); - const qrData = await generateLightningQrDataAsync(params); + const qrData = await generateLightningQrData(params); const keycard = await drawKeycard({ ...params, questions, qrData }); const label = params.walletLabel || params.coin.fullName; keycard.save(`BitGo Keycard for ${label}.pdf`); diff --git a/modules/key-card/test/unit/generateQrData.ts b/modules/key-card/test/unit/generateQrData.ts index 87c4d56cf9..85caa474fd 100644 --- a/modules/key-card/test/unit/generateQrData.ts +++ b/modules/key-card/test/unit/generateQrData.ts @@ -1,12 +1,7 @@ import * as assert from 'assert'; import * as should from 'should'; -import { decrypt, decryptAsync } from '@bitgo/sdk-api'; -import { - generateLightningQrData, - generateLightningQrDataAsync, - generateQrData, - generateQrDataAsync, -} from '../../src/generateQrData'; +import { decrypt } from '@bitgo/sdk-api'; +import { generateLightningQrData, generateQrData } from '../../src/generateQrData'; import { ApiKeyShare, Keychain, KeyType } from '@bitgo/sdk-core'; import { coins } from '@bitgo/statics'; @@ -40,13 +35,13 @@ function createKeychain({ } describe('generateQrData', function () { - it('hot wallet, backup key provided by user with encryptedPrv', function () { + it('hot wallet, backup key provided by user with encryptedPrv', async function () { const userEncryptedPrv = 'prv123encrypted'; const backupEncryptedPrv = 'prv456encrypted'; const bitgoPub = 'pub789bitgo'; const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = generateQrData({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ encryptedPrv: backupEncryptedPrv, }), @@ -80,7 +75,7 @@ describe('generateQrData', function () { qrData.passcode.description.should.equal( 'This is the wallet password, encrypted client-side with a key held by BitGo.' ); - const decryptedData = decrypt(passcodeEncryptionCode, qrData.passcode.data); + const decryptedData = await decrypt(passcodeEncryptionCode, qrData.passcode.data); decryptedData.should.equal(passphrase); }); @@ -91,13 +86,13 @@ describe('generateQrData', function () { { coinName: 'eth', keyType: 'blsdkg' }, ]; for (const testSet of testSets) { - it(`key type ${testSet.keyType}`, function () { + it(`key type ${testSet.keyType}`, async function () { const userPub = 'pub012user'; const userMasterKey = 'userMasterKey'; const backupPub = 'pub345backup'; const backupMasterKey = 'backupMasterKey'; const bitgoPub = 'pub789bitgo'; - const qrData = generateQrData({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ commonKeychain: testSet.keyType === 'tss' ? backupPub : undefined, commonPub: testSet.keyType === 'blsdkg' ? backupPub : undefined, @@ -141,12 +136,12 @@ describe('generateQrData', function () { }); describe('generateLightningQrData', function () { - it('lightning wallet with encrypted key and passcode', function () { + it('lightning wallet with encrypted key and passcode', async function () { const userAuthEncryptedPrv = 'userAuthPrv123encrypted'; const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = generateLightningQrData({ + const qrData = await generateLightningQrData({ userAuthKeychain: createKeychain({ encryptedPrv: userAuthEncryptedPrv }), coin: coins.get('lnbtc'), passcodeEncryptionCode, @@ -162,12 +157,12 @@ describe('generateQrData', function () { assert.ok(qrData.passcode); qrData.passcode.title.should.equal('D: Encrypted Wallet Password'); - const decryptedData = decrypt(passcodeEncryptionCode, qrData.passcode.data); + const decryptedData = await decrypt(passcodeEncryptionCode, qrData.passcode.data); decryptedData.should.equal(passphrase); }); - it('lightning wallet without passcode', function () { - const qrData = generateLightningQrData({ + it('lightning wallet without passcode', async function () { + const qrData = await generateLightningQrData({ userAuthKeychain: createKeychain({ encryptedPrv: 'userAuthPrv' }), coin: coins.get('lnbtc'), }); @@ -175,8 +170,8 @@ describe('generateQrData', function () { should.not.exist(qrData.passcode); }); - it('throws when userAuthKeychain is missing encryptedPrv', function () { - assert.throws( + it('throws when userAuthKeychain is missing encryptedPrv', async function () { + await assert.rejects( () => generateLightningQrData({ userAuthKeychain: createKeychain({ pub: 'pub123' }), @@ -187,13 +182,13 @@ describe('generateQrData', function () { }); }); - it('backup key from provider', function () { + it('backup key from provider', async function () { const coin = coins.get('btc'); const userEncryptedPrv = 'prv123encrypted'; const backupPub = 'pub673backup'; const provider = '3rd Party Provider'; const bitgoPub = 'pub789bitgo'; - const qrData = generateQrData({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ pub: backupPub, provider, @@ -228,11 +223,11 @@ describe('generateQrData', function () { }); }); -describe('generateQrDataAsync', function () { - it('encrypts passcode with encryptAsync', async function () { +describe('generateQrData', function () { + it('encrypts passcode with encrypt', async function () { const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = await generateQrDataAsync({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ encryptedPrv: 'backupPrv' }), bitgoKeychain: createKeychain({ pub: 'bitgoPub' }), coin: coins.get('btc'), @@ -242,14 +237,14 @@ describe('generateQrDataAsync', function () { }); assert.ok(qrData.passcode); - const decryptedData = await decryptAsync(passcodeEncryptionCode, qrData.passcode.data); + const decryptedData = await decrypt(passcodeEncryptionCode, qrData.passcode.data); decryptedData.should.equal(passphrase); }); - it('produces a v1 Box D when encryptionVersion is not set', async function () { + it('produces a v2 Box D when encryptionVersion is not set', async function () { const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = await generateQrDataAsync({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ encryptedPrv: 'backupPrv' }), bitgoKeychain: createKeychain({ pub: 'bitgoPub' }), coin: coins.get('btc'), @@ -260,13 +255,13 @@ describe('generateQrDataAsync', function () { assert.ok(qrData.passcode); const envelope = JSON.parse(qrData.passcode.data); - assert.notStrictEqual(envelope.v, 2, 'should default to v1 envelope'); + assert.strictEqual(envelope.v, 2, 'should default to v2 envelope'); }); it('produces a v2 Box D when encryptionVersion: 2', async function () { const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = await generateQrDataAsync({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ encryptedPrv: 'backupPrv' }), bitgoKeychain: createKeychain({ pub: 'bitgoPub' }), coin: coins.get('btc'), @@ -279,14 +274,14 @@ describe('generateQrDataAsync', function () { assert.ok(qrData.passcode); const envelope = JSON.parse(qrData.passcode.data); assert.strictEqual(envelope.v, 2, 'should produce v2 envelope'); - const decryptedData = await decryptAsync(passcodeEncryptionCode, qrData.passcode.data); + const decryptedData = await decrypt(passcodeEncryptionCode, qrData.passcode.data); decryptedData.should.equal(passphrase); }); it('produces a v1 Box D when encryptionVersion: 1 is explicit', async function () { const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = await generateQrDataAsync({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ encryptedPrv: 'backupPrv' }), bitgoKeychain: createKeychain({ pub: 'bitgoPub' }), coin: coins.get('btc'), @@ -302,7 +297,7 @@ describe('generateQrDataAsync', function () { }); it('omits Box D when passphrase or passcodeEncryptionCode is missing', async function () { - const qrData = await generateQrDataAsync({ + const qrData = await generateQrData({ backupKeychain: createKeychain({ encryptedPrv: 'backupPrv' }), bitgoKeychain: createKeychain({ pub: 'bitgoPub' }), coin: coins.get('btc'), @@ -313,11 +308,11 @@ describe('generateQrDataAsync', function () { }); }); -describe('generateLightningQrDataAsync', function () { - it('encrypts passcode with encryptAsync', async function () { +describe('generateLightningQrData', function () { + it('encrypts passcode with encrypt', async function () { const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = await generateLightningQrDataAsync({ + const qrData = await generateLightningQrData({ userAuthKeychain: createKeychain({ encryptedPrv: 'userAuthPrv' }), coin: coins.get('lnbtc'), passcodeEncryptionCode, @@ -325,14 +320,14 @@ describe('generateLightningQrDataAsync', function () { }); assert.ok(qrData.passcode); - const decryptedData = await decryptAsync(passcodeEncryptionCode, qrData.passcode.data); + const decryptedData = await decrypt(passcodeEncryptionCode, qrData.passcode.data); decryptedData.should.equal(passphrase); }); it('produces a v2 Box D when encryptionVersion: 2', async function () { const passphrase = 'testingIsFun'; const passcodeEncryptionCode = '123456'; - const qrData = await generateLightningQrDataAsync({ + const qrData = await generateLightningQrData({ userAuthKeychain: createKeychain({ encryptedPrv: 'userAuthPrv' }), coin: coins.get('lnbtc'), passcodeEncryptionCode, @@ -343,7 +338,7 @@ describe('generateLightningQrDataAsync', function () { assert.ok(qrData.passcode); const envelope = JSON.parse(qrData.passcode.data); assert.strictEqual(envelope.v, 2); - const decryptedData = await decryptAsync(passcodeEncryptionCode, qrData.passcode.data); + const decryptedData = await decrypt(passcodeEncryptionCode, qrData.passcode.data); decryptedData.should.equal(passphrase); }); }); diff --git a/modules/passkey-crypto/src/attachPasskeyToWallet.ts b/modules/passkey-crypto/src/attachPasskeyToWallet.ts index 537faeb149..2771abccd1 100644 --- a/modules/passkey-crypto/src/attachPasskeyToWallet.ts +++ b/modules/passkey-crypto/src/attachPasskeyToWallet.ts @@ -45,7 +45,7 @@ export async function attachPasskeyToWallet(params: { const prfSalt = deriveEnterpriseSalt(device.prfSalt, enterpriseId); // Decrypt private key with existing passphrase - const privateKey = await bitgo.decryptAsync({ password: existingPassphrase, input: keychain.encryptedPrv }); + const privateKey = await bitgo.decrypt({ password: existingPassphrase, input: keychain.encryptedPrv }); // Decode credentialId from base64url to ArrayBuffer for allowCredentials. // The WebAuthn spec requires allowCredentials to be non-empty when using evalByCredential, @@ -67,7 +67,7 @@ export async function attachPasskeyToWallet(params: { } const prfPassword = derivePassword(authResult.prfResult); - const encryptedPrv = await bitgo.encryptAsync({ password: prfPassword, input: privateKey, encryptionVersion }); + const encryptedPrv = await bitgo.encrypt({ password: prfPassword, input: privateKey, encryptionVersion }); const updatedKeychain = await bitgo .put(bitgo.url(`/${coin}/key/${keychainId}`, 2)) diff --git a/modules/passkey-crypto/src/removePasskeyFromWallet.ts b/modules/passkey-crypto/src/removePasskeyFromWallet.ts index 831057d9ec..d047f367be 100644 --- a/modules/passkey-crypto/src/removePasskeyFromWallet.ts +++ b/modules/passkey-crypto/src/removePasskeyFromWallet.ts @@ -1,4 +1,4 @@ -import { BitGoBase, decryptKeychainPrivateKeyAsync } from '@bitgo/sdk-core'; +import { BitGoBase, decryptKeychainPrivateKey } from '@bitgo/sdk-core'; import { WebAuthnOtpDevice } from './webAuthnTypes'; export async function removePasskeyFromWallet(params: { @@ -20,7 +20,7 @@ export async function removePasskeyFromWallet(params: { const keychain = await baseCoin.keychains().get({ id: keychainId }); // Verify passphrase before any mutation - const decrypted = await decryptKeychainPrivateKeyAsync(bitgo, keychain, walletPassphrase); + const decrypted = await decryptKeychainPrivateKey(bitgo, keychain, walletPassphrase); if (!decrypted) { throw new Error('Incorrect wallet passphrase. Passkey removal aborted to prevent lockout.'); } diff --git a/modules/passkey-crypto/test/unit/attachPasskeyToWallet.test.ts b/modules/passkey-crypto/test/unit/attachPasskeyToWallet.test.ts index f51d8573d2..561746a142 100644 --- a/modules/passkey-crypto/test/unit/attachPasskeyToWallet.test.ts +++ b/modules/passkey-crypto/test/unit/attachPasskeyToWallet.test.ts @@ -54,8 +54,8 @@ describe('attachPasskeyToWallet', function () { url: sinon.SinonStub; coin: sinon.SinonStub; put: sinon.SinonStub; - decryptAsync: sinon.SinonStub; - encryptAsync: sinon.SinonStub; + decrypt: sinon.SinonStub; + encrypt: sinon.SinonStub; }; let mockProvider: { @@ -84,8 +84,8 @@ describe('attachPasskeyToWallet', function () { .callsFake((path, version) => `/api/v${version ?? 1}${path}`), coin: sinon.stub().returns(mockBaseCoin), put: sinon.stub(), - decryptAsync: sinon.stub(), - encryptAsync: sinon.stub(), + decrypt: sinon.stub(), + encrypt: sinon.stub(), }; mockProvider = { @@ -93,8 +93,8 @@ describe('attachPasskeyToWallet', function () { get: sinon.stub(), }; - mockBitGo.decryptAsync.resolves(decryptedPrv); - mockBitGo.encryptAsync.resolves(reEncryptedPrv); + mockBitGo.decrypt.resolves(decryptedPrv); + mockBitGo.encrypt.resolves(reEncryptedPrv); const putSendStub = sinon.stub().returns({ result: sinon.stub().resolves(updatedKeychain) }); mockBitGo.put.returns({ send: putSendStub }); @@ -126,8 +126,8 @@ describe('attachPasskeyToWallet', function () { sinon.assert.calledWith(mockWallets.get, { id: walletId }); sinon.assert.calledOnce(mockWallet.type); sinon.assert.calledOnce(mockWallet.getEncryptedUserKeychain); - sinon.assert.calledOnce(mockBitGo.decryptAsync); - sinon.assert.calledWithExactly(mockBitGo.decryptAsync, { password: existingPassphrase, input: encryptedPrv }); + sinon.assert.calledOnce(mockBitGo.decrypt); + sinon.assert.calledWithExactly(mockBitGo.decrypt, { password: existingPassphrase, input: encryptedPrv }); // provider.get called with evalByCredential keyed on device.credentialId sinon.assert.calledOnce(mockProvider.get); @@ -153,9 +153,9 @@ describe('attachPasskeyToWallet', function () { assert.match(putBody.webauthnInfo.prfSalt, /^[A-Za-z0-9\-_]+$/); assert.strictEqual(typeof putBody.webauthnInfo.encryptedPrv, 'string'); - // encryptAsync must be called with encryptionVersion 2 - sinon.assert.calledOnce(mockBitGo.encryptAsync); - sinon.assert.calledWithMatch(mockBitGo.encryptAsync, { encryptionVersion: 2 }); + // encrypt must be called with encryptionVersion 2 + sinon.assert.calledOnce(mockBitGo.encrypt); + sinon.assert.calledWithMatch(mockBitGo.encrypt, { encryptionVersion: 2 }); assert.strictEqual(result.id, keychainId); }); @@ -165,14 +165,14 @@ describe('attachPasskeyToWallet', function () { await callAttach(); - // The PRF-derived password and the decrypted xprv must be passed to encryptAsync - sinon.assert.calledWithMatch(mockBitGo.encryptAsync, { + // The PRF-derived password and the decrypted xprv must be passed to encrypt + sinon.assert.calledWithMatch(mockBitGo.encrypt, { password: expectedPrfPassword, input: decryptedPrv, encryptionVersion: 2, }); - // The v2 blob returned by encryptAsync is what gets stored on the server + // The v2 blob returned by encrypt is what gets stored on the server const putBody = mockBitGo.put.firstCall.returnValue.send.firstCall.args[0]; assert.strictEqual(putBody.webauthnInfo.encryptedPrv, reEncryptedPrv); }); @@ -246,7 +246,7 @@ describe('attachPasskeyToWallet', function () { }); it('should propagate decrypt errors', async function () { - mockBitGo.decryptAsync.rejects(new Error('decryption failed')); + mockBitGo.decrypt.rejects(new Error('decryption failed')); await assert.rejects( () => callAttach(), diff --git a/modules/passkey-crypto/test/unit/removePasskeyFromWallet.test.ts b/modules/passkey-crypto/test/unit/removePasskeyFromWallet.test.ts index 56cc029e10..e1b2012a9d 100644 --- a/modules/passkey-crypto/test/unit/removePasskeyFromWallet.test.ts +++ b/modules/passkey-crypto/test/unit/removePasskeyFromWallet.test.ts @@ -39,7 +39,7 @@ describe('removePasskeyFromWallet', function () { wallets: sinon.stub().returns(mockWallets), keychains: sinon.stub().returns(mockKeychains), }), - decryptAsync: sinon.stub().resolves('xprv-decrypted'), + decrypt: sinon.stub().resolves('xprv-decrypted'), del: sinon.stub().returns({ result: sinon.stub().resolves({}), }), @@ -75,7 +75,7 @@ describe('removePasskeyFromWallet', function () { }); it('should throw and not call DELETE if passphrase is wrong', async function () { - mockBitGo.decryptAsync = sinon.stub().resolves(undefined); + mockBitGo.decrypt = sinon.stub().resolves(undefined); await assert.rejects( () => diff --git a/modules/sdk-api/src/bitgoAPI.ts b/modules/sdk-api/src/bitgoAPI.ts index 8be93026b4..7cddbf0c49 100644 --- a/modules/sdk-api/src/bitgoAPI.ts +++ b/modules/sdk-api/src/bitgoAPI.ts @@ -41,7 +41,7 @@ import { toBitgoRequest, verifyResponseAsync, } from './api'; -import { decrypt, decryptAsync, encrypt, encryptAsync } from './encrypt'; +import { decrypt, encrypt } from './encrypt'; import { createEncryptionSession } from './encryptionSession'; import { verifyAddress } from './v1/verifyAddress'; import { @@ -834,26 +834,15 @@ export class BitGoAPI implements BitGoBase { } /** - * Utility function to encrypt locally. + * Utility function to encrypt locally. Defaults to v2 (Argon2id + AES-256-GCM); + * pass `encryptionVersion: 1` to produce a legacy v1 (SJCL) envelope. */ - encrypt(params: EncryptOptions): string { + async encrypt(params: EncryptOptions): Promise { common.validateParams(params, ['input', 'password'], ['adata']); - if (!params.password) { - throw new Error(`cannot encrypt without password`); - } - return encrypt(params.password, params.input, { adata: params.adata }); - } - - /** - * Async encrypt that dispatches to v1 (SJCL) or v2 (Argon2id + AES-256-GCM) - * based on `encryptionVersion`. - */ - async encryptAsync(params: EncryptOptions): Promise { - common.validateParams(params, ['input', 'password'], []); if (!params.password) { throw new Error('cannot encrypt without password'); } - return await encryptAsync(params.password, params.input, { + return await encrypt(params.password, params.input, { adata: params.adata, encryptionVersion: params.encryptionVersion, }); @@ -868,36 +857,16 @@ export class BitGoAPI implements BitGoBase { } /** - * Decrypt an encrypted string locally. - */ - decrypt(params: DecryptOptions): string { - params = params || {}; - common.validateParams(params, ['input', 'password'], []); - if (!params.password) { - throw new Error(`cannot decrypt without password`); - } - try { - return decrypt(params.password, params.input); - } catch (error) { - if (error.message.includes("ccm: tag doesn't match")) { - error.message = 'password error - ' + error.message; - } - throw error; - } - } - - /** - * Async decrypt that auto-detects v1 (SJCL) or v2 (Argon2id). - * Migration path from sync decrypt() -- use this before the breaking release. + * Decrypt an encrypted string locally. Auto-detects v1 (SJCL) or v2 (Argon2id). */ - async decryptAsync(params: DecryptOptions): Promise { + async decrypt(params: DecryptOptions): Promise { params = params || {}; common.validateParams(params, ['input', 'password'], []); if (!params.password) { throw new Error(`cannot decrypt without password`); } try { - return await decryptAsync(params.password, params.input); + return await decrypt(params.password, params.input); } catch (error) { if ( error.message.includes("ccm: tag doesn't match") || @@ -910,50 +879,13 @@ export class BitGoAPI implements BitGoBase { } /** - * TODO: deprecate this function in favor of decryptKeysAsync once v2 encryption is default - * Attempt to decrypt multiple wallet keys with the provided passphrase + * Attempt to decrypt multiple wallet keys with the provided passphrase. * @param {DecryptKeysOptions} params - Parameters object containing wallet key pairs and password * @param {Array<{walletId: string, encryptedPrv: string}>} params.walletIdEncryptedKeyPairs - Array of wallet ID and encrypted private key pairs * @param {string} params.password - The passphrase to attempt decryption with - * @returns {string[]} - Array of wallet IDs for which decryption failed - */ - decryptKeys(params: DecryptKeysOptions): string[] { - const validatedParams = validateDecryptKeysParams(params); - if (validatedParams.walletIdEncryptedKeyPairs.length === 0) { - return []; - } - - const failedWalletIds: string[] = []; - - for (const keyPair of validatedParams.walletIdEncryptedKeyPairs) { - if (!keyPair.walletId || typeof keyPair.walletId !== 'string') { - throw new Error('each key pair must have a string walletId'); - } - - if (!keyPair.encryptedPrv || typeof keyPair.encryptedPrv !== 'string') { - throw new Error('each key pair must have a string encryptedPrv'); - } - - try { - this.decrypt({ - input: keyPair.encryptedPrv, - password: validatedParams.password, - }); - // If no error was thrown, decryption was successful - } catch (error) { - // If decryption fails, add the walletId to the failed list - failedWalletIds.push(keyPair.walletId); - } - } - - return failedWalletIds; - } - - /** - * Async version of decryptKeys with v2 encrypt/decrypt support. - * @param params + * @returns {Promise} - Array of wallet IDs for which decryption failed */ - async decryptKeysAsync(params: DecryptKeysOptions): Promise { + async decryptKeys(params: DecryptKeysOptions): Promise { const validatedParams = validateDecryptKeysParams(params); if (validatedParams.walletIdEncryptedKeyPairs.length === 0) { return []; @@ -971,7 +903,7 @@ export class BitGoAPI implements BitGoBase { } try { - await this.decryptAsync({ + await this.decrypt({ input: keyPair.encryptedPrv, password: validatedParams.password, }); @@ -1137,7 +1069,7 @@ export class BitGoAPI implements BitGoBase { return await this.keychains().add({ source: 'ecdh', xpub: hdNode.neutered().toBase58(), - encryptedXprv: await this.encryptAsync({ + encryptedXprv: await this.encrypt({ password: loginPassword, input: hdNode.toBase58(), encryptionVersion, @@ -1237,7 +1169,7 @@ export class BitGoAPI implements BitGoBase { throw new Error('Keychain needs encryptedXprv property'); } - const responseDetails = await this.handleTokenIssuanceAsync(response.body, password); + const responseDetails = await this.handleTokenIssuance(response.body, password); this._token = responseDetails.token; this._ecdhXprv = responseDetails.ecdhXprv; @@ -1313,59 +1245,11 @@ export class BitGoAPI implements BitGoBase { } /** - * TODO: Deprecate this function in favor of handleTokenIssuanceAsync once v2 encryption is default. - * @param responseBody Response body object - * @param password Password for the symmetric decryption - */ - handleTokenIssuance(responseBody: TokenIssuanceResponse, password?: string): TokenIssuance { - // make sure the response body contains the necessary properties - common.validateParams(responseBody, ['derivationPath'], ['encryptedECDHXprv']); - - const environment = this._env; - const environmentConfig = common.Environments[environment]; - const serverXpub = environmentConfig.serverXpub; - let ecdhXprv = this._ecdhXprv; - if (!ecdhXprv) { - if (!password || !responseBody.encryptedECDHXprv) { - throw new Error('ecdhXprv property must be set or password and encrypted encryptedECDHXprv must be provided'); - } - try { - ecdhXprv = this.decrypt({ - input: responseBody.encryptedECDHXprv, - password: password, - }); - } catch (e) { - e.errorCode = 'ecdh_xprv_decryption_failure'; - console.error('Failed to decrypt encryptedECDHXprv.'); - throw e; - } - } - - const secret = deriveTokenIssuanceEcdhSecret(ecdhXprv, responseBody.derivationPath, serverXpub); - - try { - const token = this.decrypt({ - input: responseBody.encryptedToken, - password: secret, - }); - const response: TokenIssuance = { token }; - if (!this._ecdhXprv) { - response.ecdhXprv = ecdhXprv; - } - return response; - } catch (e) { - e.errorCode = 'token_decryption_failure'; - console.error('Failed to decrypt token.'); - throw e; - } - } - - /** - * Async version of handleTokenIssuance with v2 encrypt/decrypt support. + * Decrypt an issued token (and the ECDH xprv when needed). Auto-detects v1/v2 envelopes. * @param responseBody Response body object * @param password Password for the symmetric decryption */ - async handleTokenIssuanceAsync(responseBody: TokenIssuanceResponse, password?: string): Promise { + async handleTokenIssuance(responseBody: TokenIssuanceResponse, password?: string): Promise { // make sure the response body contains the necessary properties common.validateParams(responseBody, ['derivationPath'], ['encryptedECDHXprv']); @@ -1378,7 +1262,7 @@ export class BitGoAPI implements BitGoBase { throw new Error('ecdhXprv property must be set or password and encrypted encryptedECDHXprv must be provided'); } try { - ecdhXprv = await this.decryptAsync({ + ecdhXprv = await this.decrypt({ input: responseBody.encryptedECDHXprv, password: password, }); @@ -1392,7 +1276,7 @@ export class BitGoAPI implements BitGoBase { const secret = deriveTokenIssuanceEcdhSecret(ecdhXprv, responseBody.derivationPath, serverXpub); try { - const token = await this.decryptAsync({ + const token = await this.decrypt({ input: responseBody.encryptedToken, password: secret, }); @@ -1582,7 +1466,7 @@ export class BitGoAPI implements BitGoBase { // absent (e.g. SSO/WebCrypto users), the server includes the plain token // directly in response.body.token — no decryption step needed. if (this._ecdhXprv) { - const responseDetails = await this.handleTokenIssuanceAsync(response.body); + const responseDetails = await this.handleTokenIssuance(response.body); response.body.token = responseDetails.token; } @@ -1925,92 +1809,42 @@ export class BitGoAPI implements BitGoBase { } /** - * TODO: deprecate this function in favor of splitSecretAsync when v2 encryption is the default * Split a secret into shards using Shamir Secret Sharing. * @param seed A hexadecimal secret to split * @param passwords An array of the passwords used to encrypt each share * @param m The threshold number of shards necessary to reconstitute the secret + * @param encryptionVersion Optional encryption version for the encrypted shards (defaults to v2) */ - splitSecret({ seed, passwords, m }: SplitSecretOptions): SplitSecret { - const n = validateSplitSecretInputs({ seed, passwords, m }); - const secrets: string[] = shamir.share(seed, n, m); - const shards = _.zipWith(secrets, passwords, (shard, password) => { - return this.encrypt({ input: shard, password }); - }); - return buildSplitSecretResult(seed, shards, m, n); - } - - /** - * Async version of splitSecret with v2 encrypt/decrypt support. - * @param seed - * @param passwords - * @param m - */ - async splitSecretAsync({ seed, passwords, m, encryptionVersion }: SplitSecretOptions): Promise { + async splitSecret({ seed, passwords, m, encryptionVersion }: SplitSecretOptions): Promise { const n = validateSplitSecretInputs({ seed, passwords, m }); const secrets: string[] = shamir.share(seed, n, m); const shards = await Promise.all( - secrets.map((shard, i) => this.encryptAsync({ input: shard, password: passwords[i], encryptionVersion })) + secrets.map((shard, i) => this.encrypt({ input: shard, password: passwords[i], encryptionVersion })) ); return buildSplitSecretResult(seed, shards, m, n); } /** - * TODO: deprecate this function in favor of reconstituteSecretAsync when v2 encryption is the default * Reconstitute a secret which was sharded with `splitSecret`. * @param shards * @param passwords */ - reconstituteSecret({ shards, passwords }: ReconstituteSecretOptions): ReconstitutedSecret { - validateReconstituteInputs({ shards, passwords }); - const secrets = _.zipWith(shards, passwords, (shard, password) => { - return this.decrypt({ input: shard, password }); - }); - const seed: string = shamir.combine(secrets); - return buildReconstitutedSecret(seed); - } - - /** - * Async version of reconstituteSecret with v2 encrypt/decrypt support. - * @param shards - * @param passwords - */ - async reconstituteSecretAsync({ shards, passwords }: ReconstituteSecretOptions): Promise { + async reconstituteSecret({ shards, passwords }: ReconstituteSecretOptions): Promise { validateReconstituteInputs({ shards, passwords }); - const secrets = await Promise.all( - shards.map((shard, i) => this.decryptAsync({ input: shard, password: passwords[i] })) - ); + const secrets = await Promise.all(shards.map((shard, i) => this.decrypt({ input: shard, password: passwords[i] }))); const seed: string = shamir.combine(secrets); return buildReconstitutedSecret(seed); } /** - * TODO: Deprecate this function in favour of verifyShardsAsync when v2 encryption is the default. * @param shards * @param passwords * @param m * @param xpub Optional xpub to verify the results against */ - verifyShards({ shards, passwords, m, xpub }: VerifyShardsOptions): boolean { - validateReconstituteInputs({ shards, passwords }); - const secrets = _.zipWith(shards, passwords, (shard, password) => { - return this.decrypt({ input: shard, password }); - }); - return verifyShardSecrets(secrets, m, xpub); - } - - /** - * Async version of verifyShards with v2 encrypt/decrypt support. - * @param shards - * @param passwords - * @param m - * @param xpub - */ - async verifyShardsAsync({ shards, passwords, m, xpub }: VerifyShardsOptions): Promise { + async verifyShards({ shards, passwords, m, xpub }: VerifyShardsOptions): Promise { validateReconstituteInputs({ shards, passwords }); - const secrets = await Promise.all( - shards.map((shard, i) => this.decryptAsync({ input: shard, password: passwords[i] })) - ); + const secrets = await Promise.all(shards.map((shard, i) => this.decrypt({ input: shard, password: passwords[i] }))); return verifyShardSecrets(secrets, m, xpub); } @@ -2056,7 +1890,7 @@ export class BitGoAPI implements BitGoBase { const userEcdhKeychain = await this.getECDHKeychain(userSigningKey.ecdhKeychain); let xprv; try { - xprv = await this.decryptAsync({ + xprv = await this.decrypt({ password: password, input: userEcdhKeychain.encryptedXprv, }); diff --git a/modules/sdk-api/src/encrypt.ts b/modules/sdk-api/src/encrypt.ts index b3f3dc9827..685ad3383a 100644 --- a/modules/sdk-api/src/encrypt.ts +++ b/modules/sdk-api/src/encrypt.ts @@ -16,8 +16,13 @@ export function bytesToWord(bytes?: Uint8Array | number[]): number { return bytes.reduce((num, byte) => num * 0x100 + byte, 0); } -/** Encrypt using legacy v1 SJCL (PBKDF2-SHA256 + AES-256-CCM). */ -export function encrypt( +/** + * Internal v1 (SJCL PBKDF2-SHA256 + AES-256-CCM) encrypt helper. + * + * Not exported as part of the public encrypt/decrypt surface: callers must not use + * this directly. v1 output is requested via `encrypt(..., { encryptionVersion: 1 })`. + */ +function encryptV1( password: string, plaintext: string, options?: { salt?: Buffer; iv?: Buffer; adata?: string } @@ -43,32 +48,34 @@ export function encrypt( } /** - * Async encrypt that dispatches to v1 (SJCL) or v2 (Argon2id + AES-256-GCM) - * when `encryptionVersion` is 2. Defaults to v1, matching sync `encrypt()`. + * Encrypt `plaintext` with `password`. Defaults to v2 (Argon2id + AES-256-GCM). + * + * Pass `encryptionVersion: 1` to produce a legacy v1 (SJCL) envelope; this is the + * only supported way to request v1 encryption. */ -export async function encryptAsync( +export async function encrypt( password: string, plaintext: string, options?: { salt?: Buffer; iv?: Buffer; adata?: string; encryptionVersion?: 1 | 2 } ): Promise { - if (options?.encryptionVersion === 2) { - return encryptV2(password, plaintext, { adata: options.adata }); + if (options?.encryptionVersion === 1) { + return encryptV1(password, plaintext, options); } - return encrypt(password, plaintext, options); + return encryptV2(password, plaintext, { adata: options?.adata }); } -/** Decrypt a v1 SJCL envelope. */ -export function decrypt(password: string, ciphertext: string): string { +/** + * Internal v1 (SJCL) decrypt helper. Not part of the public surface: callers use + * the auto-detecting `decrypt` instead. + */ +function decryptV1(password: string, ciphertext: string): string { return sjcl.decrypt(password, ciphertext); } /** * Auto-detect v1 (SJCL) or v2 (Argon2id + AES-256-GCM) from the envelope `v` field and decrypt. - * - * Migration path from sync `decrypt()`. Move call sites to `decryptAsync()` before - * the breaking release that flips the default to v2. */ -export async function decryptAsync(password: string, ciphertext: string): Promise { +export async function decrypt(password: string, ciphertext: string): Promise { let envelopeVersion: number | undefined; try { const envelope = JSON.parse(ciphertext); @@ -83,5 +90,5 @@ export async function decryptAsync(password: string, ciphertext: string): Promis if (envelopeVersion !== undefined && envelopeVersion !== 1) { throw new Error(`decrypt: unknown envelope version ${envelopeVersion}`); } - return sjcl.decrypt(password, ciphertext); + return decryptV1(password, ciphertext); } diff --git a/modules/sdk-api/src/v1/keychains.ts b/modules/sdk-api/src/v1/keychains.ts index f9ce3fdaf3..bc557563a1 100644 --- a/modules/sdk-api/src/v1/keychains.ts +++ b/modules/sdk-api/src/v1/keychains.ts @@ -190,12 +190,12 @@ Keychains.prototype.updatePassword = function (params, callback) { const self = this; for (const [xpub, oldEncryptedXprv] of Object.entries((encrypted as any).keychains)) { try { - const decryptedPrv = await self.bitgo.decryptAsync({ + const decryptedPrv = await self.bitgo.decrypt({ input: oldEncryptedXprv as string, password: params.oldPassword, }); const encryptionVersion = isV2Envelope(oldEncryptedXprv as string) ? 2 : 1; - const newEncryptedPrv = await self.bitgo.encryptAsync({ + const newEncryptedPrv = await self.bitgo.encrypt({ input: decryptedPrv, password: params.newPassword, encryptionVersion, diff --git a/modules/sdk-api/src/v1/travelRule.ts b/modules/sdk-api/src/v1/travelRule.ts index b234ac2797..5e296d67c4 100644 --- a/modules/sdk-api/src/v1/travelRule.ts +++ b/modules/sdk-api/src/v1/travelRule.ts @@ -198,35 +198,9 @@ function buildTravelRuleSendParams( * keychain: keychain object (with xprv) * Returns: * the tx object, augmented with decrypted travelInfo fields - * TODO: Deprecate in favor of decryptReceivedTravelInfoAsync once v2 encryption is default. + * Auto-detects v1 (SJCL) and v2 (Argon2id) envelopes. */ -TravelRule.prototype.decryptReceivedTravelInfo = function (params: DecryptReceivedTravelRuleOptions = {}) { - const { tx, hdNode } = validateTravelRuleDecryptParams(params); - if (!hdNode) { - return tx; - } - - tx!.receivedTravelInfo!.forEach((info) => { - const key = hdNode.derivePath(sanitizeLegacyPath(info.toPubKeyPath)); - const secret = getSharedSecret(key, Buffer.from(info.fromPubKey, 'hex')).toString('hex'); - try { - const decrypted = this.bitgo.decrypt({ - input: info.encryptedTravelInfo, - password: secret, - }); - info.travelInfo = JSON.parse(decrypted); - } catch (err) { - console.error('failed to decrypt or parse travel info for ', info.transactionId + ':' + info.outputIndex); - } - }); - - return tx; -}; - -/** - * Async version of decryptReceivedTravelInfo with v2 encrypt/decrypt support. - */ -TravelRule.prototype.decryptReceivedTravelInfoAsync = async function (params: DecryptReceivedTravelRuleOptions = {}) { +TravelRule.prototype.decryptReceivedTravelInfo = async function (params: DecryptReceivedTravelRuleOptions = {}) { const { tx, hdNode } = validateTravelRuleDecryptParams(params); if (!hdNode) { return tx; @@ -236,7 +210,7 @@ TravelRule.prototype.decryptReceivedTravelInfoAsync = async function (params: De const key = hdNode.derivePath(sanitizeLegacyPath(info.toPubKeyPath)); const secret = getSharedSecret(key, Buffer.from(info.fromPubKey, 'hex')).toString('hex'); try { - const decrypted = await this.bitgo.decryptAsync({ + const decrypted = await this.bitgo.decrypt({ input: info.encryptedTravelInfo, password: secret, }); @@ -249,25 +223,9 @@ TravelRule.prototype.decryptReceivedTravelInfoAsync = async function (params: De return tx; }; -/** - * TODO: Deprecate in favor of prepareParamsAsync once v2 encryption is default. - */ -TravelRule.prototype.prepareParams = function (params) { - const prepared = prepareTravelRuleParamsCommon(this, params); - const encryptedTravelInfo = this.bitgo.encrypt({ - input: prepared.travelInfoJSON, - password: prepared.sharedSecret, - }); - - return buildTravelRuleSendParams(prepared, encryptedTravelInfo); -}; - -/** - * Async version of prepareParams with v2 encrypt/decrypt support. - */ -TravelRule.prototype.prepareParamsAsync = async function (params) { +TravelRule.prototype.prepareParams = async function (params) { const prepared = prepareTravelRuleParamsCommon(this, params); - const encryptedTravelInfo = await this.bitgo.encryptAsync({ + const encryptedTravelInfo = await this.bitgo.encrypt({ input: prepared.travelInfoJSON, password: prepared.sharedSecret, encryptionVersion: params.encryptionVersion, @@ -355,7 +313,7 @@ TravelRule.prototype.sendMany = function (params, callback) { if (info.amount && info.amount !== recipient.amount) { throw new Error('amount did not match for output index ' + outputIndex); } - const sendParams = await self.prepareParamsAsync({ + const sendParams = await self.prepareParams({ txid: params.txid, recipient: recipient, travelInfo: info, diff --git a/modules/sdk-api/src/v1/wallet.ts b/modules/sdk-api/src/v1/wallet.ts index c25dab1e8d..ca53eae85e 100644 --- a/modules/sdk-api/src/v1/wallet.ts +++ b/modules/sdk-api/src/v1/wallet.ts @@ -1751,7 +1751,7 @@ Wallet.prototype.createAndSignTransaction = function (params, callback) { const safeUserKey = _.get(this.wallet, 'private.userPrivKey'); if (_.isString(safeUserKey) && _.isString(params.walletPassphrase)) { // @ts-expect-error - no implicit this - transaction.signingKey = await this.bitgo.decryptAsync({ + transaction.signingKey = await this.bitgo.decrypt({ password: params.walletPassphrase, input: safeUserKey, }); @@ -1815,7 +1815,7 @@ Wallet.prototype.getAndPrepareSigningKeychain = function (params, callback) { return self.getEncryptedUserKeychain().then(async function (keychain) { // Decrypt the user key with a passphrase try { - keychain.xprv = await self.bitgo.decryptAsync({ + keychain.xprv = await self.bitgo.decrypt({ password: params.walletPassphrase, input: keychain.encryptedXprv, }); @@ -2308,7 +2308,7 @@ Wallet.prototype.shareWallet = function (params, callback) { throw new Error('Missing walletPassphrase argument'); } try { - keychain.xprv = await self.bitgo.decryptAsync({ + keychain.xprv = await self.bitgo.decrypt({ password: params.walletPassphrase, input: keychain.encryptedXprv, }); @@ -2318,7 +2318,7 @@ Wallet.prototype.shareWallet = function (params, callback) { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(sharing.pubkey, 'hex')).toString('hex'); - const newEncryptedXprv = await self.bitgo.encryptAsync({ + const newEncryptedXprv = await self.bitgo.encrypt({ password: secret, input: keychain.xprv, encryptionVersion: params.encryptionVersion, @@ -2615,10 +2615,10 @@ Wallet.prototype.recover = async function (params) { assert(parsedUnsignedTx.outs.length === 1); assert(_.sumBy(params.unspents, 'value') - _.sumBy(parsedUnsignedTx.outs, 'value') === Number(approximateTxFee)); - const plainUserKey = await this.bitgo.decryptAsync({ password: params.walletPassphrase, input: params.userKey }); + const plainUserKey = await this.bitgo.decrypt({ password: params.walletPassphrase, input: params.userKey }); const halfSignedTx = await this.signTransaction({ ...unsignedTx, signingKey: plainUserKey }); - const plainBackupKey = await this.bitgo.decryptAsync({ password: params.walletPassphrase, input: params.backupKey }); + const plainBackupKey = await this.bitgo.decrypt({ password: params.walletPassphrase, input: params.backupKey }); const fullSignedTx = await this.signTransaction({ ...unsignedTx, transactionHex: halfSignedTx.tx, @@ -2675,7 +2675,7 @@ Wallet.prototype.sweep = async function (params) { assert(parsedUnsignedTx.outs.length === 1); assert(_.sumBy(params.unspents, 'value') - _.sumBy(parsedUnsignedTx.outs, 'value') === Number(approximateTxFee)); - const plainUserKey = await this.bitgo.decryptAsync({ password: params.walletPassphrase, input: params.userKey }); + const plainUserKey = await this.bitgo.decrypt({ password: params.walletPassphrase, input: params.userKey }); const halfSignedTx = await this.signTransaction({ ...unsignedTx, signingKey: plainUserKey }); return await this.sendTransaction({ diff --git a/modules/sdk-api/src/v1/wallets.ts b/modules/sdk-api/src/v1/wallets.ts index ffeeff737e..5f5ea86bb8 100644 --- a/modules/sdk-api/src/v1/wallets.ts +++ b/modules/sdk-api/src/v1/wallets.ts @@ -253,7 +253,7 @@ Wallets.prototype.acceptShare = function (params, callback) { } // Now we have the sharing keychain, we can work out the secret used for sharing the wallet with us - sharingKeychain.xprv = await self.bitgo.decryptAsync({ + sharingKeychain.xprv = await self.bitgo.decrypt({ password: params.userPassword, input: sharingKeychain.encryptedXprv, }); @@ -265,14 +265,14 @@ Wallets.prototype.acceptShare = function (params, callback) { ).toString('hex'); // Yes! We got the secret successfully here, now decrypt the shared wallet xprv - const decryptedSharedWalletXprv = await self.bitgo.decryptAsync({ + const decryptedSharedWalletXprv = await self.bitgo.decrypt({ password: secret, input: walletShare.keychain.encryptedXprv, }); // We will now re-encrypt the wallet with our own password const newWalletPassphrase = params.newWalletPassphrase || params.userPassword; - encryptedXprv = await self.bitgo.encryptAsync({ + encryptedXprv = await self.bitgo.encrypt({ password: newWalletPassphrase, input: decryptedSharedWalletXprv, encryptionVersion: params.encryptionVersion, @@ -370,7 +370,7 @@ Wallets.prototype.createWalletWithKeychains = function (params, callback) { return Promise.resolve() .then(() => - self.bitgo.encryptAsync({ + self.bitgo.encrypt({ password: params.passphrase, input: userKeychain.xprv, encryptionVersion: params.encryptionVersion, diff --git a/modules/sdk-api/test/unit/bitgoAPI.ts b/modules/sdk-api/test/unit/bitgoAPI.ts index 59a934ad6d..f77700864e 100644 --- a/modules/sdk-api/test/unit/bitgoAPI.ts +++ b/modules/sdk-api/test/unit/bitgoAPI.ts @@ -242,122 +242,82 @@ describe('Constructor', function () { sinon.restore(); }); - it('should throw if no params are provided', function () { + it('should throw if no params are provided', async function () { // @ts-expect-error - intentionally calling with no params for test - (() => bitgo.decryptKeys()).should.throw('Missing parameter: walletIdEncryptedKeyPairs'); + await bitgo.decryptKeys().should.be.rejectedWith('Missing parameter: walletIdEncryptedKeyPairs'); }); - it('should throw if walletIdEncryptedKeyPairs is missing', function () { - // @ts-expect-error - intentionally missing required param - (() => bitgo.decryptKeys({ password: 'password123' })).should.throw( - 'Missing parameter: walletIdEncryptedKeyPairs' - ); + it('should throw if walletIdEncryptedKeyPairs is missing', async function () { + await bitgo + // @ts-expect-error - intentionally missing required param + .decryptKeys({ password: 'password123' }) + .should.be.rejectedWith('Missing parameter: walletIdEncryptedKeyPairs'); }); - it('should throw if password is missing', function () { + it('should throw if password is missing', async function () { // @ts-expect-error - intentionally missing required param - (() => bitgo.decryptKeys({ walletIdEncryptedKeyPairs: [] })).should.throw('Missing parameter: password'); + await bitgo.decryptKeys({ walletIdEncryptedKeyPairs: [] }).should.be.rejectedWith('Missing parameter: password'); }); - it('should throw if walletIdEncryptedKeyPairs is not an array', function () { - // @ts-expect-error - intentionally providing wrong type - (() => bitgo.decryptKeys({ walletIdEncryptedKeyPairs: 'not an array', password: 'password123' })).should.throw( - 'walletIdEncryptedKeyPairs must be an array' - ); + it('should throw if walletIdEncryptedKeyPairs is not an array', async function () { + await bitgo + // @ts-expect-error - intentionally providing wrong type + .decryptKeys({ walletIdEncryptedKeyPairs: 'not an array', password: 'password123' }) + .should.be.rejectedWith('walletIdEncryptedKeyPairs must be an array'); }); - it('should return empty array for empty walletIdEncryptedKeyPairs', function () { - const result = bitgo.decryptKeys({ walletIdEncryptedKeyPairs: [], password: 'password123' }); + it('should return empty array for empty walletIdEncryptedKeyPairs', async function () { + const result = await bitgo.decryptKeys({ walletIdEncryptedKeyPairs: [], password: 'password123' }); result.should.be.an.Array(); result.should.be.empty(); }); - it('should throw if any walletId is missing or not a string', function () { - (() => - bitgo.decryptKeys({ + it('should throw if any walletId is missing or not a string', async function () { + await bitgo + .decryptKeys({ walletIdEncryptedKeyPairs: [ // @ts-expect-error - intentionally missing walletId { encryptedPrv: 'encrypted-data' }, ], password: 'password123', - })).should.throw('each key pair must have a string walletId'); + }) + .should.be.rejectedWith('each key pair must have a string walletId'); - (() => - bitgo.decryptKeys({ + await bitgo + .decryptKeys({ walletIdEncryptedKeyPairs: [ // @ts-expect-error - intentionally providing wrong type { walletId: 123, encryptedPrv: 'encrypted-data' }, ], password: 'password123', - })).should.throw('each key pair must have a string walletId'); + }) + .should.be.rejectedWith('each key pair must have a string walletId'); }); - it('should throw if any encryptedPrv is missing or not a string', function () { - (() => - bitgo.decryptKeys({ + it('should throw if any encryptedPrv is missing or not a string', async function () { + await bitgo + .decryptKeys({ walletIdEncryptedKeyPairs: [ // @ts-expect-error - intentionally missing encryptedPrv { walletId: 'wallet-id-1' }, ], password: 'password123', - })).should.throw('each key pair must have a string encryptedPrv'); + }) + .should.be.rejectedWith('each key pair must have a string encryptedPrv'); - (() => - bitgo.decryptKeys({ + await bitgo + .decryptKeys({ walletIdEncryptedKeyPairs: [ // @ts-expect-error - intentionally providing wrong type { walletId: 'wallet-id-1', encryptedPrv: 123 }, ], password: 'password123', - })).should.throw('each key pair must have a string encryptedPrv'); - }); - - it('should return walletIds of keys that failed to decrypt', function () { - const decryptStub = sinon.stub(bitgo, 'decrypt'); - decryptStub.onFirstCall().returns('decrypted-key-1'); - decryptStub.onSecondCall().throws(new Error('decryption failed')); - - const result = bitgo.decryptKeys({ - walletIdEncryptedKeyPairs: [ - { walletId: 'wallet-id-1', encryptedPrv: 'encrypted-data-1' }, - { walletId: 'wallet-id-2', encryptedPrv: 'encrypted-data-2' }, - ], - password: 'password123', - }); - - result.should.be.an.Array(); - result.should.have.length(1); - result[0].should.equal('wallet-id-2'); - }); - - it('should correctly process multiple wallet keys', function () { - const decryptStub = sinon.stub(bitgo, 'decrypt'); - decryptStub - .withArgs({ input: 'encrypted-data-2', password: 'password123' }) - .throws(new Error('decryption failed')); - decryptStub - .withArgs({ input: 'encrypted-data-4', password: 'password123' }) - .throws(new Error('decryption failed')); - decryptStub.returns('success'); - - const result = bitgo.decryptKeys({ - walletIdEncryptedKeyPairs: [ - { walletId: 'wallet-id-1', encryptedPrv: 'encrypted-data-1' }, - { walletId: 'wallet-id-2', encryptedPrv: 'encrypted-data-2' }, - { walletId: 'wallet-id-3', encryptedPrv: 'encrypted-data-3' }, - { walletId: 'wallet-id-4', encryptedPrv: 'encrypted-data-4' }, - ], - password: 'password123', - }); - - decryptStub.callCount.should.equal(4); - result.should.be.an.Array(); - result.should.have.length(2); - result.should.containDeep(['wallet-id-2', 'wallet-id-4']); + }) + .should.be.rejectedWith('each key pair must have a string encryptedPrv'); }); }); - describe('decryptKeysAsync', function () { + describe('decryptKeys', function () { let bitgo: BitGoAPI; beforeEach(function () { @@ -369,11 +329,11 @@ describe('Constructor', function () { }); it('should return walletIds of keys that failed to decrypt', async function () { - const decryptAsyncStub = sinon.stub(bitgo, 'decryptAsync'); + const decryptAsyncStub = sinon.stub(bitgo, 'decrypt'); decryptAsyncStub.onFirstCall().resolves('decrypted-key-1'); decryptAsyncStub.onSecondCall().rejects(new Error('decryption failed')); - const result = await bitgo.decryptKeysAsync({ + const result = await bitgo.decryptKeys({ walletIdEncryptedKeyPairs: [ { walletId: 'wallet-id-1', encryptedPrv: 'encrypted-data-1' }, { walletId: 'wallet-id-2', encryptedPrv: 'encrypted-data-2' }, @@ -387,7 +347,7 @@ describe('Constructor', function () { }); it('should correctly process multiple wallet keys', async function () { - const decryptAsyncStub = sinon.stub(bitgo, 'decryptAsync'); + const decryptAsyncStub = sinon.stub(bitgo, 'decrypt'); decryptAsyncStub .withArgs({ input: 'encrypted-data-2', password: 'password123' }) .rejects(new Error('decryption failed')); @@ -396,7 +356,7 @@ describe('Constructor', function () { .rejects(new Error('decryption failed')); decryptAsyncStub.resolves('success'); - const result = await bitgo.decryptKeysAsync({ + const result = await bitgo.decryptKeys({ walletIdEncryptedKeyPairs: [ { walletId: 'wallet-id-1', encryptedPrv: 'encrypted-data-1' }, { walletId: 'wallet-id-2', encryptedPrv: 'encrypted-data-2' }, @@ -731,7 +691,7 @@ describe('Constructor', function () { }); it('should return plain token from response body when ecdhXprv is absent but strategyAuthenticated', async function () { - const handleTokenSpy = sinon.spy(BitGoAPI.prototype, 'handleTokenIssuanceAsync'); + const handleTokenSpy = sinon.spy(BitGoAPI.prototype, 'handleTokenIssuance'); const { strategy } = makeStrategy({ isAuthenticated: sinon.stub().returns(true), }); @@ -745,7 +705,7 @@ describe('Constructor', function () { const result = await bitgo.addAccessToken(validParams); - // handleTokenIssuanceAsync should NOT be called — no ECDH decryption needed + // handleTokenIssuance should NOT be called — no ECDH decryption needed handleTokenSpy.called.should.be.false(); result.token.should.equal('v2xplaintoken'); @@ -1143,8 +1103,8 @@ describe('Constructor', function () { sinon.restore(); }); - it('passes encryptionVersion: 2 to encryptAsync', async function () { - const encryptAsyncSpy = sinon.spy(bitgo, 'encryptAsync'); + it('passes encryptionVersion: 2 to encrypt', async function () { + const encryptAsyncSpy = sinon.spy(bitgo, 'encrypt'); nock(ROOT).post('/api/v1/keychain').reply(200, { xpub: 'xpub123', id: 'key-id' }); await bitgo.createUserEcdhKeychain('loginPassword', 2); @@ -1154,7 +1114,7 @@ describe('Constructor', function () { }); it('passes encryptionVersion: undefined when not set (defaults to v1)', async function () { - const encryptAsyncSpy = sinon.spy(bitgo, 'encryptAsync'); + const encryptAsyncSpy = sinon.spy(bitgo, 'encrypt'); nock(ROOT).post('/api/v1/keychain').reply(200, { xpub: 'xpub123', id: 'key-id' }); await bitgo.createUserEcdhKeychain('loginPassword'); @@ -1164,7 +1124,7 @@ describe('Constructor', function () { }); }); - describe('splitSecretAsync - encryptionVersion threading', function () { + describe('splitSecret - encryptionVersion threading', function () { let bitgo: BitGoAPI; beforeEach(function () { @@ -1175,11 +1135,11 @@ describe('Constructor', function () { sinon.restore(); }); - it('passes encryptionVersion: 2 to every encryptAsync shard call', async function () { - const encryptAsyncSpy = sinon.spy(bitgo, 'encryptAsync'); + it('passes encryptionVersion: 2 to every encrypt shard call', async function () { + const encryptAsyncSpy = sinon.spy(bitgo, 'encrypt'); const passwords = ['pw1', 'pw2', 'pw3']; - await bitgo.splitSecretAsync({ + await bitgo.splitSecret({ seed: 'a'.repeat(64), passwords, m: 2, @@ -1193,9 +1153,9 @@ describe('Constructor', function () { }); it('passes encryptionVersion: undefined when not set', async function () { - const encryptAsyncSpy = sinon.spy(bitgo, 'encryptAsync'); + const encryptAsyncSpy = sinon.spy(bitgo, 'encrypt'); - await bitgo.splitSecretAsync({ + await bitgo.splitSecret({ seed: 'b'.repeat(64), passwords: ['pw1', 'pw2'], m: 2, diff --git a/modules/sdk-api/test/unit/encrypt.ts b/modules/sdk-api/test/unit/encrypt.ts index 3a8be1cd3a..53d7dffb71 100644 --- a/modules/sdk-api/test/unit/encrypt.ts +++ b/modules/sdk-api/test/unit/encrypt.ts @@ -1,81 +1,113 @@ import assert from 'assert'; import { randomBytes } from 'crypto'; -import { - decrypt, - decryptAsync, - decryptV2, - encrypt, - encryptAsync, - encryptV2, - V2Envelope, - createEncryptionSession, -} from '../../src'; +import { decrypt, decryptV2, encrypt, encryptV2, V2Envelope, createEncryptionSession } from '../../src'; import { BitGoAPI } from '../../src/bitgoAPI'; describe('encryption methods tests', () => { - describe('encrypt', () => { - it('encrypts the plaintext with the given password', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const ciphertext = encrypt(password, plaintext); + describe('encrypt (async, default v2)', () => { + const password = 'myPassword'; + const plaintext = 'Hello, World!'; + it('encrypts the plaintext with the given password', async () => { + const ciphertext = await encrypt(password, plaintext); assert(ciphertext !== plaintext, 'ciphertext should not be equal to plaintext'); }); - it('returns a different ciphertext for the same plaintext and password', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const ciphertext1 = encrypt(password, plaintext); - const ciphertext2 = encrypt(password, plaintext); + it('produces a v2 envelope by default', async () => { + const ciphertext = await encrypt(password, plaintext); + const envelope: V2Envelope = JSON.parse(ciphertext); + assert.strictEqual(envelope.v, 2); + }); + it('returns a different ciphertext for the same plaintext and password', async () => { + const ciphertext1 = await encrypt(password, plaintext); + const ciphertext2 = await encrypt(password, plaintext); assert(ciphertext1 !== ciphertext2, 'ciphertexts should not be equal'); }); - it('throws an error if the salt length is not 8 bytes', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const options = { salt: randomBytes(4) }; + it('forwards adata to the v2 envelope', async () => { + const adata = 'txhash:m/0/1'; + const ciphertext = await encrypt(password, plaintext, { adata }); + const envelope: V2Envelope = JSON.parse(ciphertext); + assert.strictEqual(envelope.adata, adata); + assert.strictEqual(await decrypt(password, ciphertext), plaintext); + }); + }); + + describe('encrypt with encryptionVersion: 1 (legacy v1)', () => { + const password = 'myPassword'; + const plaintext = 'Hello, World!'; + + it('produces a v1 envelope that decrypts back via decrypt', async () => { + const ciphertext = await encrypt(password, plaintext, { encryptionVersion: 1 }); + const envelope = JSON.parse(ciphertext); + assert.notStrictEqual(envelope.v, 2, 'encryptionVersion 1 should not produce a v2 envelope'); + assert.strictEqual(await decrypt(password, ciphertext), plaintext); + }); - assert.throws(() => encrypt(password, plaintext, options), new Error(`salt must be 8 bytes`)); + it('throws an error if the salt length is not 8 bytes', async () => { + await assert.rejects( + () => encrypt(password, plaintext, { encryptionVersion: 1, salt: randomBytes(4) }), + /salt must be 8 bytes/ + ); }); - it('throws an error if the iv length is not 16 bytes', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const options = { iv: randomBytes(4) }; + it('throws an error if the iv length is not 16 bytes', async () => { + await assert.rejects( + () => encrypt(password, plaintext, { encryptionVersion: 1, iv: randomBytes(4) }), + /iv must be 16 bytes/ + ); + }); - assert.throws(() => encrypt(password, plaintext, options), new Error(`iv must be 16 bytes`)); + it('forwards salt and iv options for deterministic v1 output', async () => { + const salt = randomBytes(8); + const iv = randomBytes(16); + const ct1 = await encrypt(password, plaintext, { encryptionVersion: 1, salt, iv }); + const ct2 = await encrypt(password, plaintext, { encryptionVersion: 1, salt, iv }); + assert.strictEqual(ct1, ct2); + assert.strictEqual(await decrypt(password, ct1), plaintext); }); }); - describe('decrypt', () => { - it('decrypts the ciphertext with the given password', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const ciphertext = encrypt(password, plaintext); - const decrypted = decrypt(password, ciphertext); + describe('decrypt (auto-detect v1/v2)', () => { + const password = 'myPassword'; + const plaintext = 'Hello, World!'; - assert(decrypted === plaintext, 'decrypted should be equal to plaintext'); + it('decrypts default v2 data', async () => { + const ciphertext = await encrypt(password, plaintext); + assert.strictEqual(await decrypt(password, ciphertext), plaintext); }); - it('throws an error if the password is wrong', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const ciphertext = encrypt(password, plaintext); - const wrongPassword = 'wrongPassword'; + it('decrypts legacy v1 data', async () => { + const ciphertext = await encrypt(password, plaintext, { encryptionVersion: 1 }); + assert.strictEqual(await decrypt(password, ciphertext), plaintext); + }); - assert.throws(() => decrypt(wrongPassword, ciphertext), 'sjcl exception: cc: invalid aes key'); + it('throws on wrong password for v1', async () => { + const ciphertext = await encrypt(password, plaintext, { encryptionVersion: 1 }); + await assert.rejects(() => decrypt('wrongPassword', ciphertext)); }); - it('decrypts the ciphertext with the given password and adata', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - const adata = 'additional data'; - const ciphertext = encrypt(password, plaintext, { adata }); - const decrypted = decrypt(password, ciphertext); + it('throws on wrong password for v2', async () => { + const ciphertext = await encrypt(password, plaintext); + await assert.rejects(() => decrypt('wrongPassword', ciphertext)); + }); - assert(decrypted === plaintext, 'decrypted should be equal to plaintext'); + it('throws on invalid JSON input', async () => { + await assert.rejects(() => decrypt(password, 'not-json'), /ciphertext is not valid JSON/); + }); + + it('wrong password on v2 data does not fall through to v1 decrypt', async () => { + const v2ct = await encryptV2(password, plaintext, { memorySize: 1024, iterations: 1, parallelism: 1 }); + let caughtError: Error | undefined; + try { + await decrypt('wrong', v2ct); + } catch (e) { + caughtError = e as Error; + } + assert.ok(caughtError, 'should have thrown'); + assert.ok(!caughtError.message?.includes('sjcl'), 'error must not be from SJCL'); }); }); @@ -160,7 +192,7 @@ describe('encryption methods tests', () => { }); it('v1 and v2 are independent (v1 data does not decrypt with v2)', async () => { - const v1ct = encrypt(password, plaintext); + const v1ct = await encrypt(password, plaintext, { encryptionVersion: 1 }); await assert.rejects(() => decryptV2(password, v1ct), /invalid envelope/); }); @@ -195,113 +227,6 @@ describe('encryption methods tests', () => { }); }); - describe('encryptAsync (v1/v2 dispatch)', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - - it('dispatches to v1 by default and output is decryptable via decrypt', async () => { - const ct = await encryptAsync(password, plaintext); - const envelope = JSON.parse(ct); - assert.notStrictEqual(envelope.v, 2, 'default should not produce v2 envelope'); - assert.strictEqual(decrypt(password, ct), plaintext); - }); - - it('dispatches to v2 when encryptionVersion: 2', async () => { - const ct = await encryptAsync(password, plaintext, { encryptionVersion: 2 }); - const envelope: V2Envelope = JSON.parse(ct); - assert.strictEqual(envelope.v, 2); - const result = await decryptAsync(password, ct); - assert.strictEqual(result, plaintext); - }); - - it('dispatches to v1 when encryptionVersion: 1', async () => { - const ct = await encryptAsync(password, plaintext, { encryptionVersion: 1 }); - const envelope = JSON.parse(ct); - assert.notStrictEqual(envelope.v, 2); - assert.strictEqual(decrypt(password, ct), plaintext); - }); - - it('forwards adata to v2 envelope', async () => { - const adata = 'txhash:m/0/1'; - const ct = await encryptAsync(password, plaintext, { encryptionVersion: 2, adata }); - const envelope: V2Envelope = JSON.parse(ct); - assert.strictEqual(envelope.adata, adata); - assert.strictEqual(await decryptAsync(password, ct), plaintext); - }); - - it('encrypts v1 with adata', async () => { - const adata = 'additional data'; - const ct = await encryptAsync(password, plaintext, { adata }); - assert.strictEqual(decrypt(password, ct), plaintext); - }); - - it('returns different ciphertext for the same plaintext and password', async () => { - const ct1 = await encryptAsync(password, plaintext); - const ct2 = await encryptAsync(password, plaintext); - assert.notStrictEqual(ct1, ct2); - }); - - it('forwards salt and iv options to v1 encrypt for deterministic output', async () => { - const salt = randomBytes(8); - const iv = randomBytes(16); - const ct1 = await encryptAsync(password, plaintext, { salt, iv }); - const ct2 = await encryptAsync(password, plaintext, { salt, iv }); - assert.strictEqual(ct1, ct2); - assert.strictEqual(decrypt(password, ct1), plaintext); - }); - - it('throws an error if the salt length is not 8 bytes', async () => { - await assert.rejects(() => encryptAsync(password, plaintext, { salt: randomBytes(4) }), /salt must be 8 bytes/); - }); - - it('throws an error if the iv length is not 16 bytes', async () => { - await assert.rejects(() => encryptAsync(password, plaintext, { iv: randomBytes(4) }), /iv must be 16 bytes/); - }); - }); - - describe('decryptAsync (auto-detect v1/v2)', () => { - const password = 'myPassword'; - const plaintext = 'Hello, World!'; - - it('decrypts v1 data', async () => { - const v1ct = encrypt(password, plaintext); - const result = await decryptAsync(password, v1ct); - assert.strictEqual(result, plaintext); - }); - - it('decrypts v2 data', async () => { - const v2ct = await encryptV2(password, plaintext); - const result = await decryptAsync(password, v2ct); - assert.strictEqual(result, plaintext); - }); - - it('throws on wrong password for v1', async () => { - const v1ct = encrypt(password, plaintext); - await assert.rejects(() => decryptAsync('wrong', v1ct)); - }); - - it('throws on wrong password for v2', async () => { - const v2ct = await encryptV2(password, plaintext); - await assert.rejects(() => decryptAsync('wrong', v2ct)); - }); - - it('throws on invalid JSON input', async () => { - await assert.rejects(() => decryptAsync(password, 'not-json'), /ciphertext is not valid JSON/); - }); - - it('wrong password on v2 data does not fall through to v1 decrypt', async () => { - const v2ct = await encryptV2(password, plaintext, { memorySize: 1024, iterations: 1, parallelism: 1 }); - let caughtError: Error | undefined; - try { - await decryptAsync('wrong', v2ct); - } catch (e) { - caughtError = e as Error; - } - assert.ok(caughtError, 'should have thrown'); - assert.ok(!caughtError.message?.includes('sjcl'), 'error must not be from SJCL'); - }); - }); - describe('EncryptionSession (HKDF caching)', () => { const opts = { memorySize: 1024, iterations: 1, parallelism: 1 }; const password = 'test-password'; @@ -336,11 +261,11 @@ describe('encryption methods tests', () => { assert.strictEqual(result, plaintext); }); - it('session envelope can be decrypted via decryptAsync', async () => { + it('session envelope can be decrypted via decrypt', async () => { const session = await createEncryptionSession(password, opts); const ct = await session.encrypt(plaintext); session.destroy(); - const result = await decryptAsync(password, ct); + const result = await decrypt(password, ct); assert.strictEqual(result, plaintext); }); @@ -439,37 +364,35 @@ describe('encryption methods tests', () => { }); }); - describe('BitGoAPI.encryptAsync', () => { + describe('BitGoAPI.encrypt', () => { let bitgo: BitGoAPI; const password = 'test-password'; - const plaintext = 'hello encryptAsync'; + const plaintext = 'hello encrypt'; before(() => { bitgo = new BitGoAPI({ env: 'test' }); }); - it('dispatches to v1 by default and output is decryptable via decrypt', async () => { - const ct = await bitgo.encryptAsync({ input: plaintext, password }); - const envelope = JSON.parse(ct); - assert.notStrictEqual(envelope.v, 2, 'default should not produce v2 envelope'); - assert.strictEqual(decrypt(password, ct), plaintext); - }); - - it('dispatches to v2 when encryptionVersion: 2 and output is decryptable via decryptAsync', async () => { - const ct = await bitgo.encryptAsync({ input: plaintext, password, encryptionVersion: 2 }); + it('produces a v2 envelope by default and output is decryptable via decrypt', async () => { + const ct = await bitgo.encrypt({ input: plaintext, password }); const envelope: V2Envelope = JSON.parse(ct); assert.strictEqual(envelope.v, 2); - const result = await decryptAsync(password, ct); - assert.strictEqual(result, plaintext); + assert.strictEqual(await decrypt(password, ct), plaintext); + }); + + it('produces a v1 envelope when encryptionVersion: 1', async () => { + const ct = await bitgo.encrypt({ input: plaintext, password, encryptionVersion: 1 }); + const envelope = JSON.parse(ct); + assert.notStrictEqual(envelope.v, 2); + assert.strictEqual(await decrypt(password, ct), plaintext); }); it('forwards adata to v2 envelope', async () => { const adata = 'txhash:m/0/1'; - const ct = await bitgo.encryptAsync({ input: plaintext, password, encryptionVersion: 2, adata }); + const ct = await bitgo.encrypt({ input: plaintext, password, adata }); const envelope: V2Envelope = JSON.parse(ct); assert.strictEqual(envelope.adata, adata); - const result = await decryptAsync(password, ct); - assert.strictEqual(result, plaintext); + assert.strictEqual(await decrypt(password, ct), plaintext); }); }); diff --git a/modules/sdk-api/test/unit/v1/travelRule.ts b/modules/sdk-api/test/unit/v1/travelRule.ts index 4a307eeeae..b10305ca22 100644 --- a/modules/sdk-api/test/unit/v1/travelRule.ts +++ b/modules/sdk-api/test/unit/v1/travelRule.ts @@ -1,5 +1,5 @@ import * as sinon from 'sinon'; -import * as should from 'should'; +import 'should'; import { BitGoAPI } from '../../../src/bitgoAPI'; const TravelRule = require('../../../src/v1/travelRule'); @@ -21,48 +21,27 @@ describe('TravelRule unit tests', () => { }); // --------------------------------------------------------------------------- - // decryptReceivedTravelInfo (sync) + // decryptReceivedTravelInfo // --------------------------------------------------------------------------- describe('decryptReceivedTravelInfo', () => { - it('throws when tx param is missing', () => { - should.throws(() => travel.decryptReceivedTravelInfo({}), /expecting tx param to be object/); - }); - - it('returns tx unchanged when receivedTravelInfo is empty', () => { - const tx = { receivedTravelInfo: [] }; - const result = travel.decryptReceivedTravelInfo({ tx }); - result.should.equal(tx); - }); - - it('returns tx unchanged when receivedTravelInfo is not present', () => { - const tx = { id: 'txid123' }; - const result = travel.decryptReceivedTravelInfo({ tx }); - result.should.equal(tx); - }); - }); - - // --------------------------------------------------------------------------- - // decryptReceivedTravelInfoAsync - // --------------------------------------------------------------------------- - describe('decryptReceivedTravelInfoAsync', () => { it('throws when tx param is missing', async () => { - await travel.decryptReceivedTravelInfoAsync({}).should.be.rejectedWith(/expecting tx param to be object/); + await travel.decryptReceivedTravelInfo({}).should.be.rejectedWith(/expecting tx param to be object/); }); it('returns tx unchanged when receivedTravelInfo is empty', async () => { const tx = { receivedTravelInfo: [] }; - const result = await travel.decryptReceivedTravelInfoAsync({ tx }); + const result = await travel.decryptReceivedTravelInfo({ tx }); result.should.equal(tx); }); it('returns tx unchanged when receivedTravelInfo is not present', async () => { const tx = { id: 'txid456' }; - const result = await travel.decryptReceivedTravelInfoAsync({ tx }); + const result = await travel.decryptReceivedTravelInfo({ tx }); result.should.equal(tx); }); - it('calls decryptAsync for each travel info entry', async () => { - const decryptStub = sinon.stub(bitgo, 'decryptAsync').resolves(JSON.stringify({ fromUserName: 'Alice' })); + it('calls decrypt for each travel info entry', async () => { + const decryptStub = sinon.stub(bitgo, 'decrypt').resolves(JSON.stringify({ fromUserName: 'Alice' })); const xprv = 'xprv9s21ZrQH143K2fJ91S4BRsupcYrE6mmY96fcX5HkhoTrrwmwjd16Cn87cWinJjByrfpojjx7ezsJLx7TAKLT8m8hM5Kax9YcoxnBeJZ3t2k'; @@ -80,62 +59,19 @@ describe('TravelRule unit tests', () => { ], }; - const result = await travel.decryptReceivedTravelInfoAsync({ tx, keychain }); + const result = await travel.decryptReceivedTravelInfo({ tx, keychain }); decryptStub.callCount.should.equal(1); result.should.equal(tx); }); }); // --------------------------------------------------------------------------- - // prepareParams (sync) + // prepareParams // --------------------------------------------------------------------------- describe('prepareParams', () => { - it('throws when recipient is missing', () => { - should.throws( - () => - travel.prepareParams({ - txid: 'abc123', - travelInfo: { fromUserName: 'Alice' }, - }), - /invalid or missing recipient/ - ); - }); - - it('throws when travelInfo is missing', () => { - should.throws( - () => - travel.prepareParams({ - txid: 'abc123', - recipient: { enterprise: 'SDKTest', pubKey: KNOWN_RECIPIENT_PUB, outputIndex: '0' }, - }), - /invalid or missing travelInfo/ - ); - }); - - it('calls bitgo.encrypt and returns expected shape', () => { - const encryptStub = sinon.stub(bitgo, 'encrypt').returns('encryptedBlob'); - - const result = travel.prepareParams({ - txid: 'abc123', - recipient: { enterprise: 'SDKTest', pubKey: KNOWN_RECIPIENT_PUB, outputIndex: '0' }, - travelInfo: { fromUserName: 'Alice', toAddress: '1BitGo' }, - }); - - encryptStub.callCount.should.equal(1); - result.should.have.property('txid', 'abc123'); - result.should.have.property('toPubKey', KNOWN_RECIPIENT_PUB); - result.should.have.property('fromPubKey').which.is.a.String(); - result.should.have.property('encryptedTravelInfo', 'encryptedBlob'); - }); - }); - - // --------------------------------------------------------------------------- - // prepareParamsAsync - // --------------------------------------------------------------------------- - describe('prepareParamsAsync', () => { it('throws when recipient is missing', async () => { await travel - .prepareParamsAsync({ + .prepareParams({ txid: 'abc123', travelInfo: { fromUserName: 'Alice' }, }) @@ -144,17 +80,17 @@ describe('TravelRule unit tests', () => { it('throws when travelInfo is missing', async () => { await travel - .prepareParamsAsync({ + .prepareParams({ txid: 'abc123', recipient: { enterprise: 'SDKTest', pubKey: KNOWN_RECIPIENT_PUB, outputIndex: '0' }, }) .should.be.rejectedWith(/invalid or missing travelInfo/); }); - it('calls encryptAsync and returns expected output shape', async () => { - const encryptStub = sinon.stub(bitgo, 'encryptAsync').resolves('asyncEncryptedBlob'); + it('calls encrypt and returns expected output shape', async () => { + const encryptStub = sinon.stub(bitgo, 'encrypt').resolves('asyncEncryptedBlob'); - const result = await travel.prepareParamsAsync({ + const result = await travel.prepareParams({ txid: 'txid789', recipient: { enterprise: 'SDKTest', pubKey: KNOWN_RECIPIENT_PUB, outputIndex: '1' }, travelInfo: { fromUserName: 'Bob', toAddress: '1BitGo' }, diff --git a/modules/sdk-api/test/unit/v1/wallet.ts b/modules/sdk-api/test/unit/v1/wallet.ts index 5836e39080..7f6fde6e41 100644 --- a/modules/sdk-api/test/unit/v1/wallet.ts +++ b/modules/sdk-api/test/unit/v1/wallet.ts @@ -1290,7 +1290,7 @@ describe('Wallet Prototype Methods', function () { describe('successful tx acceleration', function successfulTxDescribe() { const feeRate = 20000; - beforeEach(() => { + beforeEach(async () => { nock(bgUrl).post(`/api/v1/wallet/${wallet.id()}/address/1`).reply(200, { address: '2NCYjG8Q56yr8tx9jazNoYnGKxjgB2MQSfY', }); @@ -1306,7 +1306,7 @@ describe('Wallet Prototype Methods', function () { nock(bgUrl) .post(`/api/v1/keychain/${userKeypair.xpub}`, {}) .reply(200, { - encryptedXprv: bitgo.encrypt({ input: userKeypair.xprv, password: TestBitGo.TEST_WALLET1_PASSCODE }), + encryptedXprv: await bitgo.encrypt({ input: userKeypair.xprv, password: TestBitGo.TEST_WALLET1_PASSCODE }), path: userKeypair.path + userKeypair.walletSubPath, }); }); diff --git a/modules/sdk-coin-ada/src/ada.ts b/modules/sdk-coin-ada/src/ada.ts index 265dcb3e1b..40f324e713 100644 --- a/modules/sdk-coin-ada/src/ada.ts +++ b/modules/sdk-coin-ada/src/ada.ts @@ -464,7 +464,7 @@ export class Ada extends BaseCoin { // Decrypt private keys from KeyCard values let userPrv; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -476,7 +476,7 @@ export class Ada extends BaseCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-ada/test/unit/ada.ts b/modules/sdk-coin-ada/test/unit/ada.ts index 8061937fa1..a63b7cd791 100644 --- a/modules/sdk-coin-ada/test/unit/ada.ts +++ b/modules/sdk-coin-ada/test/unit/ada.ts @@ -413,7 +413,7 @@ describe('ADA', function () { }, multisigType: 'tss', }; - const fakePrv = encrypt('password', 'prv'); + const fakePrv = await encrypt('password', 'prv'); const walletObj = new Wallet(bitgo, basecoin, walletData); const bgUrl = common.Environments['mock'].uri; diff --git a/modules/sdk-coin-algo/src/algo.ts b/modules/sdk-coin-algo/src/algo.ts index 50de701bd8..f17cf0ec5e 100644 --- a/modules/sdk-coin-algo/src/algo.ts +++ b/modules/sdk-coin-algo/src/algo.ts @@ -883,8 +883,8 @@ export class Algo extends BaseCoin { throw new Error('bitgo public key from the keyCard is required for non-bitgo recovery'); } try { - userPrv = await this.bitgo.decryptAsync({ input: params.userKey, password: params.walletPassphrase }); - backupPrv = await this.bitgo.decryptAsync({ input: params.backupKey, password: params.walletPassphrase }); + userPrv = await this.bitgo.decrypt({ input: params.userKey, password: params.walletPassphrase }); + backupPrv = await this.bitgo.decrypt({ input: params.backupKey, password: params.walletPassphrase }); const userKeyAddress = Utils.privateKeyToAlgoAddress(userPrv); const backupKeyAddress = Utils.privateKeyToAlgoAddress(backupPrv); txBuilder.numberOfRequiredSigners(2).setSigners([userKeyAddress, backupKeyAddress, params.bitgoKey]); diff --git a/modules/sdk-coin-algo/test/unit/algo.ts b/modules/sdk-coin-algo/test/unit/algo.ts index da7e820533..ee8e1d090b 100644 --- a/modules/sdk-coin-algo/test/unit/algo.ts +++ b/modules/sdk-coin-algo/test/unit/algo.ts @@ -1029,10 +1029,10 @@ describe('ALGO:', function () { const expectedAmount = new BigNumber(nativeBalance).minus(fee).minus(MIN_ACCOUNT_BALANCE).toString(); let getBalanceStub: SinonStub; - beforeEach(function () { + beforeEach(async function () { const userSeed = Buffer.from('9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60', 'hex'); userKp = basecoin.generateRootKeyPair(userSeed); - encryptedUserPrv = bitgo.encrypt({ + encryptedUserPrv = await bitgo.encrypt({ input: userKp.prv, password: walletPassphrase, }); @@ -1040,7 +1040,7 @@ describe('ALGO:', function () { const backupSeed = Buffer.from('6d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60', 'hex'); backupKp = basecoin.generateRootKeyPair(backupSeed); - encryptedBackupPrv = bitgo.encrypt({ + encryptedBackupPrv = await bitgo.encrypt({ input: backupKp.prv, password: walletPassphrase, }); @@ -1142,41 +1142,38 @@ describe('ALGO:', function () { }); }); - it('should throw error if the walletPassphrase is incorrect', () => { - assert.throws( - () => { + it('should throw error if the walletPassphrase is incorrect', async () => { + await assert.rejects( + () => basecoin.assertIsValidKey({ encryptedPrv: key, walletPassphrase: 'foo', - }); - }, - { message: "failed to decrypt prv: password error - ccm: tag doesn't match" } + }), + { message: 'failed to decrypt prv: incorrect password' } ); }); - it('should throw error if the key is altered', () => { + it('should throw error if the key is altered', async () => { const alteredKey = key.replace(/[0-9]/g, '0'); - assert.throws( - () => { + await assert.rejects( + () => basecoin.assertIsValidKey({ encryptedPrv: alteredKey, walletPassphrase, - }); - }, - { message: 'failed to decrypt prv: json decrypt: invalid parameters' } + }), + { message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON' } ); }); - it('should throw error if the key is not a valid key', () => { + it('should throw error if the key is not a valid key', async () => { const invalidKey = '#@)$#($*@)#($*'; - const encryptedPrv = encrypt(walletPassphrase, invalidKey); - assert.throws( - () => { + const encryptedPrv = await encrypt(walletPassphrase, invalidKey); + await assert.rejects( + () => basecoin.assertIsValidKey({ encryptedPrv, walletPassphrase, - }); - }, + }), { message: 'Invalid private key: Invalid base32 characters' } ); }); diff --git a/modules/sdk-coin-avaxc/src/avaxc.ts b/modules/sdk-coin-avaxc/src/avaxc.ts index ce5f533405..a0b897b4ea 100644 --- a/modules/sdk-coin-avaxc/src/avaxc.ts +++ b/modules/sdk-coin-avaxc/src/avaxc.ts @@ -635,7 +635,7 @@ export class AvaxC extends AbstractEthLikeNewCoins { : new optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice)); if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userKey = await this.bitgo.decryptAsync({ + userKey = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -654,7 +654,7 @@ export class AvaxC extends AbstractEthLikeNewCoins { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-dot/src/dot.ts b/modules/sdk-coin-dot/src/dot.ts index 148f65e707..50f084b62c 100644 --- a/modules/sdk-coin-dot/src/dot.ts +++ b/modules/sdk-coin-dot/src/dot.ts @@ -410,7 +410,7 @@ export class Dot extends BaseCoin { // Decrypt private keys from KeyCard values let userPrv; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -422,7 +422,7 @@ export class Dot extends BaseCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-eos/src/eos.ts b/modules/sdk-coin-eos/src/eos.ts index 33d8a459c6..52d6eec779 100644 --- a/modules/sdk-coin-eos/src/eos.ts +++ b/modules/sdk-coin-eos/src/eos.ts @@ -20,7 +20,7 @@ import { BitGoBase, checkKrsProvider, Environments, - getBip32KeysAsync, + getBip32Keys, getIsKrsRecovery, getIsUnsignedSweep, HalfSignedAccountTransaction as BaseHalfSignedTransaction, @@ -905,7 +905,7 @@ export class Eos extends BaseCoin { throw new Error('Invalid destination address!'); } - const keys = await getBip32KeysAsync(this.bitgo, params, { requireBitGoXpub: false }); + const keys = await getBip32Keys(this.bitgo, params, { requireBitGoXpub: false }); const rootAddressDetails = this.getAddressDetails(params.rootAddress); const account = await this.getAccountFromNode({ address: rootAddressDetails.address }); diff --git a/modules/sdk-coin-etc/src/etc.ts b/modules/sdk-coin-etc/src/etc.ts index 078416c7ad..1bb99229d1 100644 --- a/modules/sdk-coin-etc/src/etc.ts +++ b/modules/sdk-coin-etc/src/etc.ts @@ -89,7 +89,7 @@ export class Etc extends AbstractEthLikeCoin { if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userKey = await this.bitgo.decryptAsync({ + userKey = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -108,7 +108,7 @@ export class Etc extends AbstractEthLikeCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-eth/src/erc20Token.ts b/modules/sdk-coin-eth/src/erc20Token.ts index b07b65df62..75c3096787 100644 --- a/modules/sdk-coin-eth/src/erc20Token.ts +++ b/modules/sdk-coin-eth/src/erc20Token.ts @@ -171,7 +171,7 @@ export class Erc20Token extends Eth { let userPrv; if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -191,7 +191,7 @@ export class Erc20Token extends Eth { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-eth/src/erc721Token.ts b/modules/sdk-coin-eth/src/erc721Token.ts index 86d5fc51a6..45cb4e79db 100644 --- a/modules/sdk-coin-eth/src/erc721Token.ts +++ b/modules/sdk-coin-eth/src/erc721Token.ts @@ -171,7 +171,7 @@ export class Erc721Token extends Eth { let userPrv; if (!userKey.startsWith('xpub') && !userKey.startsWith('xprv')) { try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -191,7 +191,7 @@ export class Erc721Token extends Eth { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-eth/src/eth.ts b/modules/sdk-coin-eth/src/eth.ts index 2ba3b225a2..03a3c92e71 100644 --- a/modules/sdk-coin-eth/src/eth.ts +++ b/modules/sdk-coin-eth/src/eth.ts @@ -217,7 +217,7 @@ export class Eth extends AbstractEthLikeNewCoins { : new optionalDeps.ethUtil.BN(this.setGasPrice(params.gasPrice)); if (!isUnsignedSweep) { try { - userKey = await this.bitgo.decryptAsync({ + userKey = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -238,7 +238,7 @@ export class Eth extends AbstractEthLikeNewCoins { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-eth/test/unit/eth.ts b/modules/sdk-coin-eth/test/unit/eth.ts index ebc5cfbc25..24bc0339c0 100644 --- a/modules/sdk-coin-eth/test/unit/eth.ts +++ b/modules/sdk-coin-eth/test/unit/eth.ts @@ -2715,26 +2715,26 @@ describe('ETH:', function () { }); }); - it('should throw error if the walletPassphrase is incorrect', () => { - assert.throws( + it('should throw error if the walletPassphrase is incorrect', async () => { + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: key, walletPassphrase: 'foo', }), - { message: "failed to decrypt prv: password error - ccm: tag doesn't match" } + { message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON' } ); }); - it('should throw error if the key is altered', () => { + it('should throw error if the key is altered', async () => { const alteredKey = key.replace(/[0-9]/g, '0'); - assert.throws( + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: alteredKey, walletPassphrase: 'kAm[EFQ6o=SxlcLFDw%,', }), - { message: 'failed to decrypt prv: json decrypt: invalid parameters' } + { message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON' } ); }); }); @@ -2754,9 +2754,9 @@ describe('ETH:', function () { }); }); - it('should throw if the commonKeychain is altered', () => { + it('should throw if the commonKeychain is altered', async () => { const alteredCommonKeychain = generateRandomPassword(10); - assert.throws( + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: key, @@ -2768,9 +2768,9 @@ describe('ETH:', function () { ); }); - it('should throw error if the walletPassphrase is incorrect', () => { + it('should throw error if the walletPassphrase is incorrect', async () => { const incorrectPassphrase = 'foo'; - assert.throws( + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: key, @@ -2778,13 +2778,13 @@ describe('ETH:', function () { walletPassphrase: incorrectPassphrase, multiSigType, }), - { message: "failed to decrypt prv: password error - ccm: tag doesn't match" } + { message: 'failed to decrypt prv: incorrect password' } ); }); - it('should throw error if the key is altered', () => { + it('should throw error if the key is altered', async () => { const alteredKey = key.replace(/[0-9]/g, '0'); - assert.throws( + await assert.rejects( () => coin.assertIsValidKey({ encryptedPrv: alteredKey, @@ -2792,7 +2792,7 @@ describe('ETH:', function () { walletPassphrase, multiSigType, }), - { message: 'failed to decrypt prv: json decrypt: invalid parameters' } + { message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON' } ); }); }); diff --git a/modules/sdk-coin-eth/test/unit/ethWallet.ts b/modules/sdk-coin-eth/test/unit/ethWallet.ts index 3e36160e4c..981117a468 100644 --- a/modules/sdk-coin-eth/test/unit/ethWallet.ts +++ b/modules/sdk-coin-eth/test/unit/ethWallet.ts @@ -224,11 +224,11 @@ describe('Ethereum Hop Transactions', function () { let gasLimitEstimate; let gasPrice; - const nockUserKey = function () { + const nockUserKey = async function () { nock(bgUrl) .get(`/api/v2/teth/key/user`) .reply(200, { - encryptedPrv: bitgo.encrypt({ input: userKeypair.xprv, password: TestBitGo.TEST_WALLET1_PASSCODE }), + encryptedPrv: await bitgo.encrypt({ input: userKeypair.xprv, password: TestBitGo.TEST_WALLET1_PASSCODE }), path: userKeypair.path + userKeypair.walletSubPath, }); }; @@ -272,7 +272,7 @@ describe('Ethereum Hop Transactions', function () { }); it('should prebuild a hop transaction if given the correct args', async function () { - nockUserKey(); + await nockUserKey(); const feeScope = nockFees(); nockBuild(ethWallet.id()); const res = (await ethWallet.prebuildTransaction(buildParams)) as any; diff --git a/modules/sdk-coin-hbar/src/hbar.ts b/modules/sdk-coin-hbar/src/hbar.ts index a70da6f0dd..86db4bf1ad 100644 --- a/modules/sdk-coin-hbar/src/hbar.ts +++ b/modules/sdk-coin-hbar/src/hbar.ts @@ -597,8 +597,8 @@ export class Hbar extends BaseCoin { let backUp: string | undefined; if (!isUnsignedSweep) { try { - userPrv = await this.bitgo.decryptAsync({ input: params.userKey, password: params.walletPassphrase }); - backUp = await this.bitgo.decryptAsync({ input: params.backupKey, password: params.walletPassphrase }); + userPrv = await this.bitgo.decrypt({ input: params.userKey, password: params.walletPassphrase }); + backUp = await this.bitgo.decrypt({ input: params.backupKey, password: params.walletPassphrase }); } catch (e) { throw new Error( 'unable to decrypt userKey or backupKey with the walletPassphrase provided, got error: ' + e.message diff --git a/modules/sdk-coin-hbar/test/unit/hbar.ts b/modules/sdk-coin-hbar/test/unit/hbar.ts index 4e99206ead..996ad20961 100644 --- a/modules/sdk-coin-hbar/test/unit/hbar.ts +++ b/modules/sdk-coin-hbar/test/unit/hbar.ts @@ -1673,21 +1673,21 @@ describe('Hedera Hashgraph:', function () { }); }); - it('should throw error if the walletPassphrase is incorrect', () => { - assert.throws( + it('should throw error if the walletPassphrase is incorrect', async () => { + await assert.rejects( () => basecoin.assertIsValidKey({ coinName: 'hbar', encryptedPrv: key, walletPassphrase: 'foo', }), - { message: "failed to decrypt prv: password error - ccm: tag doesn't match" } + { message: 'failed to decrypt prv: incorrect password' } ); }); - it('should throw error if the key is altered', () => { + it('should throw error if the key is altered', async () => { const alteredKey = key.replace(/[0-9]/g, '0'); - assert.throws( + await assert.rejects( () => basecoin.assertIsValidKey({ coinName: 'hbar', @@ -1695,15 +1695,15 @@ describe('Hedera Hashgraph:', function () { walletPassphrase, }), { - message: 'failed to decrypt prv: json decrypt: invalid parameters', + message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON', } ); }); - it('should throw error if the key is not a valid key', () => { + it('should throw error if the key is not a valid key', async () => { const invalidKey = '#@)$#($*@)#($*'; - const encryptedPrv = encrypt(walletPassphrase, invalidKey); - assert.throws( + const encryptedPrv = await encrypt(walletPassphrase, invalidKey); + await assert.rejects( () => basecoin.assertIsValidKey({ coinName: 'hbar', diff --git a/modules/sdk-coin-iota/src/iota.ts b/modules/sdk-coin-iota/src/iota.ts index ee0529d284..2c19465caa 100644 --- a/modules/sdk-coin-iota/src/iota.ts +++ b/modules/sdk-coin-iota/src/iota.ts @@ -823,7 +823,7 @@ export class Iota extends BaseCoin { // Decrypt private keys from KeyCard values let userPrv: string; try { - userPrv = await this.bitgo.decryptAsync({ input: userKey, password: params.walletPassphrase }); + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase }); } catch (e) { throw new Error(`Error decrypting user keychain: ${(e as Error).message}`); } @@ -831,7 +831,7 @@ export class Iota extends BaseCoin { let backupPrv: string; try { - backupPrv = await this.bitgo.decryptAsync({ input: backupKey, password: params.walletPassphrase }); + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase }); } catch (e) { throw new Error(`Error decrypting backup keychain: ${(e as Error).message}`); } diff --git a/modules/sdk-coin-near/src/near.ts b/modules/sdk-coin-near/src/near.ts index 0ec0112335..f4ca475aa0 100644 --- a/modules/sdk-coin-near/src/near.ts +++ b/modules/sdk-coin-near/src/near.ts @@ -654,7 +654,7 @@ export class Near extends BaseCoin { // Decrypt private keys from KeyCard values let userPrv; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -666,7 +666,7 @@ export class Near extends BaseCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-near/test/unit/tokenEnablementValidation.ts b/modules/sdk-coin-near/test/unit/tokenEnablementValidation.ts index ba34e2333b..79ec293a66 100644 --- a/modules/sdk-coin-near/test/unit/tokenEnablementValidation.ts +++ b/modules/sdk-coin-near/test/unit/tokenEnablementValidation.ts @@ -391,7 +391,7 @@ describe('NEAR Token Enablement Validation', function () { const spoofedTxHex = testData.rawTx.storageDeposit.unsigned; const bgUrl = common.Environments['test'].uri; - const encryptedPrv = bitgo.encrypt({ + const encryptedPrv = await bitgo.encrypt({ input: testData.accounts.account1.secretKey, password: 'test', }); diff --git a/modules/sdk-coin-polyx/src/polyx.ts b/modules/sdk-coin-polyx/src/polyx.ts index 5398048267..c0d1f70b97 100644 --- a/modules/sdk-coin-polyx/src/polyx.ts +++ b/modules/sdk-coin-polyx/src/polyx.ts @@ -205,7 +205,7 @@ export class Polyx extends SubstrateCoin { // Decrypt private keys from KeyCard values let userPrv; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -216,7 +216,7 @@ export class Polyx extends SubstrateCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-sol/src/sol.ts b/modules/sdk-coin-sol/src/sol.ts index c605bd029f..574d8fda68 100644 --- a/modules/sdk-coin-sol/src/sol.ts +++ b/modules/sdk-coin-sol/src/sol.ts @@ -1945,7 +1945,7 @@ export class Sol extends BaseCoin { if (!isMpcV2) { let userPrv: string; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase!, }); @@ -1956,7 +1956,7 @@ export class Sol extends BaseCoin { let backupPrv: string; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase!, }); diff --git a/modules/sdk-coin-sol/test/unit/sol.ts b/modules/sdk-coin-sol/test/unit/sol.ts index 26ed1ea9bb..a8d8285b4c 100644 --- a/modules/sdk-coin-sol/test/unit/sol.ts +++ b/modules/sdk-coin-sol/test/unit/sol.ts @@ -3061,8 +3061,8 @@ describe('SOL:', function () { const [otherUserDkg] = await MPSUtil.generateEdDsaDKGKeyShares(); const walletPassphrase = testData.keys.walletPassword; - mpcV2UserKey = encrypt(walletPassphrase, userDkg.getReducedKeyShare().toString('base64')); - mpcV2BackupKey = encrypt(walletPassphrase, backupDkg.getReducedKeyShare().toString('base64')); + mpcV2UserKey = await encrypt(walletPassphrase, userDkg.getReducedKeyShare().toString('base64')); + mpcV2BackupKey = await encrypt(walletPassphrase, backupDkg.getReducedKeyShare().toString('base64')); mpcV2CommonKeyChain = userDkg.getCommonKeychain(); mpcV2WalletAddress = new KeyPair({ pub: deriveUnhardenedMps(mpcV2CommonKeyChain, 'm/0').slice(0, 64), @@ -3072,8 +3072,8 @@ describe('SOL:', function () { pub: deriveUnhardenedMps(mismatchedBitgoKey, 'm/0').slice(0, 64), }).getAddress(); - mpcV2TokenUserKey = encrypt(walletPassphrase, tokenUserDkg.getReducedKeyShare().toString('base64')); - mpcV2TokenBackupKey = encrypt(walletPassphrase, tokenBackupDkg.getReducedKeyShare().toString('base64')); + mpcV2TokenUserKey = await encrypt(walletPassphrase, tokenUserDkg.getReducedKeyShare().toString('base64')); + mpcV2TokenBackupKey = await encrypt(walletPassphrase, tokenBackupDkg.getReducedKeyShare().toString('base64')); mpcV2TokenCommonKeyChain = tokenUserDkg.getCommonKeychain(); mpcV2TokenWalletAddress = new KeyPair({ pub: deriveUnhardenedMps(mpcV2TokenCommonKeyChain, 'm/0').slice(0, 64), @@ -3956,16 +3956,16 @@ describe('SOL:', function () { const [tokenUserDkg, tokenBackupDkg] = await MPSUtil.generateEdDsaDKGKeyShares(); walletPassphrase = testData.keys.walletPassword; - mpcV2UserKey = encrypt(walletPassphrase, userDkg.getReducedKeyShare().toString('base64')); - mpcV2BackupKey = encrypt(walletPassphrase, backupDkg.getReducedKeyShare().toString('base64')); + mpcV2UserKey = await encrypt(walletPassphrase, userDkg.getReducedKeyShare().toString('base64')); + mpcV2BackupKey = await encrypt(walletPassphrase, backupDkg.getReducedKeyShare().toString('base64')); mpcV2CommonKeyChain = userDkg.getCommonKeychain(); mpcV2Address1 = new KeyPair({ pub: deriveUnhardenedMps(mpcV2CommonKeyChain, 'm/1').slice(0, 64) }).getAddress(); mpcV2Address2 = new KeyPair({ pub: deriveUnhardenedMps(mpcV2CommonKeyChain, 'm/2').slice(0, 64) }).getAddress(); mpcV2Address3 = new KeyPair({ pub: deriveUnhardenedMps(mpcV2CommonKeyChain, 'm/3').slice(0, 64) }).getAddress(); - mpcV2TokenUserKey = encrypt(walletPassphrase, tokenUserDkg.getReducedKeyShare().toString('base64')); - mpcV2TokenBackupKey = encrypt(walletPassphrase, tokenBackupDkg.getReducedKeyShare().toString('base64')); + mpcV2TokenUserKey = await encrypt(walletPassphrase, tokenUserDkg.getReducedKeyShare().toString('base64')); + mpcV2TokenBackupKey = await encrypt(walletPassphrase, tokenBackupDkg.getReducedKeyShare().toString('base64')); mpcV2TokenCommonKeyChain = tokenUserDkg.getCommonKeychain(); mpcV2TokenBaseAddress = new KeyPair({ @@ -4363,9 +4363,9 @@ describe('SOL:', function () { }); }); - it('should throw error if the commonKeychain is invalid', () => { + it('should throw error if the commonKeychain is invalid', async () => { const alteredCommonKeychain = generateRandomPassword(10); - assert.throws( + await assert.rejects( () => basecoin.assertIsValidKey({ encryptedPrv: key, @@ -4379,9 +4379,9 @@ describe('SOL:', function () { ); }); - it('should throw error if the walletPassphrase is incorrect', () => { + it('should throw error if the walletPassphrase is incorrect', async () => { const incorrectPassphrase = 'foo'; - assert.throws( + await assert.rejects( () => basecoin.assertIsValidKey({ encryptedPrv: key, @@ -4390,14 +4390,14 @@ describe('SOL:', function () { multiSigType, }), { - message: "failed to decrypt prv: password error - ccm: tag doesn't match", + message: 'failed to decrypt prv: incorrect password', } ); }); - it('should throw error if the key is altered', () => { + it('should throw error if the key is altered', async () => { const alteredKey = key.replace(/[0-9]/g, '0'); - assert.throws( + await assert.rejects( () => basecoin.assertIsValidKey({ encryptedPrv: alteredKey, @@ -4406,7 +4406,7 @@ describe('SOL:', function () { multiSigType, }), { - message: 'failed to decrypt prv: json decrypt: invalid parameters', + message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON', } ); }); @@ -4426,7 +4426,7 @@ describe('SOL:', function () { }, multisigType: 'tss', }; - const fakePrv = encrypt('password', 'prv'); + const fakePrv = await encrypt('password', 'prv'); const walletObj = new Wallet(bitgo, basecoin, walletData); const bgUrl = common.Environments['mock'].uri; diff --git a/modules/sdk-coin-stx/src/stx.ts b/modules/sdk-coin-stx/src/stx.ts index 622050447f..eee65ca6b4 100644 --- a/modules/sdk-coin-stx/src/stx.ts +++ b/modules/sdk-coin-stx/src/stx.ts @@ -4,7 +4,7 @@ import { BaseTransaction, BitGoBase, Environments, - getBip32KeysAsync, + getBip32Keys, getIsUnsignedSweep, KeyPair, MethodNotImplementedError, @@ -701,7 +701,7 @@ export class Stx extends BaseCoin { } } const isUnsignedSweep = getIsUnsignedSweep(params); - const keys = await getBip32KeysAsync(this.bitgo, params, { requireBitGoXpub: true }); + const keys = await getBip32Keys(this.bitgo, params, { requireBitGoXpub: true }); const rootAddressDetails = getAddressDetails(params.rootAddress); const [accountBalanceData, accountNonceData] = await Promise.all([ this.getNativeStxBalanceFromNode({ address: rootAddressDetails.address }), diff --git a/modules/sdk-coin-sui/src/sui.ts b/modules/sdk-coin-sui/src/sui.ts index 8809696be6..00d5554666 100644 --- a/modules/sdk-coin-sui/src/sui.ts +++ b/modules/sdk-coin-sui/src/sui.ts @@ -658,7 +658,7 @@ export class Sui extends BaseCoin { // Decrypt private keys from KeyCard values let userPrv: string; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -670,7 +670,7 @@ export class Sui extends BaseCoin { let backupPrv: string; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-ton/src/ton.ts b/modules/sdk-coin-ton/src/ton.ts index 4abdee90e7..792790f429 100644 --- a/modules/sdk-coin-ton/src/ton.ts +++ b/modules/sdk-coin-ton/src/ton.ts @@ -458,7 +458,7 @@ export class Ton extends BaseCoin { let userPrv; try { - userPrv = await this.bitgo.decryptAsync({ + userPrv = await this.bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -469,7 +469,7 @@ export class Ton extends BaseCoin { let backupPrv; try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-ton/test/unit/ton.ts b/modules/sdk-coin-ton/test/unit/ton.ts index 41e9215ea6..9862cc95dc 100644 --- a/modules/sdk-coin-ton/test/unit/ton.ts +++ b/modules/sdk-coin-ton/test/unit/ton.ts @@ -715,7 +715,7 @@ describe('TON:', function () { sandbox.stub(Tonweb, 'HttpProvider').returns(mockProvider); - const decryptStub = sandbox.stub(bitgo, 'decryptAsync'); + const decryptStub = sandbox.stub(bitgo, 'decrypt'); decryptStub.onFirstCall().resolves(JSON.stringify({ dummy: 'userSigningMaterial' })); decryptStub.onSecondCall().resolves(JSON.stringify({ dummy: 'backupSigningMaterial' })); diff --git a/modules/sdk-coin-trx/src/trx.ts b/modules/sdk-coin-trx/src/trx.ts index 507ee73bdd..0a00781e88 100644 --- a/modules/sdk-coin-trx/src/trx.ts +++ b/modules/sdk-coin-trx/src/trx.ts @@ -10,7 +10,7 @@ import { BaseCoin, BitGoBase, common, - getBip32KeysAsync, + getBip32Keys, getIsKrsRecovery, getIsUnsignedSweep, KeyPair, @@ -853,7 +853,7 @@ export class Trx extends BaseCoin { } // get our user, backup keys - const keys = await getBip32KeysAsync(this.bitgo, params, { requireBitGoXpub: false }); + const keys = await getBip32Keys(this.bitgo, params, { requireBitGoXpub: false }); // we need to decode our bitgoKey to a base58 address const bitgoHexAddr = this.pubToHexAddress(this.xpubToUncompressedPub(params.bitgoKey)); @@ -1088,7 +1088,7 @@ export class Trx extends BaseCoin { return this.formatForOfflineVault(await txBuilder.build(), SAFE_TRON_TOKEN_TRANSACTION_FEE, recoveryAmount); } - const userPrvHex = await this.bitgo.decryptAsync({ + const userPrvHex = await this.bitgo.decrypt({ password: params.walletPassphrase!, input: params.userKey, }); @@ -1116,7 +1116,7 @@ export class Trx extends BaseCoin { return this.formatForOfflineVault(tx, SAFE_TRON_TRANSACTION_FEE, recoveryAmountMinusFees); } - const userPrvHex = await this.bitgo.decryptAsync({ + const userPrvHex = await this.bitgo.decrypt({ password: params.walletPassphrase!, input: params.userKey, }); @@ -1145,7 +1145,7 @@ export class Trx extends BaseCoin { ); } - const keys = await getBip32KeysAsync(this.bitgo, params, { requireBitGoXpub: false }); + const keys = await getBip32Keys(this.bitgo, params, { requireBitGoXpub: false }); const baseAddrHex = this.pubToHexAddress(this.xpubToUncompressedPub(params.bitgoKey)); const txnsBatch: RecoveryTransaction[] = []; diff --git a/modules/sdk-coin-trx/test/resources.ts b/modules/sdk-coin-trx/test/resources.ts index 9a477c0761..11dd695711 100644 --- a/modules/sdk-coin-trx/test/resources.ts +++ b/modules/sdk-coin-trx/test/resources.ts @@ -244,7 +244,7 @@ export const TestRecoverData = { * bitgoKey is a compressed secp256k1 public key (66 hex chars) used as the commonKeychain. * The wallet address is derived from it via new TronKeyPair({ pub: bitgoKey }).getAddress(). * userPrvKey is the raw secp256k1 private key hex that the encrypted userKey decrypts to. - * userKey is a placeholder encrypted blob; tests mock decryptAsync to return userPrvKey directly. + * userKey is a placeholder encrypted blob; tests mock decrypt to return userPrvKey directly. */ export const TssTestRecoverData = { // compressed pub of PARTICIPANTS.from.pk — used as commonKeychain (bitgoKey) @@ -256,11 +256,11 @@ export const TssTestRecoverData = { 'xpub6BvMpt8ke8tCycBBw6uDob6PyNBkHbTyEztaRuwdMZhpiFk1mXpS7P7iv4c4w7XWFFRySMokUuFUqqgpZxK5wLxm6pgjpkNFhKsMaXTJoUN', backupKey: 'xpub687kC8LeSJwj1gYQr4Js2BHbLK1nFeLvMzsDmH2LKMNrqAHNfeCw1sp61cbf2WxeY1QssaUBh9EFJbJ9LBuPivv7XDsFPVaFYj19ueCNczT', - // encrypted userKey JSON (placeholder — tests mock decryptAsync) + // encrypted userKey JSON (placeholder — tests mock decrypt) encryptedUserKey: '{"iv":"abc","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","ct":"xyz"}', walletPassphrase: 'testpassphrase123', - // raw secp256k1 private key hex returned when decryptAsync is called (PARTICIPANTS.custodian.pk) + // raw secp256k1 private key hex returned when decrypt is called (PARTICIPANTS.custodian.pk) userPrvKey: 'c4b3a04836efc2ee2917235f55ccfb2dcf6b8341e5ea0405da5ba10cd526dfed', recoveryDestination: 'TWkzN4WjxkyoRTmFHaMQ9po77uEerngjyQ', }; diff --git a/modules/sdk-coin-trx/test/unit/trx.ts b/modules/sdk-coin-trx/test/unit/trx.ts index 6ef3e9eb35..a8cccb56f6 100644 --- a/modules/sdk-coin-trx/test/unit/trx.ts +++ b/modules/sdk-coin-trx/test/unit/trx.ts @@ -689,7 +689,7 @@ describe('TRON:', function () { return undefined; }); - mock.method(bitgo as any, 'decryptAsync', () => { + mock.method(bitgo as any, 'decrypt', () => { return Promise.resolve(TssTestRecoverData.userPrvKey); }); @@ -764,7 +764,7 @@ describe('TRON:', function () { return Promise.resolve(SampleRawTokenSendTxn); }); - mock.method(bitgo as any, 'decryptAsync', () => { + mock.method(bitgo as any, 'decrypt', () => { return Promise.resolve(TssTestRecoverData.userPrvKey); }); diff --git a/modules/sdk-coin-xlm/src/getStellarKeys.ts b/modules/sdk-coin-xlm/src/getStellarKeys.ts index 338d9e7265..bc0c751ed7 100644 --- a/modules/sdk-coin-xlm/src/getStellarKeys.ts +++ b/modules/sdk-coin-xlm/src/getStellarKeys.ts @@ -13,7 +13,7 @@ export async function getStellarKeys(bitgo: BitGoBase, params: InitiateRecoveryO try { if (!userKey.startsWith('S') && !userKey.startsWith('G')) { - userKey = await bitgo.decryptAsync({ + userKey = await bitgo.decrypt({ input: userKey, password: params.walletPassphrase, }); @@ -34,7 +34,7 @@ export async function getStellarKeys(bitgo: BitGoBase, params: InitiateRecoveryO try { if (!backupKey.startsWith('S') && !isKrsRecovery && !isUnsignedSweep) { - backupKey = await bitgo.decryptAsync({ + backupKey = await bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-coin-xlm/test/unit/xlm.ts b/modules/sdk-coin-xlm/test/unit/xlm.ts index 859da463e3..64e4eba532 100644 --- a/modules/sdk-coin-xlm/test/unit/xlm.ts +++ b/modules/sdk-coin-xlm/test/unit/xlm.ts @@ -1203,37 +1203,37 @@ describe('XLM:', function () { }); }); - it('should throw error if the walletPassphrase is incorrect', () => { - assert.throws( + it('should throw error if the walletPassphrase is incorrect', async () => { + await assert.rejects( () => basecoin.assertIsValidKey({ encryptedPrv: key, walletPassphrase: 'foo', }), { - message: "failed to decrypt prv: password error - ccm: tag doesn't match", + message: 'failed to decrypt prv: incorrect password', } ); }); - it('should throw error if the key is altered', () => { + it('should throw error if the key is altered', async () => { const alteredKey = key.replace(/[0-9]/g, '0'); - assert.throws( + await assert.rejects( () => basecoin.assertIsValidKey({ encryptedPrv: alteredKey, walletPassphrase, }), { - message: 'failed to decrypt prv: json decrypt: invalid parameters', + message: 'failed to decrypt prv: decrypt: ciphertext is not valid JSON', } ); }); - it('should return { isValid: false } if the key is not a valid key', () => { + it('should return { isValid: false } if the key is not a valid key', async () => { const invalidKey = '#@)$#($*@)#($*'; - const encryptedPrv = encrypt(walletPassphrase, invalidKey); - assert.throws( + const encryptedPrv = await encrypt(walletPassphrase, invalidKey); + await assert.rejects( () => basecoin.assertIsValidKey({ encryptedPrv, diff --git a/modules/sdk-coin-xrp/src/xrp.ts b/modules/sdk-coin-xrp/src/xrp.ts index 31818992b2..fb14c7429c 100644 --- a/modules/sdk-coin-xrp/src/xrp.ts +++ b/modules/sdk-coin-xrp/src/xrp.ts @@ -11,7 +11,7 @@ import { BaseCoin, BitGoBase, checkKrsProvider, - getBip32KeysAsync, + getBip32Keys, InvalidAddressError, KeyPair, MethodNotImplementedError, @@ -592,7 +592,7 @@ export class Xrp extends BaseCoin { throw new Error('Invalid destination address!'); } - const keys = await getBip32KeysAsync(this.bitgo, params, { requireBitGoXpub: false }); + const keys = await getBip32Keys(this.bitgo, params, { requireBitGoXpub: false }); const { addressDetails, feeDetails, serverDetails, accountLines } = await promiseProps({ addressDetails: this.bitgo.post(rippledUrl).send(accountInfoParams), diff --git a/modules/sdk-coin-xtz/src/xtz.ts b/modules/sdk-coin-xtz/src/xtz.ts index 9eb0b67609..559b6b539e 100644 --- a/modules/sdk-coin-xtz/src/xtz.ts +++ b/modules/sdk-coin-xtz/src/xtz.ts @@ -351,7 +351,7 @@ export class Xtz extends BaseCoin { keyPair = new KeyPair({ pub: backupKey }); } else { try { - backupPrv = await this.bitgo.decryptAsync({ + backupPrv = await this.bitgo.decrypt({ input: backupKey, password: params.walletPassphrase, }); diff --git a/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts b/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts index 56fdef4ec4..0f5a3c142b 100644 --- a/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts +++ b/modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts @@ -661,29 +661,14 @@ export abstract class BaseCoin implements IBaseCoin { } /** @inheritDoc */ - assertIsValidKey(params: AuditKeyParams): void { + async assertIsValidKey(params: AuditKeyParams): Promise { if (!params.encryptedPrv) { throw new Error('encryptedPrv is required'); } let decryptedKey: string; try { - decryptedKey = this.bitgo.decrypt({ password: params.walletPassphrase, input: params.encryptedPrv }); - } catch (e) { - throw new Error(`failed to decrypt prv: ${e.message}`); - } - this.auditDecryptedKey({ ...params, prv: decryptedKey }); - } - - /** @inheritDoc */ - async assertIsValidKeyAsync(params: AuditKeyParams): Promise { - if (!params.encryptedPrv) { - throw new Error('encryptedPrv is required'); - } - let decryptedKey: string; - - try { - decryptedKey = await this.bitgo.decryptAsync({ password: params.walletPassphrase, input: params.encryptedPrv }); + decryptedKey = await this.bitgo.decrypt({ password: params.walletPassphrase, input: params.encryptedPrv }); } catch (e) { throw new Error(`failed to decrypt prv: ${e.message}`); } diff --git a/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts b/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts index 8d4cda4a27..3d9f7ecc18 100644 --- a/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts +++ b/modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts @@ -748,7 +748,6 @@ export interface IBaseCoin { * @param {string} params.walletPassphrase - The passphrase to decrypt the private key * @param {string} params.multiSigType - The type of multisig (e.g. 'onchain' or 'tss') */ - assertIsValidKey({ publicKey, encryptedPrv, walletPassphrase, multiSigType }: AuditKeyParams): void; - assertIsValidKeyAsync(params: AuditKeyParams): Promise; + assertIsValidKey(params: AuditKeyParams): Promise; requiresWalletInitializationTransaction(): boolean; } diff --git a/modules/sdk-core/src/bitgo/bitgoBase.ts b/modules/sdk-core/src/bitgo/bitgoBase.ts index ed582f2f37..4be871cbdf 100644 --- a/modules/sdk-core/src/bitgo/bitgoBase.ts +++ b/modules/sdk-core/src/bitgo/bitgoBase.ts @@ -15,13 +15,10 @@ import { EcdhDerivedKeypair, GetSigningKeyApi } from './keychain'; export interface BitGoBase { wallets(): any; // TODO - define v1 wallets type coin(coinName: string): IBaseCoin; // need to change it to BaseCoin once it's moved to @bitgo/sdk-core - decrypt(params: DecryptOptions): string; - decryptAsync(params: DecryptOptions): Promise; - decryptKeys(params: DecryptKeysOptions): string[]; - decryptKeysAsync(params: DecryptKeysOptions): Promise; + decrypt(params: DecryptOptions): Promise; + decryptKeys(params: DecryptKeysOptions): Promise; del(url: string): BitGoRequest; - encrypt(params: EncryptOptions): string; - encryptAsync(params: EncryptOptions): Promise; + encrypt(params: EncryptOptions): Promise; createEncryptionSession(password: string): Promise; readonly env: EnvironmentName; fetchConstants(): Promise; diff --git a/modules/sdk-core/src/bitgo/internal/keycard.ts b/modules/sdk-core/src/bitgo/internal/keycard.ts index a58698d836..9c7d537d52 100644 --- a/modules/sdk-core/src/bitgo/internal/keycard.ts +++ b/modules/sdk-core/src/bitgo/internal/keycard.ts @@ -6,7 +6,7 @@ */ import { isUndefined } from 'lodash'; import { Keychain } from '../keychain'; -import { EncryptFn, EncryptFnAsync, EncryptionVersion } from '../../api'; +import { EncryptFnAsync, EncryptionVersion } from '../../api'; /** * Return the list of questions that will appear on the second page of the keycard @@ -82,7 +82,7 @@ const generateQuestions = (coin: string) => { }; interface GetKeyDataOptions { - encrypt: EncryptFn; + encrypt: EncryptFnAsync; userKeychain: Keychain; bitgoKeychain: Keychain; backupKeychain: Keychain; @@ -91,12 +91,8 @@ interface GetKeyDataOptions { passcodeEncryptionCode?: string; walletKeyID?: string; backupKeyID?: string; -} - -type GetKeyDataAsyncOptions = Omit & { - encrypt: EncryptFnAsync; encryptionVersion?: EncryptionVersion; -}; +} interface BuildKeycardQrDataOptions { userKeychain: Keychain; @@ -181,40 +177,11 @@ function buildKeycardQrData(options: BuildKeycardQrDataOptions, encryptedWalletP } /** - * TODO: Deprecate this function in favor of getKeyDataAsync once v2 encryption is default. - * Collect all data which will go onto the keycard - * @param options - */ -function getKeyData(options: GetKeyDataOptions): any { - const { encrypt, backupKeychain, passphrase, passcodeEncryptionCode, ...qrOptions } = options; - - // When using just 'generateWallet', we get back an unencrypted prv for the backup keychain - // If the user passes in their passphrase, we can encrypt it - if (backupKeychain.prv && passphrase) { - backupKeychain.encryptedPrv = encrypt({ - input: backupKeychain.prv, - password: passphrase, - }); - } - - // If we have the passcode encryption code, create a box D with the encryptedWalletPasscode - let encryptedWalletPasscode: string | undefined; - if (passphrase && passcodeEncryptionCode) { - encryptedWalletPasscode = encrypt({ - input: passphrase, - password: passcodeEncryptionCode, - }); - } - - return buildKeycardQrData({ ...qrOptions, backupKeychain }, encryptedWalletPasscode); -} - -/** - * Async version of getKeyData with support for v2 (Argon2id) encryption. - * Sequential encrypt: backup key first, then passcode (matches original getKeyDataAsync). + * Collect all data which will go onto the keycard. + * Sequential encrypt: backup key first, then passcode. * @param options */ -async function getKeyDataAsync(options: GetKeyDataAsyncOptions): Promise { +async function getKeyData(options: GetKeyDataOptions): Promise { const { encrypt, backupKeychain, passphrase, passcodeEncryptionCode, encryptionVersion, ...qrOptions } = options; if (backupKeychain.prv && passphrase) { @@ -245,14 +212,12 @@ interface DrawKeycardLayoutOptions { coinName: string; } -interface DrawKeycardOptions extends GetKeyDataOptions, DrawKeycardLayoutOptions { +export interface DrawKeycardOptions extends GetKeyDataOptions, DrawKeycardLayoutOptions { coinShortName: string; } -export type DrawKeycardAsyncOptions = Omit & { - encrypt: EncryptFnAsync; - encryptionVersion?: EncryptionVersion; -}; +/** @deprecated Use {@link DrawKeycardOptions}. `drawKeycard` is now async by default. */ +export type DrawKeycardAsyncOptions = DrawKeycardOptions; /** * Render keycard PDF pages from pre-built QR data. @@ -407,51 +372,11 @@ function renderKeycardPdf(options: DrawKeycardLayoutOptions, keyData: any): any } /** - * TODO: Deprecate this function in favor of drawKeycardAsync once v2 encryption is default. - * Draw a keycard into a new pdf document object - * @param options - */ -export function drawKeycard(options: DrawKeycardOptions): any { - const { - encrypt, - passphrase, - passcodeEncryptionCode, - walletKeyID, - backupKeyID, - userKeychain, - bitgoKeychain, - backupKeychain, - coinShortName, - jsPDF, - QRCode, - activationCode, - walletLabel, - coinName, - } = options; - - const keyData = getKeyData({ - encrypt, - coinShortName, - passphrase, - passcodeEncryptionCode, - walletKeyID, - backupKeyID, - userKeychain, - bitgoKeychain, - backupKeychain, - }); - - return renderKeycardPdf({ jsPDF, QRCode, activationCode, walletLabel, coinName }, keyData); -} - -/** - * Async version of drawKeycard with support for v2 (Argon2id) encryption. - * Use this when the encrypt callback may return a Promise (e.g. encryptAsync). - * - * Draw a keycard into a new pdf document object + * Draw a keycard into a new pdf document object. + * Defaults to v2 (Argon2id) encryption for Box D; pass `encryptionVersion: 1` for legacy v1. * @param options */ -export async function drawKeycardAsync(options: DrawKeycardAsyncOptions): Promise { +export async function drawKeycard(options: DrawKeycardOptions): Promise { const { encrypt, passphrase, @@ -471,7 +396,7 @@ export async function drawKeycardAsync(options: DrawKeycardAsyncOptions): Promis } = options; // Get the data for the first page (qr codes) - const keyData = await getKeyDataAsync({ + const keyData = await getKeyData({ encrypt, coinShortName, passphrase, diff --git a/modules/sdk-core/src/bitgo/keychain/decryptKeychain.ts b/modules/sdk-core/src/bitgo/keychain/decryptKeychain.ts index 9c6d0e42c8..d1a83d45f8 100644 --- a/modules/sdk-core/src/bitgo/keychain/decryptKeychain.ts +++ b/modules/sdk-core/src/bitgo/keychain/decryptKeychain.ts @@ -2,9 +2,9 @@ import { BitGoBase } from '../bitgoBase'; import { OptionalKeychainEncryptedKey } from './iKeychains'; import { notEmpty } from '../utils'; -function maybeDecrypt(bitgo: BitGoBase, input: string, password: string): string | undefined { +async function maybeDecrypt(bitgo: BitGoBase, input: string, password: string): Promise { try { - return bitgo.decrypt({ + return await bitgo.decrypt({ input, password, }); @@ -13,43 +13,8 @@ function maybeDecrypt(bitgo: BitGoBase, input: string, password: string): string } } -async function maybeDecryptAsync(bitgo: BitGoBase, input: string, password: string): Promise { - try { - return await bitgo.decryptAsync({ - input, - password, - }); - } catch (_e) { - return undefined; - } -} - -/** - * Decrypts the private key of a keychain (sync, v1 only). - * This method will try the password against the traditional encryptedPrv, - * and any webauthn device encryptedPrvs. - * - * @param bitgo - * @param keychain - * @param password - */ -export function decryptKeychainPrivateKey( - bitgo: BitGoBase, - keychain: OptionalKeychainEncryptedKey, - password: string -): string | undefined { - const prvs = [keychain.encryptedPrv, ...(keychain.webauthnDevices ?? []).map((d) => d.encryptedPrv)].filter(notEmpty); - for (const prv of prvs) { - const decrypted = maybeDecrypt(bitgo, prv, password); - if (decrypted) { - return decrypted; - } - } - return undefined; -} - /** - * Decrypts the private key of a keychain (async, supports v1 and v2 envelopes). + * Decrypts the private key of a keychain (supports v1 and v2 envelopes). * This method will try the password against the traditional encryptedPrv, * and any webauthn device encryptedPrvs. * Auto-detects v1 (SJCL) and v2 (Argon2id) envelopes. @@ -58,14 +23,14 @@ export function decryptKeychainPrivateKey( * @param keychain * @param password */ -export async function decryptKeychainPrivateKeyAsync( +export async function decryptKeychainPrivateKey( bitgo: BitGoBase, keychain: OptionalKeychainEncryptedKey, password: string ): Promise { const prvs = [keychain.encryptedPrv, ...(keychain.webauthnDevices ?? []).map((d) => d.encryptedPrv)].filter(notEmpty); for (const prv of prvs) { - const decrypted = await maybeDecryptAsync(bitgo, prv, password); + const decrypted = await maybeDecrypt(bitgo, prv, password); if (decrypted) { return decrypted; } diff --git a/modules/sdk-core/src/bitgo/keychain/iKeychains.ts b/modules/sdk-core/src/bitgo/keychain/iKeychains.ts index 9feb9e6686..0a80280446 100644 --- a/modules/sdk-core/src/bitgo/keychain/iKeychains.ts +++ b/modules/sdk-core/src/bitgo/keychain/iKeychains.ts @@ -247,8 +247,7 @@ export interface IKeychains { get(params: GetKeychainOptions): Promise; list(params?: ListKeychainOptions): Promise; updatePassword(params: UpdatePasswordOptions): Promise; - updateSingleKeychainPassword(params?: UpdateSingleKeychainPasswordOptions): Keychain; - updateSingleKeychainPasswordAsync(params?: UpdateSingleKeychainPasswordOptions): Promise; + updateSingleKeychainPassword(params?: UpdateSingleKeychainPasswordOptions): Promise; create(params?: { seed?: Buffer; isRootKey?: boolean }): KeyPair; add(params?: AddKeychainOptions): Promise; createBitGo(params?: CreateBitGoOptions): Promise; diff --git a/modules/sdk-core/src/bitgo/keychain/keychains.ts b/modules/sdk-core/src/bitgo/keychain/keychains.ts index 1b2dd307f1..96a8678940 100644 --- a/modules/sdk-core/src/bitgo/keychain/keychains.ts +++ b/modules/sdk-core/src/bitgo/keychain/keychains.ts @@ -114,7 +114,7 @@ export class Keychains implements IKeychains { continue; } try { - const updatedKeychain = await this.updateSingleKeychainPasswordAsync({ + const updatedKeychain = await this.updateSingleKeychainPassword({ keychain: key, oldPassword: params.oldPassword, newPassword: params.newPassword, @@ -130,8 +130,13 @@ export class Keychains implements IKeychains { } } } catch (e) { - // if the password was incorrect, silence the error, throw otherwise - if (!e.message.includes('private key is incorrect')) { + // if the password was incorrect, silence the error, throw otherwise. + // updateSingleKeychainPassword wraps a wrong-password (or corrupt input) failure as + // 'failed to update keychain password: ...', so treat that as a skip-able error. + if ( + !e.message.includes('private key is incorrect') && + !e.message.includes('failed to update keychain password') + ) { throw e; } } @@ -145,40 +150,6 @@ export class Keychains implements IKeychains { return changedKeys; } - /** - * TODO: Deprecate this function in favor of updateSingleKeychainPasswordAsync once v2 encryption is default - * Update the password used to decrypt a single keychain. - * Handles v1 (SJCL) envelopes only. For v2 (Argon2id) support use {@link updateSingleKeychainPasswordAsync}. - * @param params - * @param params.keychain - The keychain whose password should be updated - * @param params.oldPassword - The old password used for encrypting the key - * @param params.newPassword - The new password to be used for encrypting the key - * @returns {Keychain} - */ - updateSingleKeychainPassword(params: UpdateSingleKeychainPasswordOptions = {}): Keychain { - if (!_.isString(params.oldPassword)) { - throw new Error('expected old password to be a string'); - } - - if (!_.isString(params.newPassword)) { - throw new Error('expected new password to be a string'); - } - - if (!_.isObject(params.keychain) || !_.isString(params.keychain.encryptedPrv)) { - throw new Error('expected keychain to be an object with an encryptedPrv property'); - } - - const oldEncryptedPrv = params.keychain.encryptedPrv; - try { - const decryptedPrv = this.bitgo.decrypt({ input: oldEncryptedPrv, password: params.oldPassword }); - const newEncryptedPrv = this.bitgo.encrypt({ input: decryptedPrv, password: params.newPassword }); - return _.assign({}, params.keychain, { encryptedPrv: newEncryptedPrv }); - } catch (e) { - // catching an error here means that the password was incorrect or, less likely, the input to decrypt is corrupted - throw new Error('password used to decrypt keychain private key is incorrect'); - } - } - /** * Helper function to determine the encryption version of a ciphertext by parsing it as JSON and checking the "v" field. * Return undefined if the ciphertext is not a valid JSON or does not contain a supported "v" field. @@ -208,7 +179,7 @@ export class Keychains implements IKeychains { * @param params.newPassword - The new password to be used for encrypting the key * @returns {Promise} */ - async updateSingleKeychainPasswordAsync(params: UpdateSingleKeychainPasswordOptions = {}): Promise { + async updateSingleKeychainPassword(params: UpdateSingleKeychainPasswordOptions = {}): Promise { if (!_.isString(params.oldPassword)) { throw new Error('expected old password to be a string'); } @@ -223,9 +194,9 @@ export class Keychains implements IKeychains { const oldEncryptedPrv = params.keychain.encryptedPrv; try { - const decryptedPrv = await this.bitgo.decryptAsync({ input: oldEncryptedPrv, password: params.oldPassword }); + const decryptedPrv = await this.bitgo.decrypt({ input: oldEncryptedPrv, password: params.oldPassword }); const encryptionVersion = this.getEncryptionVersion(oldEncryptedPrv); - const newEncryptedPrv = await this.bitgo.encryptAsync({ + const newEncryptedPrv = await this.bitgo.encrypt({ input: decryptedPrv, password: params.newPassword, encryptionVersion, @@ -339,7 +310,7 @@ export class Keychains implements IKeychains { _.extend(params, key); if (params.passphrase !== undefined) { _.extend(params, { - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ input: key.prv, password: params.passphrase, encryptionVersion: params.encryptionVersion, @@ -422,17 +393,17 @@ export class Keychains implements IKeychains { throw new Error('failed to get recovery info'); } - const decryptedWalletPassphrase = await this.bitgo.decryptAsync({ + const decryptedWalletPassphrase = await this.bitgo.decrypt({ input: params.encryptedMaterial.encryptedWalletPassphrase, password: recoveryInfo.passcodeEncryptionCode, }); - const decryptedUserKey = await this.bitgo.decryptAsync({ + const decryptedUserKey = await this.bitgo.decrypt({ input: params.encryptedMaterial.encryptedUserKey, password: decryptedWalletPassphrase, }); - const decryptedBackupKey = await this.bitgo.decryptAsync({ + const decryptedBackupKey = await this.bitgo.decrypt({ input: params.encryptedMaterial.encryptedBackupKey, password: decryptedWalletPassphrase, }); @@ -572,7 +543,7 @@ export class Keychains implements IKeychains { const newKeychain = keychains.create(); const originalPasscodeEncryptionCode = generateRandomPassword(5); - const encryptedPrv = await this.bitgo.encryptAsync({ + const encryptedPrv = await this.bitgo.encrypt({ password: walletPassphrase, input: newKeychain.prv, encryptionVersion, @@ -615,7 +586,7 @@ export class Keychains implements IKeychains { throw Error('Expected a public key to be generated'); } pub = keyPub; - encryptedPrv = await this.bitgo.encryptAsync({ + encryptedPrv = await this.bitgo.encrypt({ input: keyPrv, password: params.password, encryptionVersion: params.encryptionVersion, diff --git a/modules/sdk-core/src/bitgo/recovery/initiate.ts b/modules/sdk-core/src/bitgo/recovery/initiate.ts index d415632a82..79c65959d8 100644 --- a/modules/sdk-core/src/bitgo/recovery/initiate.ts +++ b/modules/sdk-core/src/bitgo/recovery/initiate.ts @@ -129,36 +129,17 @@ function addBitgoKeyIfRequired( } /** - * TODO: Deprecate this function in favour of validateKeyAsync when v2 encryption is the default. + * Validate and decrypt a recovery key. Auto-detects v1 (SJCL) and v2 (Argon2id) envelopes. */ -export function validateKey( - bitgo: BitGoBase, - { key, source, passphrase, isUnsignedSweep, isKrsRecovery }: ValidateKeyOptions -): BIP32Interface { - if (!key.startsWith('xprv') && !isUnsignedSweep) { - // Try to decrypt the key - try { - if (source === 'user' || (source === 'backup' && !isKrsRecovery)) { - return bip32.fromBase58(bitgo.decrypt({ password: passphrase, input: key })); - } - } catch (e) { - throw new Error(`Failed to decrypt ${source} key with passcode - try again!`); - } - } - return parseKeyAsXprv(key, source); -} - -/** - * Async version of validateKey with v2 encrypt/decrypt support. - */ -export async function validateKeyAsync( +export async function validateKey( bitgo: BitGoBase, { key, source, passphrase, isUnsignedSweep, isKrsRecovery }: ValidateKeyOptions ): Promise { if (!key.startsWith('xprv') && !isUnsignedSweep) { + // Try to decrypt the key try { if (source === 'user' || (source === 'backup' && !isKrsRecovery)) { - return bip32.fromBase58(await bitgo.decryptAsync({ password: passphrase, input: key })); + return bip32.fromBase58(await bitgo.decrypt({ password: passphrase, input: key })); } } catch (e) { throw new Error(`Failed to decrypt ${source} key with passcode - try again!`); @@ -167,35 +148,7 @@ export async function validateKeyAsync( return parseKeyAsXprv(key, source); } -/** - * TODO: Deprecate this function in favour of getBip32KeysAsync when v2 encryption is the default. - */ -export function getBip32Keys( - bitgo: BitGoBase, - params: InitiateRecoveryOptions | InitiateConsolidationRecoveryOptions, - { requireBitGoXpub }: { requireBitGoXpub: boolean } -): BIP32Interface[] { - const isKrsRecovery = getIsKrsRecovery(params); - const isUnsignedSweep = getIsUnsignedSweep(params); - const validateKeyOpts = { - passphrase: params.walletPassphrase, - isKrsRecovery, - isUnsignedSweep, - }; - const keys = [ - // Box A - validateKey(bitgo, { key: params.userKey, source: 'user', ...validateKeyOpts }), - // Box B - validateKey(bitgo, { key: params.backupKey, source: 'backup', ...validateKeyOpts }), - ]; - - return addBitgoKeyIfRequired(keys, params, requireBitGoXpub); -} - -/** - * Async version of getBip32Keys with v2 encrypt/decrypt support. - */ -export async function getBip32KeysAsync( +export async function getBip32Keys( bitgo: BitGoBase, params: InitiateRecoveryOptions | InitiateConsolidationRecoveryOptions, { requireBitGoXpub }: { requireBitGoXpub: boolean } @@ -209,9 +162,9 @@ export async function getBip32KeysAsync( }; const keys = [ // Box A — sequential: backup only after user completes - await validateKeyAsync(bitgo, { key: params.userKey, source: 'user', ...validateKeyOpts }), + await validateKey(bitgo, { key: params.userKey, source: 'user', ...validateKeyOpts }), // Box B - await validateKeyAsync(bitgo, { key: params.backupKey, source: 'backup', ...validateKeyOpts }), + await validateKey(bitgo, { key: params.backupKey, source: 'backup', ...validateKeyOpts }), ]; return addBitgoKeyIfRequired(keys, params, requireBitGoXpub); diff --git a/modules/sdk-core/src/bitgo/trading/tradingAccount.ts b/modules/sdk-core/src/bitgo/trading/tradingAccount.ts index 8c5942681c..95f5ff09cb 100644 --- a/modules/sdk-core/src/bitgo/trading/tradingAccount.ts +++ b/modules/sdk-core/src/bitgo/trading/tradingAccount.ts @@ -92,7 +92,7 @@ export class TradingAccount implements ITradingAccount { if (!key.encryptedPrv) { throw new Error('Expected encryptedPrv to be present on user keychain.'); } - prv = await this.wallet.bitgo.decryptAsync({ + prv = await this.wallet.bitgo.decrypt({ input: key.encryptedPrv, password: params.walletPassphrase, }); diff --git a/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts b/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts index d46010397e..2b983ccdd7 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts @@ -42,7 +42,6 @@ import { TssSignTxRequestParamsWithPrv, TxRequest, TxRequestVersion, - isV2Envelope, } from './baseTypes'; import { GShare, SignShare } from '../../../account-lib/mpc/tss'; import { RequestTracer } from '../util'; @@ -728,9 +727,10 @@ export default class BaseTssUtils extends MpcUtils implements ITssUtil }> { const bitgoGpgKey = await openpgp.readKey({ armoredKey: bitgoPublicGpgKey }); - const decryptedGpgPrvKey = isV2Envelope(encryptedUserGpgPrvKey) - ? await this.bitgo.decryptAsync({ input: encryptedUserGpgPrvKey, password: walletPassphrase }) - : this.bitgo.decrypt({ input: encryptedUserGpgPrvKey, password: walletPassphrase }); + const decryptedGpgPrvKey = await this.bitgo.decrypt({ + input: encryptedUserGpgPrvKey, + password: walletPassphrase, + }); if (adata) { this.validateAdata(adata, encryptedUserGpgPrvKey, userGpgKeyDomainSeparator); diff --git a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsa.ts b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsa.ts index 4a08647480..5bd3b40546 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsa.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsa.ts @@ -417,7 +417,7 @@ export class EcdsaUtils extends BaseEcdsaUtils { keyType: 'tss' as KeyType, commonKeychain: bitgoKeychain.commonKeychain, prv: prv, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ input: prv, password: passphrase, encryptionVersion, @@ -431,7 +431,7 @@ export class EcdsaUtils extends BaseEcdsaUtils { recipientKeychainParams.webauthnInfo = { otpDeviceId: webauthnInfo.otpDeviceId, prfSalt: webauthnInfo.prfSalt, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ input: prv, password: webauthnInfo.passphrase, encryptionVersion, @@ -518,7 +518,7 @@ export class EcdsaUtils extends BaseEcdsaUtils { userPublicGpgKey: userPublicGpgKey, kShare: userSignShare.kShare, wShare: params.walletPassphrase - ? await this.bitgo.encryptAsync({ + ? await this.bitgo.encrypt({ input: JSON.stringify(userSignShare.wShare), password: params.walletPassphrase, encryptionVersion: params.encryptionVersion, @@ -553,7 +553,7 @@ export class EcdsaUtils extends BaseEcdsaUtils { i: userGammaAndMuShares.muShare.i, }, oShare: params.walletPassphrase - ? await this.bitgo.encryptAsync({ + ? await this.bitgo.encrypt({ input: JSON.stringify(userOmicronAndDeltaShare.oShare), password: params.walletPassphrase, encryptionVersion: params.encryptionVersion, @@ -612,7 +612,7 @@ export class EcdsaUtils extends BaseEcdsaUtils { walletPassphrase: string; encryptionVersion?: EncryptionVersion; }): Promise { - const decryptedWShare = await this.bitgo.decryptAsync({ + const decryptedWShare = await this.bitgo.decrypt({ input: params.encryptedWShare, password: params.walletPassphrase, }); @@ -650,7 +650,7 @@ export class EcdsaUtils extends BaseEcdsaUtils { } catch (err) { hash = undefined; } - const decryptedOShare = await this.bitgo.decryptAsync({ input: encryptedOShare, password: walletPassphrase }); + const decryptedOShare = await this.bitgo.decrypt({ input: encryptedOShare, password: walletPassphrase }); const { i, R, s, y } = await ECDSAMethods.createUserSignatureShare( JSON.parse(decryptedOShare), dShareFromBitgo, diff --git a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts index 329a0c4ac7..defecc850d 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts @@ -405,7 +405,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(reducedPrivateMaterial)))) ); } else { - encryptedPrv = await this.bitgo.encryptAsync({ + encryptedPrv = await this.bitgo.encrypt({ input: privateMaterialBase64, password: passphrase, encryptionVersion, @@ -414,7 +414,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { // scalar s_i) with the wallet passphrase. The result is stored as reducedEncryptedPrv // on the key card QR code and represents a second copy of private key material // beyond the server-stored encryptedPrv. - reducedEncryptedPrv = await this.bitgo.encryptAsync({ + reducedEncryptedPrv = await this.bitgo.encrypt({ // Buffer.toString('base64') can not be used here as it does not work on the browser. // The browser deals with a Buffer as Uint8Array, therefore in the browser .toString('base64') just creates a comma seperated string of the array values. input: btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(reducedPrivateMaterial)))), @@ -445,7 +445,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { recipientKeychainParams.webauthnInfo = { otpDeviceId: webauthnInfo.otpDeviceId, prfSalt: webauthnInfo.prfSalt, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ input: privateMaterialBase64, password: webauthnInfo.passphrase, encryptionVersion, @@ -1223,13 +1223,13 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { } } - const encryptedRound1Session = await this.bitgo.encryptAsync({ + const encryptedRound1Session = await this.bitgo.encrypt({ input: sessionData, password: walletPassphrase, adata: `${EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND1_STATE}:${adata}`, encryptionVersion: 1, }); - const encryptedUserGpgPrvKey = await this.bitgo.encryptAsync({ + const encryptedUserGpgPrvKey = await this.bitgo.encrypt({ input: userGpgKey.privateKey, password: walletPassphrase, adata: `${EcdsaMPCv2Utils.DKLS23_SIGNING_USER_GPG_KEY}:${adata}`, @@ -1284,12 +1284,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { bitgoGpgKey ); - let round1Session: string; - if (useV2) { - round1Session = await this.bitgo.decryptAsync({ input: encryptedRound1Session, password: walletPassphrase }); - } else { - round1Session = this.bitgo.decrypt({ input: encryptedRound1Session, password: walletPassphrase }); - } + const round1Session = await this.bitgo.decrypt({ input: encryptedRound1Session, password: walletPassphrase }); this.validateAdata(adata, encryptedRound1Session, EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND1_STATE); const userKeyShare = Buffer.from(prv, 'base64'); @@ -1326,7 +1321,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { } } - const encryptedRound2Session = await this.bitgo.encryptAsync({ + const encryptedRound2Session = await this.bitgo.encrypt({ input: sessionData, password: walletPassphrase, adata: `${EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND2_STATE}:${adata}`, @@ -1356,8 +1351,6 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { const { hashBuffer, derivationPath } = this.getHashStringAndDerivationPath(txRequest); const adata = `${hashBuffer.toString('hex')}:${derivationPath}`; - const useV2 = isV2Envelope(encryptedRound2Session); - const { bitgoGpgKey, userGpgPrvKey } = await this.getBitgoAndUserGpgKeys( bitgoPublicGpgKey, encryptedUserGpgPrvKey, @@ -1389,12 +1382,7 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { broadcastMessages: [], }); - let round2Session: string; - if (useV2) { - round2Session = await this.bitgo.decryptAsync({ input: encryptedRound2Session, password: walletPassphrase }); - } else { - round2Session = this.bitgo.decrypt({ input: encryptedRound2Session, password: walletPassphrase }); - } + const round2Session = await this.bitgo.decrypt({ input: encryptedRound2Session, password: walletPassphrase }); this.validateAdata(adata, encryptedRound2Session, EcdsaMPCv2Utils.DKLS23_SIGNING_ROUND2_STATE); const userKeyShare = Buffer.from(prv, 'base64'); @@ -1414,37 +1402,24 @@ export class EcdsaMPCv2Utils extends BaseEcdsaUtils { } /** - * TODO: deprecate this function in favor of isGG18SigningMaterialAsync once v2 encryption is default * Checks if the given key share, when decrypted, contains valid GG18 signing material. + * When a `bitgo` instance is provided, decryption auto-detects v1 (SJCL) and v2 (Argon2id) + * envelopes; otherwise it falls back to a direct v1 (SJCL) decrypt. * * @param {string} keyShare - The encrypted key share string. * @param {string|undefined} walletPassphrase - The passphrase used to decrypt the key share - * @returns {boolean} - Returns `true` if the decrypted data contains valid signing material, otherwise `false`. - */ -export function isGG18SigningMaterial(keyShare: string, walletPassphrase: string | undefined): boolean { - const prv = sjcl.decrypt(walletPassphrase, keyShare); - try { - const signingMaterial = JSON.parse(prv); - return ( - signingMaterial.pShare && - signingMaterial.bitgoNShare && - (signingMaterial.userNShare || signingMaterial.backupNShare) - ); - } catch (error) { - return false; - } -} - -/** - * Async version of {@link isGG18SigningMaterial} with v1/v2 auto-detect decrypt via `bitgo.decryptAsync`. + * @param {BitGoBase} [bitgo] - Optional BitGo instance for v1/v2 auto-detect decrypt + * @returns {Promise} - Resolves `true` if the decrypted data contains valid signing material, otherwise `false`. */ -export async function isGG18SigningMaterialAsync( +export async function isGG18SigningMaterial( keyShare: string, walletPassphrase: string | undefined, - bitgo: BitGoBase + bitgo?: BitGoBase ): Promise { try { - const prv = await bitgo.decryptAsync({ password: walletPassphrase, input: keyShare }); + const prv = bitgo + ? await bitgo.decrypt({ password: walletPassphrase, input: keyShare }) + : sjcl.decrypt(walletPassphrase, keyShare); const signingMaterial = JSON.parse(prv); return ( signingMaterial.pShare && @@ -1475,12 +1450,12 @@ export async function getMpcV2RecoveryKeyShares( commonKeyChain: string; }> { if (bitgo) { - if (await isGG18SigningMaterialAsync(encryptedUserKey, walletPassphrase, bitgo)) { + if (await isGG18SigningMaterial(encryptedUserKey, walletPassphrase, bitgo)) { return getMpcV2RecoveryKeySharesFromGG18(encryptedUserKey, encryptedBackupKey, walletPassphrase, bitgo); } return getMpcV2RecoveryKeySharesFromReducedKey(encryptedUserKey, encryptedBackupKey, walletPassphrase, bitgo); } - if (isGG18SigningMaterial(encryptedUserKey, walletPassphrase)) { + if (await isGG18SigningMaterial(encryptedUserKey, walletPassphrase)) { return getMpcV2RecoveryKeySharesFromGG18(encryptedUserKey, encryptedBackupKey, walletPassphrase); } return getMpcV2RecoveryKeySharesFromReducedKey(encryptedUserKey, encryptedBackupKey, walletPassphrase); @@ -1595,11 +1570,11 @@ async function getMpcV2RecoveryKeySharesFromReducedKey( let bakcupCompressedPrv: Buffer; if (bitgo) { userCompressedPrv = Buffer.from( - await bitgo.decryptAsync({ password: walletPassphrase, input: encryptedMPCv2UserKey }), + await bitgo.decrypt({ password: walletPassphrase, input: encryptedMPCv2UserKey }), 'base64' ); bakcupCompressedPrv = Buffer.from( - await bitgo.decryptAsync({ password: walletPassphrase, input: encryptedMPCv2BackupKey }), + await bitgo.decrypt({ password: walletPassphrase, input: encryptedMPCv2BackupKey }), 'base64' ); } else { @@ -1649,8 +1624,8 @@ async function getKeyCombinedFromTssKeyShares( let userPrv; try { if (bitgo) { - backupPrv = await bitgo.decryptAsync({ password: walletPassphrase, input: encryptedGG18BackupKey }); - userPrv = await bitgo.decryptAsync({ password: walletPassphrase, input: encryptedGG18UserKey }); + backupPrv = await bitgo.decrypt({ password: walletPassphrase, input: encryptedGG18BackupKey }); + userPrv = await bitgo.decrypt({ password: walletPassphrase, input: encryptedGG18UserKey }); } else { backupPrv = sjcl.decrypt(walletPassphrase, encryptedGG18BackupKey); userPrv = sjcl.decrypt(walletPassphrase, encryptedGG18UserKey); diff --git a/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsa.ts b/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsa.ts index 5cad5b6b82..dfee017835 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsa.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsa.ts @@ -195,7 +195,7 @@ export class EddsaUtils extends baseTSSUtils { if (encryptionSession) { userKeychainParams.encryptedPrv = await encryptionSession.encrypt(JSON.stringify(userSigningMaterial)); } else { - userKeychainParams.encryptedPrv = await this.bitgo.encryptAsync({ + userKeychainParams.encryptedPrv = await this.bitgo.encrypt({ input: JSON.stringify(userSigningMaterial), password: passphrase, encryptionVersion, @@ -208,7 +208,7 @@ export class EddsaUtils extends baseTSSUtils { userKeychainParams.webauthnInfo = { otpDeviceId: webauthnInfo.otpDeviceId, prfSalt: webauthnInfo.prfSalt, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ input: JSON.stringify(userSigningMaterial), password: webauthnInfo.passphrase, encryptionVersion, @@ -301,7 +301,7 @@ export class EddsaUtils extends baseTSSUtils { if (encryptionSession) { params.encryptedPrv = await encryptionSession.encrypt(prv); } else { - params.encryptedPrv = await this.bitgo.encryptAsync({ input: prv, password: passphrase, encryptionVersion }); + params.encryptedPrv = await this.bitgo.encrypt({ input: prv, password: passphrase, encryptionVersion }); } } @@ -498,7 +498,7 @@ export class EddsaUtils extends baseTSSUtils { session.destroy(); } } else { - encryptedRShare = await this.bitgo.encryptAsync({ + encryptedRShare = await this.bitgo.encrypt({ input: stringifiedRShare, password: params.walletPassphrase, encryptionVersion: 1, @@ -516,18 +516,10 @@ export class EddsaUtils extends baseTSSUtils { }): Promise<{ rShare: SignShare }> { const { walletPassphrase, encryptedUserToBitgoRShare } = params; - let decryptedRShare: string; - if (isV2Envelope(encryptedUserToBitgoRShare.share)) { - decryptedRShare = await this.bitgo.decryptAsync({ - input: encryptedUserToBitgoRShare.share, - password: walletPassphrase, - }); - } else { - decryptedRShare = this.bitgo.decrypt({ - input: encryptedUserToBitgoRShare.share, - password: walletPassphrase, - }); - } + const decryptedRShare = await this.bitgo.decrypt({ + input: encryptedUserToBitgoRShare.share, + password: walletPassphrase, + }); const rShare = JSON.parse(decryptedRShare); assert(rShare.xShare, 'Unable to find xShare in decryptedRShare'); assert(rShare.rShares, 'Unable to find rShares in decryptedRShare'); diff --git a/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts b/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts index 76f5a34557..3a46d77307 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts @@ -237,7 +237,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { assert(reducedPrivateMaterial, `Reduced private material is required for ${source} keychain`); assert(passphrase, `Passphrase is required for ${source} keychain`); privateMaterialBase64 = privateMaterial.toString('base64'); - encryptedPrv = await this.bitgo.encryptAsync({ + encryptedPrv = await this.bitgo.encrypt({ input: privateMaterialBase64, password: passphrase, encryptionVersion, @@ -246,7 +246,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { // key) with the wallet passphrase. The result is stored as reducedEncryptedPrv // on the key card QR code and represents a second copy of key material // beyond the server-stored encryptedPrv. - reducedEncryptedPrv = await this.bitgo.encryptAsync({ + reducedEncryptedPrv = await this.bitgo.encrypt({ // Buffer.toString('base64') can not be used here as it does not work on the browser. // The browser deals with a Buffer as Uint8Array, therefore in the browser .toString('base64') just creates a comma separated string of the array values. input: btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(reducedPrivateMaterial)))), @@ -276,7 +276,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { keychainParams.webauthnInfo = { otpDeviceId: webauthnInfo.otpDeviceId, prfSalt: webauthnInfo.prfSalt, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ input: privateMaterialBase64, password: webauthnInfo.passphrase, encryptionVersion, @@ -608,13 +608,13 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { } } - const encryptedRound1Session = await this.bitgo.encryptAsync({ + const encryptedRound1Session = await this.bitgo.encrypt({ input: sessionPayload, password: walletPassphrase, adata: `${EddsaMPCv2Utils.MPS_DSG_SIGNING_ROUND1_STATE}:${adata}`, encryptionVersion: 1, }); - const encryptedUserGpgPrvKey = await this.bitgo.encryptAsync({ + const encryptedUserGpgPrvKey = await this.bitgo.encrypt({ input: userGpgKey.privateKey, password: walletPassphrase, adata: `${EddsaMPCv2Utils.MPS_DSG_SIGNING_USER_GPG_KEY}:${adata}`, @@ -674,18 +674,10 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { this.validateAdata(adata, encryptedRound1Session, EddsaMPCv2Utils.MPS_DSG_SIGNING_ROUND1_STATE); - let decryptedRound1Session: string; - if (useV2) { - decryptedRound1Session = await this.bitgo.decryptAsync({ - input: encryptedRound1Session, - password: walletPassphrase, - }); - } else { - decryptedRound1Session = this.bitgo.decrypt({ - input: encryptedRound1Session, - password: walletPassphrase, - }); - } + const decryptedRound1Session = await this.bitgo.decrypt({ + input: encryptedRound1Session, + password: walletPassphrase, + }); const { dsgSession, userMsgPayload } = JSON.parse(decryptedRound1Session) as { dsgSession: string; @@ -721,7 +713,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { } } - const encryptedRound2Session = await this.bitgo.encryptAsync({ + const encryptedRound2Session = await this.bitgo.encrypt({ input: sessionPayload, password: walletPassphrase, adata: `${EddsaMPCv2Utils.MPS_DSG_SIGNING_ROUND2_STATE}:${adata}`, @@ -750,8 +742,6 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { ); const adata = `${signableHex}:${derivationPath}`; - const useV2 = isV2Envelope(encryptedRound2Session); - const { bitgoGpgKey, userGpgPrvKey } = await this.getBitgoAndUserGpgKeys( bitgoPublicGpgKey, encryptedUserGpgPrvKey, @@ -781,18 +771,10 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { this.validateAdata(adata, encryptedRound2Session, EddsaMPCv2Utils.MPS_DSG_SIGNING_ROUND2_STATE); - let decryptedRound2Session: string; - if (useV2) { - decryptedRound2Session = await this.bitgo.decryptAsync({ - input: encryptedRound2Session, - password: walletPassphrase, - }); - } else { - decryptedRound2Session = this.bitgo.decrypt({ - input: encryptedRound2Session, - password: walletPassphrase, - }); - } + const decryptedRound2Session = await this.bitgo.decrypt({ + input: encryptedRound2Session, + password: walletPassphrase, + }); const { dsgSession, userMsgPayload } = JSON.parse(decryptedRound2Session) as { dsgSession: string; @@ -924,7 +906,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { * @param encryptedKeyShare encrypted user or backup keycard * @param walletPassphrase passphrase used to encrypt the keycard * @param bitgo optional BitGoBase instance; when provided, decrypts via - * bitgo.decryptAsync (supports both v1 SJCL and v2 Argon2id envelopes); + * bitgo.decrypt (supports both v1 SJCL and v2 Argon2id envelopes); * when absent, falls back to sjcl.decrypt (v1 only) */ export async function isEddsaMpcV1SigningMaterial( @@ -933,7 +915,7 @@ export async function isEddsaMpcV1SigningMaterial( bitgo?: BitGoBase ): Promise { const prv = bitgo - ? await bitgo.decryptAsync({ input: encryptedKeyShare, password: walletPassphrase }) + ? await bitgo.decrypt({ input: encryptedKeyShare, password: walletPassphrase }) : sjcl.decrypt(walletPassphrase, encryptedKeyShare); try { @@ -969,7 +951,7 @@ export async function getEddsaMpcV2RecoveryKeySharesFromReducedKey( ): Promise { const decodeKey = async (encryptedKey: string): Promise => { const decrypted = bitgo - ? await bitgo.decryptAsync({ input: encryptedKey, password: walletPassphrase }) + ? await bitgo.decrypt({ input: encryptedKey, password: walletPassphrase }) : sjcl.decrypt(walletPassphrase, encryptedKey); let reduced: MPSTypes.EddsaReducedKeyShare; try { diff --git a/modules/sdk-core/src/bitgo/wallet/iWallet.ts b/modules/sdk-core/src/bitgo/wallet/iWallet.ts index 3681c535f3..a9663e6bbc 100644 --- a/modules/sdk-core/src/bitgo/wallet/iWallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/iWallet.ts @@ -1170,8 +1170,7 @@ export interface IWallet { removeUser(params?: RemoveUserOptions): Promise; prebuildTransaction(params?: PrebuildTransactionOptions): Promise; signTransaction(params?: WalletSignTransactionOptions): Promise; - getUserPrv(params?: GetUserPrvOptions): string; - getUserPrvAsync(params?: GetUserPrvOptions): Promise; + getUserPrv(params?: GetUserPrvOptions): Promise; prebuildAndSignTransaction(params?: PrebuildAndSignTransactionOptions): Promise; signAndSendTxRequest(params?: SignAndSendTxRequestOptions): Promise; accelerateTransaction(params?: AccelerateTransactionOptions): Promise; @@ -1195,8 +1194,7 @@ export interface IWallet { toStakingWallet(): IStakingWallet; toGoStakingWallet(): IGoStakingWallet; toAddressBook(): IAddressBook; - downloadKeycard(params?: DownloadKeycardOptions): void; - downloadKeycardAsync(params?: DownloadKeycardOptions): Promise; + downloadKeycard(params?: DownloadKeycardOptions): Promise; buildAccountConsolidations(params?: BuildConsolidationTransactionOptions): Promise; sendAccountConsolidation(params?: PrebuildAndSignTransactionOptions): Promise; sendAccountConsolidations(params?: BuildConsolidationTransactionOptions): Promise; diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index 870d8f2a9e..8cb9e914e3 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -31,15 +31,9 @@ import { NeedUserSignupError, } from '../errors'; import { SubmitTransactionResponse } from '../inscriptionBuilder'; -import { drawKeycard, drawKeycardAsync } from '../internal'; +import { drawKeycard } from '../internal'; import * as internal from '../internal/internal'; -import { - decryptKeychainPrivateKey, - decryptKeychainPrivateKeyAsync, - Keychain, - KeychainWithEncryptedPrv, - KeyIndices, -} from '../keychain'; +import { decryptKeychainPrivateKey, Keychain, KeychainWithEncryptedPrv, KeyIndices } from '../keychain'; import { CreateLightningInvoiceParams, LightningInvoiceResponse } from '../../lightning'; import { getLightningAuthKey } from '../lightning/lightningWalletUtil'; import { IPendingApproval, PendingApproval, PendingApprovals } from '../pendingApproval'; @@ -1757,7 +1751,7 @@ export class Wallet implements IWallet { if (!params.walletPassphrase) { throw new Error('wallet passphrase was not provided'); } - const userPrv = await decryptKeychainPrivateKeyAsync(this.bitgo, userKeychain, params.walletPassphrase); + const userPrv = await decryptKeychainPrivateKey(this.bitgo, userKeychain, params.walletPassphrase); if (!userPrv) { throw new Error('error decrypting wallet private key'); } @@ -1854,7 +1848,7 @@ export class Wallet implements IWallet { const needsKeychain = shareOption.permissions && shareOption.permissions.includes('spend'); if (needsKeychain && decryptedKeychain) { - const sharedKeychain = await this.encryptPrvForUserAsync( + const sharedKeychain = await this.encryptPrvForUser( decryptedKeychain.prv, decryptedKeychain.pub, shareOption.pubKey, @@ -1938,7 +1932,7 @@ export class Wallet implements IWallet { throw new Error('Missing walletPassphrase argument'); } - const prv = await decryptKeychainPrivateKeyAsync(this.bitgo, keychain, walletPassphrase); + const prv = await decryptKeychainPrivateKey(this.bitgo, keychain, walletPassphrase); if (!prv) { throw new IncorrectPasswordError('Password shared is incorrect for this wallet'); } @@ -1960,7 +1954,6 @@ export class Wallet implements IWallet { } /** - * TODO: Deprecate this function in favour of encryptPrvForUserAsync when v2 encryption is the default. * Encrypts a decrypted private key for sharing with a specific user. * This is the pure encryption step - no API calls, no decryption. * @@ -1968,34 +1961,10 @@ export class Wallet implements IWallet { * @param pub - The wallet's public key * @param userPubkey - The recipient user's public key * @param path - The key path + * @param encryptionVersion - Optional encryption version (defaults to v2) * @returns The encrypted keychain for the recipient with all required fields */ - encryptPrvForUser(decryptedPrv: string, pub: string, userPubkey: string, path: string): BulkWalletShareKeychain { - const eckey = makeRandomKey(); - const secret = getSharedSecret(eckey, Buffer.from(userPubkey, 'hex')).toString('hex'); - const newEncryptedPrv = this.bitgo.encrypt({ password: secret, input: decryptedPrv }); - - const keychain: BulkWalletShareKeychain = { - pub, - encryptedPrv: newEncryptedPrv, - fromPubKey: eckey.publicKey.toString('hex'), - toPubKey: userPubkey, - path: path, - }; - - assert(keychain.pub, 'pub must be defined for sharing'); - assert(keychain.encryptedPrv, 'encryptedPrv must be defined for sharing'); - assert(keychain.fromPubKey, 'fromPubKey must be defined for sharing'); - assert(keychain.toPubKey, 'toPubKey must be defined for sharing'); - assert(keychain.path, 'path must be defined for sharing'); - - return keychain; - } - - /** - * Async version of encryptPrvForUser with v2 encrypt/decrypt support. - */ - async encryptPrvForUserAsync( + async encryptPrvForUser( decryptedPrv: string, pub: string, userPubkey: string, @@ -2004,7 +1973,7 @@ export class Wallet implements IWallet { ): Promise { const eckey = makeRandomKey(); const secret = getSharedSecret(eckey, Buffer.from(userPubkey, 'hex')).toString('hex'); - const newEncryptedPrv = await this.bitgo.encryptAsync({ password: secret, input: decryptedPrv, encryptionVersion }); + const newEncryptedPrv = await this.bitgo.encrypt({ password: secret, input: decryptedPrv, encryptionVersion }); const keychain: BulkWalletShareKeychain = { pub, @@ -2043,7 +2012,7 @@ export class Wallet implements IWallet { if (!decryptedKeychain) { return {}; } - return await this.encryptPrvForUserAsync( + return await this.encryptPrvForUser( decryptedKeychain.prv, decryptedKeychain.pub, pubkey, @@ -2373,7 +2342,7 @@ export class Wallet implements IWallet { if (this.multisigType() === 'tss') { return this.signTransactionTss({ ...presign, - prv: await this.getUserPrvAsync(presign as GetUserPrvOptions), + prv: await this.getUserPrv(presign as GetUserPrvOptions), apiVersion, }); } @@ -2412,7 +2381,7 @@ export class Wallet implements IWallet { if (this.baseCoin.getFamily() === 'ofc') { const userKeySigningRequired = this.toTradingAccount().userKeySigningRequired; - const prv = userKeySigningRequired ? await this.getUserPrvAsync(presign as GetUserPrvOptions) : undefined; + const prv = userKeySigningRequired ? await this.getUserPrv(presign as GetUserPrvOptions) : undefined; return this.baseCoin.signTransaction({ ...signTransactionParams, prv, @@ -2422,7 +2391,7 @@ export class Wallet implements IWallet { return this.baseCoin.signTransaction({ ...signTransactionParams, - prv: await this.getUserPrvAsync(presign as GetUserPrvOptions), + prv: await this.getUserPrv(presign as GetUserPrvOptions), wallet: this, }); } @@ -2457,7 +2426,7 @@ export class Wallet implements IWallet { ...params, walletData: this._wallet, tssUtils: this.tssUtils, - prv: await this.getUserPrvAsync(userPrvOptions), + prv: await this.getUserPrv(userPrvOptions), keychain: keychains[0], backupKeychain: keychains.length > 1 ? keychains[1] : null, bitgoKeychain: keychains.length > 2 ? keychains[2] : null, @@ -2496,7 +2465,7 @@ export class Wallet implements IWallet { ...params, walletData: this._wallet, tssUtils: this.tssUtils, - prv: await this.getUserPrvAsync(userPrvOptions), + prv: await this.getUserPrv(userPrvOptions), keychain: keychains[0], backupKeychain: keychains.length > 1 ? keychains[1] : null, bitgoKeychain: keychains.length > 2 ? keychains[2] : null, @@ -2549,11 +2518,12 @@ export class Wallet implements IWallet { } /** - * Get the user private key from either a derivation or an encrypted keychain (sync, v1 only). + * Get the user private key from either a derivation or an encrypted keychain. + * Supports both v1 (SJCL) and v2 (Argon2id) envelopes. * @param [params.keychain / params.key] (object) or params.prv (string) * @param params.walletPassphrase (string) */ - getUserPrv(params: GetUserPrvOptions = {}): string { + async getUserPrv(params: GetUserPrvOptions = {}): Promise { const userKeychain = params.keychain || params.key; let userPrv = params.prv; if (userPrv && typeof userPrv !== 'string') { @@ -2586,53 +2556,7 @@ export class Wallet implements IWallet { if (!params.walletPassphrase) { throw new Error('walletPassphrase property missing'); } - userPrv = decryptKeychainPrivateKey(this.bitgo, userKeychain, params.walletPassphrase); - if (!userPrv) { - throw new Error('failed to decrypt user keychain'); - } - } - return userPrv; - } - - /** - * Async version of getUserPrv that supports both v1 (SJCL) and v2 (Argon2id) envelopes. - * @param [params.keychain / params.key] (object) or params.prv (string) - * @param params.walletPassphrase (string) - */ - async getUserPrvAsync(params: GetUserPrvOptions = {}): Promise { - const userKeychain = params.keychain || params.key; - let userPrv = params.prv; - if (userPrv && typeof userPrv !== 'string') { - throw new Error('prv must be a string'); - } - - if ( - params.coldDerivationSeed === undefined && - params.keychain !== undefined && - params.keychain.derivedFromParentWithSeed !== undefined && - this.multisigType() === 'onchain' - ) { - params.coldDerivationSeed = params.keychain.derivedFromParentWithSeed; - } - - if (userPrv && params.coldDerivationSeed) { - const derivation = this.baseCoin.deriveKeyWithSeed({ - key: userPrv, - seed: params.coldDerivationSeed, - }); - userPrv = derivation.key; - } else if (!userPrv) { - if (!userKeychain || typeof userKeychain !== 'object') { - throw new Error('keychain must be an object'); - } - const userEncryptedPrv = userKeychain.encryptedPrv; - if (!userEncryptedPrv) { - throw new Error('keychain does not have property encryptedPrv'); - } - if (!params.walletPassphrase) { - throw new Error('walletPassphrase property missing'); - } - userPrv = await decryptKeychainPrivateKeyAsync(this.bitgo, userKeychain, params.walletPassphrase); + userPrv = await decryptKeychainPrivateKey(this.bitgo, userKeychain, params.walletPassphrase); if (!userPrv) { throw new Error('failed to decrypt user keychain'); } @@ -3400,8 +3324,8 @@ export class Wallet implements IWallet { } /** - * TODO: Deprecate this function in favour of downloadKeycardAsync when v2 encryption is the default. - * Creates and downloads PDF keycard for wallet (requires response from wallets.generateWallet) + * Creates and downloads PDF keycard for wallet (requires response from wallets.generateWallet). + * Defaults to v2 encryption for Box D; pass `encryptionVersion: 1` for legacy v1. * * Note: this is example code and is not the version used on bitgo.com * @@ -3418,49 +3342,7 @@ export class Wallet implements IWallet { * * backupKeyID - the Key ID used for deriving a cold wallet's backup key * @returns {*} */ - downloadKeycard(params: DownloadKeycardOptions = {}): void { - const { - jsPDF, - QRCode, - userKeychain, - backupKeychain, - bitgoKeychain, - passphrase, - passcodeEncryptionCode, - walletKeyID, - backupKeyID, - activationCode, - } = validateDownloadKeycardParams(params); - - const coinShortName = this.baseCoin.type; - const coinName = this.baseCoin.getFullName(); - const walletLabel = this._wallet.label; - - const doc = drawKeycard({ - jsPDF, - QRCode, - encrypt: this.bitgo.encrypt, - coinShortName, - coinName, - activationCode, - walletLabel, - passphrase, - passcodeEncryptionCode, - userKeychain, - backupKeychain, - bitgoKeychain, - walletKeyID, - backupKeyID, - }); - - // Save the PDF on the user's browser - doc.save(`BitGo Keycard for ${walletLabel}.pdf`); - } - - /** - * Async version of downloadKeycard with v2 encrypt/decrypt support. - */ - async downloadKeycardAsync(params: DownloadKeycardOptions = {}): Promise { + async downloadKeycard(params: DownloadKeycardOptions = {}): Promise { const { jsPDF, QRCode, @@ -3479,10 +3361,10 @@ export class Wallet implements IWallet { const coinName = this.baseCoin.getFullName(); const walletLabel = this._wallet.label; - const doc = await drawKeycardAsync({ + const doc = await drawKeycard({ jsPDF, QRCode, - encrypt: (p) => this.bitgo.encryptAsync(p), + encrypt: (p) => this.bitgo.encrypt(p), encryptionVersion, coinShortName, coinName, @@ -5223,7 +5105,7 @@ export class Wallet implements IWallet { // we ignore this check with if customSigningFunction is provided // which means that the user is handling the signing in external signing mode if (!customSigningFunction && keychains?.[0]?.encryptedPrv && walletPassphrase) { - if (!(await decryptKeychainPrivateKeyAsync(this.bitgo, keychains[0], walletPassphrase))) { + if (!(await decryptKeychainPrivateKey(this.bitgo, keychains[0], walletPassphrase))) { const error: Error & { code?: string } = new Error( `unable to decrypt keychain with the given wallet passphrase` ); diff --git a/modules/sdk-core/src/bitgo/wallet/wallets.ts b/modules/sdk-core/src/bitgo/wallet/wallets.ts index b6febbc929..886a59d394 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallets.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallets.ts @@ -180,7 +180,7 @@ export class Wallets implements IWallets { const keychain = this.baseCoin.keychains().create(); const keychainParams: AddKeychainOptions = { pub: keychain.pub, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ password: passphrase, input: keychain.prv, encryptionVersion, @@ -240,7 +240,7 @@ export class Wallets implements IWallets { const keychainParams: AddKeychainOptions = { pub: keychain.pub, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ password: passphrase, input: keychain.prv, encryptionVersion, @@ -328,7 +328,7 @@ export class Wallets implements IWallets { ); const walletData = await this.generateLightningWallet(options); - walletData.encryptedWalletPassphrase = await this.bitgo.encryptAsync({ + walletData.encryptedWalletPassphrase = await this.bitgo.encrypt({ input: options.passphrase, password: options.passcodeEncryptionCode, encryptionVersion: options.encryptionVersion, @@ -348,7 +348,7 @@ export class Wallets implements IWallets { ); const walletData = await this.generateGoAccountWallet(options); - walletData.encryptedWalletPassphrase = await this.bitgo.encryptAsync({ + walletData.encryptedWalletPassphrase = await this.bitgo.encrypt({ input: options.passphrase, password: options.passcodeEncryptionCode, encryptionVersion: options.encryptionVersion, @@ -457,7 +457,7 @@ export class Wallets implements IWallets { encryptionVersion: params.encryptionVersion, }); if (params.passcodeEncryptionCode) { - walletData.encryptedWalletPassphrase = await this.bitgo.encryptAsync({ + walletData.encryptedWalletPassphrase = await this.bitgo.encrypt({ input: passphrase, password: params.passcodeEncryptionCode, encryptionVersion: params.encryptionVersion, @@ -593,7 +593,7 @@ export class Wallets implements IWallets { } // Create the user key. userKeychain = this.baseCoin.keychains().create(); - userKeychain.encryptedPrv = await this.bitgo.encryptAsync({ + userKeychain.encryptedPrv = await this.bitgo.encrypt({ password: passphrase, input: userKeychain.prv, encryptionVersion: params.encryptionVersion, @@ -613,7 +613,7 @@ export class Wallets implements IWallets { otpDeviceId: params.webauthnInfo.otpDeviceId, prfSalt: params.webauthnInfo.prfSalt, enterpriseId: params.enterprise, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ password: params.webauthnInfo.passphrase, input: userKeychain.prv, encryptionVersion: params.encryptionVersion, @@ -713,7 +713,7 @@ export class Wallets implements IWallets { } if (canEncrypt && params.passcodeEncryptionCode) { - result.encryptedWalletPassphrase = await this.bitgo.encryptAsync({ + result.encryptedWalletPassphrase = await this.bitgo.encrypt({ input: passphrase, password: params.passcodeEncryptionCode, encryptionVersion: params.encryptionVersion, @@ -1117,7 +1117,7 @@ export class Wallets implements IWallets { } const walletKeychain = this.baseCoin.keychains().create(); - const encryptedPrv = await this.bitgo.encryptAsync({ + const encryptedPrv = await this.bitgo.encrypt({ password: params.newWalletPassphrase || params.userPassword, input: walletKeychain.prv, encryptionVersion: params.encryptionVersion, @@ -1159,7 +1159,7 @@ export class Wallets implements IWallets { }; const payloadString = JSON.stringify(payload); - const privateKey = await this.bitgo.decryptAsync({ + const privateKey = await this.bitgo.decrypt({ password: params.userPassword, input: walletKeychain.encryptedPrv, }); @@ -1203,7 +1203,7 @@ export class Wallets implements IWallets { } // Now we have the sharing keychain, we can work out the secret used for sharing the wallet with us - sharingKeychain.prv = await this.bitgo.decryptAsync({ + sharingKeychain.prv = await this.bitgo.decrypt({ password: params.userPassword, input: sharingKeychain.encryptedXprv, }); @@ -1214,14 +1214,14 @@ export class Wallets implements IWallets { ).toString('hex'); // Yes! We got the secret successfully here, now decrypt the shared wallet prv - const decryptedSharedWalletPrv = await this.bitgo.decryptAsync({ + const decryptedSharedWalletPrv = await this.bitgo.decrypt({ password: secret, input: walletShare.keychain.encryptedPrv, }); // We will now re-encrypt the wallet with our own password const newWalletPassphrase = params.newWalletPassphrase || params.userPassword; - encryptedPrv = await this.bitgo.encryptAsync({ + encryptedPrv = await this.bitgo.encrypt({ password: newWalletPassphrase, input: decryptedSharedWalletPrv, encryptionVersion: params.encryptionVersion, @@ -1280,7 +1280,7 @@ export class Wallets implements IWallets { throw new Error('encryptedXprv was not found on sharing keychain'); } - sharingKeychain.prv = await this.bitgo.decryptAsync({ + sharingKeychain.prv = await this.bitgo.decrypt({ password: params.userLoginPassword, input: sharingKeychain.encryptedXprv, }); @@ -1295,7 +1295,7 @@ export class Wallets implements IWallets { throw new Error('userLoginPassword param must be provided to generate user keychain'); } const walletKeychain = this.baseCoin.keychains().create(); - const encryptedPrv = await this.bitgo.encryptAsync({ + const encryptedPrv = await this.bitgo.encrypt({ password: newWalletPassphrase, input: walletKeychain.prv, encryptionVersion: params.encryptionVersion, @@ -1318,11 +1318,11 @@ export class Wallets implements IWallets { Buffer.from(walletShare.keychain.fromPubKey, 'hex') ).toString('hex'); - const decryptedSharedWalletPrv = await this.bitgo.decryptAsync({ + const decryptedSharedWalletPrv = await this.bitgo.decrypt({ password: secret, input: walletShare.keychain.encryptedPrv, }); - const newEncryptedPrv = await this.bitgo.encryptAsync({ + const newEncryptedPrv = await this.bitgo.encrypt({ password: newWalletPassphrase, input: decryptedSharedWalletPrv, encryptionVersion: params.encryptionVersion, @@ -1335,7 +1335,7 @@ export class Wallets implements IWallets { entry.webauthnInfo = { otpDeviceId: webauthnInfo.otpDeviceId, prfSalt: webauthnInfo.prfSalt, - encryptedPrv: await this.bitgo.encryptAsync({ + encryptedPrv: await this.bitgo.encrypt({ password: webauthnInfo.passphrase, input: decryptedSharedWalletPrv, encryptionVersion: params.encryptionVersion, @@ -1451,7 +1451,7 @@ export class Wallets implements IWallets { if (!sharingKeychain.encryptedXprv) { throw new Error('encryptedXprv was not found on sharing keychain'); } - sharingKeychainPrv = await this.bitgo.decryptAsync({ + sharingKeychainPrv = await this.bitgo.decrypt({ password: userLoginPassword, input: sharingKeychain.encryptedXprv, }); @@ -1573,7 +1573,7 @@ export class Wallets implements IWallets { timestamp: new Date().toISOString(), }); - const prv = await this.bitgo.decryptAsync({ + const prv = await this.bitgo.decrypt({ password: userLoginPassword, input: walletKeychain.encryptedPrv, }); @@ -1598,7 +1598,7 @@ export class Wallets implements IWallets { } const walletKeychain = this.baseCoin.keychains().create(); - const encryptedPrv = await this.bitgo.encryptAsync({ + const encryptedPrv = await this.bitgo.encrypt({ password: newWalletPassphrase || userLoginPassword, input: walletKeychain.prv, encryptionVersion, @@ -1638,13 +1638,13 @@ export class Wallets implements IWallets { 'hex' ); - const decryptedPrv = await this.bitgo.decryptAsync({ + const decryptedPrv = await this.bitgo.decrypt({ password: sharedSecret, input: walletShare.keychain.encryptedPrv, }); // We will now re-encrypt the wallet with our own password - const encryptedPrv = await this.bitgo.encryptAsync({ + const encryptedPrv = await this.bitgo.encrypt({ password: newWalletPassphrase || userLoginPassword, input: decryptedPrv, encryptionVersion, diff --git a/modules/sdk-core/test/unit/bitgo/recovery/initiate.ts b/modules/sdk-core/test/unit/bitgo/recovery/initiate.ts index d21a1384cd..694d877f27 100644 --- a/modules/sdk-core/test/unit/bitgo/recovery/initiate.ts +++ b/modules/sdk-core/test/unit/bitgo/recovery/initiate.ts @@ -1,12 +1,7 @@ import 'should'; import * as sinon from 'sinon'; import * as sjcl from '@bitgo/sjcl'; -import { - validateKey, - validateKeyAsync, - getBip32Keys, - getBip32KeysAsync, -} from '../../../../src/bitgo/recovery/initiate'; +import { validateKey, getBip32Keys } from '../../../../src/bitgo/recovery/initiate'; import { BitGoBase } from '../../../../src/bitgo/bitgoBase'; // A deterministic xprv used across all tests. @@ -16,7 +11,7 @@ const TEST_XPUB = 'xpub661MyMwAqRbcF9Nc7TbBo1rZAagiWEVPWKbDKThNG8zqjk76HAKLkaSbTn6dK2dQPfuD7xjicxCZVWvj67fP5nQ9W7QURmoMVAX8m6jZsGp'; /** - * Encrypt plaintext with SJCL (same algorithm as BitGoAPI.encrypt). + * Encrypt plaintext with SJCL (legacy v1 envelope, matching `encryptionVersion: 1`). */ function sjclEncrypt(password: string, input: string): string { return sjcl.encrypt(password, input) as unknown as string; @@ -24,10 +19,8 @@ function sjclEncrypt(password: string, input: string): string { function makeMockBitGo(decryptImpl: (params: { password?: string; input: string }) => string): BitGoBase { return { - decrypt: sinon.stub().callsFake(decryptImpl), - decryptAsync: sinon.stub().callsFake(async (params: { password?: string; input: string }) => decryptImpl(params)), + decrypt: sinon.stub().callsFake(async (params: { password?: string; input: string }) => decryptImpl(params)), encrypt: sinon.stub(), - encryptAsync: sinon.stub(), } as unknown as BitGoBase; } @@ -36,11 +29,11 @@ describe('validateKey', () => { sinon.restore(); }); - it('returns a BIP32 node directly when key starts with xprv (bypasses decrypt)', () => { + it('returns a BIP32 node directly when key starts with xprv (bypasses decrypt)', async () => { const bitgo = makeMockBitGo(() => { throw new Error('should not be called'); }); - const node = validateKey(bitgo, { + const node = await validateKey(bitgo, { key: TEST_XPRV, source: 'user', passphrase: 'secret', @@ -51,14 +44,14 @@ describe('validateKey', () => { (bitgo.decrypt as sinon.SinonStub).callCount.should.equal(0); }); - it('calls decrypt and returns BIP32 node when key is encrypted (not xprv)', () => { + it('calls decrypt and returns BIP32 node when key is encrypted (not xprv)', async () => { const passphrase = 'hunter2'; const encryptedKey = sjclEncrypt(passphrase, TEST_XPRV); const bitgo = makeMockBitGo(({ password, input }) => { if (input === encryptedKey && password === passphrase) return TEST_XPRV; throw new Error('unexpected decrypt call'); }); - const node = validateKey(bitgo, { + const node = await validateKey(bitgo, { key: encryptedKey, source: 'user', passphrase, @@ -69,64 +62,11 @@ describe('validateKey', () => { (bitgo.decrypt as sinon.SinonStub).callCount.should.equal(1); }); - it('throws with friendly message when decrypt fails (wrong passphrase)', () => { + it('rejects with friendly message when decrypt fails (wrong passphrase)', async () => { const bitgo = makeMockBitGo(() => { throw new Error('sjcl: ccm: tag does not match'); }); - (() => - validateKey(bitgo, { - key: 'notAnXprv', - source: 'user', - passphrase: 'wrong', - isUnsignedSweep: false, - isKrsRecovery: false, - })).should.throw('Failed to decrypt user key with passcode - try again!'); - }); -}); - -describe('validateKeyAsync', () => { - afterEach(() => { - sinon.restore(); - }); - - it('returns a BIP32 node directly when key starts with xprv (bypasses decryptAsync)', async () => { - const bitgo = makeMockBitGo(() => { - throw new Error('should not be called'); - }); - const node = await validateKeyAsync(bitgo, { - key: TEST_XPRV, - source: 'user', - passphrase: 'secret', - isUnsignedSweep: false, - isKrsRecovery: false, - }); - node.toBase58().should.equal(TEST_XPRV); - (bitgo.decryptAsync as sinon.SinonStub).callCount.should.equal(0); - }); - - it('calls decryptAsync and returns BIP32 node when key is encrypted (not xprv)', async () => { - const passphrase = 'hunter2'; - const encryptedKey = sjclEncrypt(passphrase, TEST_XPRV); - const bitgo = makeMockBitGo(({ password, input }) => { - if (input === encryptedKey && password === passphrase) return TEST_XPRV; - throw new Error('unexpected decrypt call'); - }); - const node = await validateKeyAsync(bitgo, { - key: encryptedKey, - source: 'user', - passphrase, - isUnsignedSweep: false, - isKrsRecovery: false, - }); - node.toBase58().should.equal(TEST_XPRV); - (bitgo.decryptAsync as sinon.SinonStub).callCount.should.equal(1); - }); - - it('rejects with friendly message when decryptAsync fails (wrong passphrase)', async () => { - const bitgo = makeMockBitGo(() => { - throw new Error('sjcl: ccm: tag does not match'); - }); - await validateKeyAsync(bitgo, { + await validateKey(bitgo, { key: 'notAnXprv', source: 'user', passphrase: 'wrong', @@ -135,11 +75,11 @@ describe('validateKeyAsync', () => { }).should.be.rejectedWith('Failed to decrypt user key with passcode - try again!'); }); - it('skips decryptAsync for unsigned sweep (isUnsignedSweep = true)', async () => { + it('skips decrypt for unsigned sweep (isUnsignedSweep = true)', async () => { const bitgo = makeMockBitGo(() => { throw new Error('should not be called'); }); - const node = await validateKeyAsync(bitgo, { + const node = await validateKey(bitgo, { key: TEST_XPUB, source: 'user', passphrase: 'secret', @@ -147,7 +87,7 @@ describe('validateKeyAsync', () => { isKrsRecovery: false, }); node.neutered().toBase58().should.equal(TEST_XPUB); - (bitgo.decryptAsync as sinon.SinonStub).callCount.should.equal(0); + (bitgo.decrypt as sinon.SinonStub).callCount.should.equal(0); }); }); @@ -156,43 +96,11 @@ describe('getBip32Keys', () => { sinon.restore(); }); - it('returns [userKey, backupKey] when both are provided as xprv', () => { - const bitgo = makeMockBitGo(() => { - throw new Error('should not be called'); - }); - const keys = getBip32Keys( - bitgo, - { userKey: TEST_XPRV, backupKey: TEST_XPRV, recoveryDestination: 'addr' }, - { requireBitGoXpub: false } - ); - keys.should.have.length(2); - keys[0].toBase58().should.equal(TEST_XPRV); - keys[1].toBase58().should.equal(TEST_XPRV); - }); - - it('throws when requireBitGoXpub is true but bitgoKey is missing', () => { - const bitgo = makeMockBitGo(() => { - throw new Error('should not be called'); - }); - (() => - getBip32Keys( - bitgo, - { userKey: TEST_XPRV, backupKey: TEST_XPRV, recoveryDestination: 'addr' }, - { requireBitGoXpub: true } - )).should.throw('BitGo xpub required but not provided'); - }); -}); - -describe('getBip32KeysAsync', () => { - afterEach(() => { - sinon.restore(); - }); - it('returns [userKey, backupKey] when both are provided as xprv', async () => { const bitgo = makeMockBitGo(() => { throw new Error('should not be called'); }); - const keys = await getBip32KeysAsync( + const keys = await getBip32Keys( bitgo, { userKey: TEST_XPRV, backupKey: TEST_XPRV, recoveryDestination: 'addr' }, { requireBitGoXpub: false } @@ -206,7 +114,7 @@ describe('getBip32KeysAsync', () => { const bitgo = makeMockBitGo(() => { throw new Error('should not be called'); }); - const keys = await getBip32KeysAsync( + const keys = await getBip32Keys( bitgo, { userKey: TEST_XPRV, backupKey: TEST_XPRV, bitgoKey: TEST_XPUB, recoveryDestination: 'addr' }, { requireBitGoXpub: true } @@ -215,28 +123,28 @@ describe('getBip32KeysAsync', () => { keys[2].neutered().toBase58().should.equal(TEST_XPUB); }); - it('calls decryptAsync for encrypted user key', async () => { + it('calls decrypt for encrypted user key', async () => { const passphrase = 'pass1'; const encryptedUserKey = sjclEncrypt(passphrase, TEST_XPRV); const bitgo = makeMockBitGo(({ password, input }) => { if (input === encryptedUserKey && password === passphrase) return TEST_XPRV; throw new Error('unexpected decrypt call'); }); - const keys = await getBip32KeysAsync( + const keys = await getBip32Keys( bitgo, { userKey: encryptedUserKey, backupKey: TEST_XPRV, walletPassphrase: passphrase, recoveryDestination: 'addr' }, { requireBitGoXpub: false } ); keys.should.have.length(2); keys[0].toBase58().should.equal(TEST_XPRV); - (bitgo.decryptAsync as sinon.SinonStub).callCount.should.equal(1); + (bitgo.decrypt as sinon.SinonStub).callCount.should.equal(1); }); it('rejects when requireBitGoXpub is true but bitgoKey is missing', async () => { const bitgo = makeMockBitGo(() => { throw new Error('should not be called'); }); - await getBip32KeysAsync( + await getBip32Keys( bitgo, { userKey: TEST_XPRV, backupKey: TEST_XPRV, recoveryDestination: 'addr' }, { requireBitGoXpub: true } diff --git a/modules/sdk-core/test/unit/bitgo/trading/tradingAccount.ts b/modules/sdk-core/test/unit/bitgo/trading/tradingAccount.ts index a6ab5af59b..c8402461f2 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/tradingAccount.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/tradingAccount.ts @@ -31,7 +31,7 @@ describe('TradingAccount', function () { coin: sinon.stub().callsFake((coin: string) => ({ url: sinon.stub().callsFake((url: string) => `https://app.bitgo-staging.com/api/v2/${coin}${url}`), })), - decryptAsync: sinon + decrypt: sinon .stub() .callsFake(async ({ input, password }) => input === encryptedPrv && password === walletPassphrase ? decryptedPrv : undefined @@ -160,7 +160,7 @@ describe('TradingAccount', function () { const result = await tradingAccount.signPayload({ payload, walletPassphrase }); mockBaseCoin.keychains().get.calledWith({ id: 'user-key-id' }).should.be.true(); - mockBitGo.decryptAsync.calledWith({ input: encryptedPrv, password: walletPassphrase }).should.be.true(); + mockBitGo.decrypt.calledWith({ input: encryptedPrv, password: walletPassphrase }).should.be.true(); mockBaseCoin.signMessage.calledOnce.should.be.true(); result.should.equal(Buffer.from(signature, 'hex').toString('hex')); }); @@ -184,7 +184,7 @@ describe('TradingAccount', function () { it('should use prv directly and not call decrypt when both are provided', async function () { await tradingAccount.signPayload({ payload, walletPassphrase, prv: decryptedPrv }); - mockBitGo.decryptAsync.called.should.be.false(); + mockBitGo.decrypt.called.should.be.false(); mockBaseCoin.keychains.called.should.be.false(); const signMessageCall = mockBaseCoin.signMessage.getCall(0); signMessageCall.args[0].should.deepEqual({ prv: decryptedPrv }); @@ -195,7 +195,7 @@ describe('TradingAccount', function () { it('should sign using the provided prv without calling decrypt', async function () { const result = await tradingAccount.signPayload({ payload, prv: decryptedPrv }); - mockBitGo.decryptAsync.called.should.be.false(); + mockBitGo.decrypt.called.should.be.false(); mockBaseCoin.keychains.called.should.be.false(); mockBaseCoin.signMessage.calledOnce.should.be.true(); result.should.equal(Buffer.from(signature, 'hex').toString('hex')); diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/baseTSSUtils.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/baseTSSUtils.ts index 3798523124..020af23025 100644 --- a/modules/sdk-core/test/unit/bitgo/utils/tss/baseTSSUtils.ts +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/baseTSSUtils.ts @@ -79,7 +79,7 @@ describe('Base TSS Utils', function () { }); beforeEach(function () { - decryptAsyncStub = sinon.stub().callsFake(async ({ input, password }: Parameters[0]) => { + decryptAsyncStub = sinon.stub().callsFake(async ({ input, password }: Parameters[0]) => { try { const parsed = JSON.parse(input); if (parsed.v === 2 && parsed.plaintext !== undefined) { @@ -94,11 +94,11 @@ describe('Base TSS Utils', function () { mockBg.encrypt = sinon .stub() .callsFake((params) => encryptWithSjcl(params.password ?? '', params.input, params.adata)); - mockBg.encryptAsync = sinon + mockBg.encrypt = sinon .stub() .callsFake(async (params) => encryptWithSjcl(params.password ?? '', params.input, params.adata)); mockBg.decrypt = sinon.stub().callsFake((params) => sjcl.decrypt(params.password ?? '', params.input)); - mockBg.decryptAsync = decryptAsyncStub; + mockBg.decrypt = decryptAsyncStub; mockBitgo = mockBg; const mockCoin = {} as IBaseCoin; @@ -191,9 +191,9 @@ describe('Base TSS Utils', function () { }); describe('validateAdata', function () { - it('passes when adata matches with domain separator', function () { + it('passes when adata matches with domain separator', async function () { const adata = 'test-value'; - const cyphertext = mockBitgo.encrypt({ + const cyphertext = await mockBitgo.encrypt({ input: 'secret', password: walletPassphrase, adata: `${roundOneDomainSeparator}:${adata}`, @@ -201,14 +201,14 @@ describe('Base TSS Utils', function () { assert.doesNotThrow(() => baseTssUtils.validateAdataForTest(adata, cyphertext, roundOneDomainSeparator)); }); - it('passes when adata matches without domain separator', function () { + it('passes when adata matches without domain separator', async function () { const adata = 'test-value'; - const cyphertext = mockBitgo.encrypt({ input: 'secret', password: walletPassphrase, adata }); + const cyphertext = await mockBitgo.encrypt({ input: 'secret', password: walletPassphrase, adata }); assert.doesNotThrow(() => baseTssUtils.validateAdataForTest(adata, cyphertext, roundOneDomainSeparator)); }); - it('throws when adata does not match', function () { - const cyphertext = mockBitgo.encrypt({ + it('throws when adata does not match', async function () { + const cyphertext = await mockBitgo.encrypt({ input: 'secret', password: walletPassphrase, adata: `${roundOneDomainSeparator}:correct`, @@ -229,7 +229,7 @@ describe('Base TSS Utils', function () { describe('getBitgoAndUserGpgKeys', function () { it('decrypts v1 SJCL envelope without adata and skips validation', async function () { - const encryptedUserGpgPrvKey = mockBitgo.encrypt({ + const encryptedUserGpgPrvKey = await mockBitgo.encrypt({ input: userGpgKeyPair.privateKey, password: walletPassphrase, }); @@ -248,7 +248,7 @@ describe('Base TSS Utils', function () { it('decrypts v1 SJCL envelope with matching adata', async function () { const adata = 'test-adata'; - const encryptedUserGpgPrvKey = mockBitgo.encrypt({ + const encryptedUserGpgPrvKey = await mockBitgo.encrypt({ input: userGpgKeyPair.privateKey, password: walletPassphrase, adata: `${signingUserGpgKeyDomainSeparator}:${adata}`, @@ -266,9 +266,9 @@ describe('Base TSS Utils', function () { assert.equal(result.userGpgPrvKey.isPrivate(), true); }); - it('decrypts via decryptAsync and returns GPG keys', async function () { + it('decrypts via decrypt and returns GPG keys', async function () { const adata = 'test-adata'; - // Use a fake v2 envelope (v:2) so isV2Envelope returns true and decryptAsync is called. + // Use a fake v2 envelope (v:2) so isV2Envelope returns true and decrypt is called. // The stub extracts plaintext directly from this fake format. const encryptedUserGpgPrvKey = JSON.stringify({ v: 2, @@ -290,7 +290,7 @@ describe('Base TSS Utils', function () { }); it('throws when adata does not match', async function () { - const encryptedUserGpgPrvKey = mockBitgo.encrypt({ + const encryptedUserGpgPrvKey = await mockBitgo.encrypt({ input: userGpgKeyPair.privateKey, password: walletPassphrase, adata: `${signingUserGpgKeyDomainSeparator}:correct-adata`, diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts index e0be05481b..4853206d01 100644 --- a/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/ecdsa/ecdsaMPCv2.ts @@ -59,9 +59,9 @@ describe('ECDSA MPC v2', async () => { return sjcl.decrypt(params.password, params.input); }; mockBg.encrypt = sinon.stub().callsFake(encryptImpl); - mockBg.encryptAsync = sinon.stub().callsFake(async (params) => encryptImpl(params)); + mockBg.encrypt = sinon.stub().callsFake(async (params) => encryptImpl(params)); mockBg.decrypt = sinon.stub().callsFake(decryptImpl); - mockBg.decryptAsync = sinon.stub().callsFake(async (params) => decryptImpl(params)); + mockBg.decrypt = sinon.stub().callsFake(async (params) => decryptImpl(params)); const mockCoin = {} as IBaseCoin; mockCoin.getHashFunction = sinon.stub().callsFake(() => createKeccakHash('keccak256') as Hash); @@ -616,9 +616,9 @@ describe('ECDSA MPC v2', async () => { mockBgWithPost.getEnv = sinon.stub().returns('test'); mockBgWithPost.setRequestTracer = sinon.stub(); mockBgWithPost.encrypt = sinon.stub().returns('encrypted'); - mockBgWithPost.encryptAsync = sinon.stub().resolves('encrypted'); + mockBgWithPost.encrypt = sinon.stub().resolves('encrypted'); mockBgWithPost.decrypt = sinon.stub().returns('decrypted'); - mockBgWithPost.decryptAsync = sinon.stub().resolves('decrypted'); + mockBgWithPost.decrypt = sinon.stub().resolves('decrypted'); mockBgWithPost.post = sinon.stub().returns({ send: sinon.stub().returnsThis(), set: sinon.stub().returnsThis(), @@ -687,9 +687,9 @@ describe('ECDSA MPC v2', async () => { mockBgWithPost.getEnv = sinon.stub().returns('test'); mockBgWithPost.setRequestTracer = sinon.stub(); mockBgWithPost.encrypt = sinon.stub().returns('encrypted'); - mockBgWithPost.encryptAsync = sinon.stub().resolves('encrypted'); + mockBgWithPost.encrypt = sinon.stub().resolves('encrypted'); mockBgWithPost.decrypt = sinon.stub().returns('decrypted'); - mockBgWithPost.decryptAsync = sinon.stub().resolves('decrypted'); + mockBgWithPost.decrypt = sinon.stub().resolves('decrypted'); mockBgWithPost.post = sinon.stub().returns({ send: sinon.stub().returnsThis(), set: sinon.stub().returnsThis(), @@ -909,9 +909,9 @@ describe('ECDSA MPC v2', async () => { }; encryptAsyncSpy = sinon.stub().callsFake(async (params) => encryptImpl(params)); mockBg.encrypt = sinon.stub().callsFake(encryptImpl); - mockBg.encryptAsync = encryptAsyncSpy; + mockBg.encrypt = encryptAsyncSpy; mockBg.decrypt = sinon.stub().callsFake((params) => sjcl.decrypt(params.password, params.input)); - mockBg.decryptAsync = sinon.stub().callsFake(async (params) => sjcl.decrypt(params.password, params.input)); + mockBg.decrypt = sinon.stub().callsFake(async (params) => sjcl.decrypt(params.password, params.input)); const mockCoin = {} as IBaseCoin; mockCoin.getHashFunction = sinon.stub().callsFake(() => createKeccakHash('keccak256') as Hash); @@ -936,7 +936,7 @@ describe('ECDSA MPC v2', async () => { encryptedPrv: undefined, }); - assert.ok(encryptAsyncSpy.called, 'encryptAsync should be called in v1 path'); + assert.ok(encryptAsyncSpy.called, 'encrypt should be called in v1 path'); for (const call of encryptAsyncSpy.getCalls()) { assert.strictEqual( call.args[0].encryptionVersion, diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts index df2b3d17dc..171e328357 100644 --- a/modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts @@ -362,16 +362,16 @@ describe('getEddsaMPCv2RecoveryKeyShares', () => { assert.strictEqual(result.commonKeyChain, userDkg.getCommonKeychain()); }); - it('should route decryption through bitgo.decryptAsync when a bitgo instance is provided', async () => { + it('should route decryption through bitgo.decrypt when a bitgo instance is provided', async () => { // sdk-core has no devDependency on sdk-api or argon2, so we cannot encrypt with a real v2 envelope here. - // The stub verifies that the function delegates to bitgo.decryptAsync (which supports v1 + v2 in + // The stub verifies that the function delegates to bitgo.decrypt (which supports v1 + v2 in // production) rather than falling back to sjcl.decrypt. const [userDkg, backupDkg] = await MPSUtil.generateEdDsaDKGKeyShares(); const userKeyBase64 = userDkg.getReducedKeyShare().toString('base64'); const backupKeyBase64 = backupDkg.getReducedKeyShare().toString('base64'); const mockBitgo = { - decryptAsync: sinon.stub().onFirstCall().resolves(userKeyBase64).onSecondCall().resolves(backupKeyBase64), + decrypt: sinon.stub().onFirstCall().resolves(userKeyBase64).onSecondCall().resolves(backupKeyBase64), } as unknown as BitGoBase; const result = await EDDSAUtils.getEddsaMpcV2RecoveryKeySharesFromReducedKey( @@ -381,7 +381,7 @@ describe('getEddsaMPCv2RecoveryKeyShares', () => { mockBitgo ); - sinon.assert.calledTwice(mockBitgo.decryptAsync as sinon.SinonStub); + sinon.assert.calledTwice(mockBitgo.decrypt as sinon.SinonStub); assert.deepStrictEqual(result.userKeyShare, userDkg.getKeyShare()); assert.deepStrictEqual(result.backupKeyShare, backupDkg.getKeyShare()); assert.strictEqual(result.commonKeyChain, userDkg.getCommonKeychain()); @@ -493,8 +493,7 @@ describe('EddsaMPCv2Utils.createOfflineRound1Share', () => { }); }; mockBitgo = { - encrypt: sinon.stub().callsFake(sjclEncrypt), - encryptAsync: sinon.stub().callsFake(async (params) => sjclEncrypt(params)), + encrypt: sinon.stub().callsFake(async (params) => sjclEncrypt(params)), } as unknown as BitGoBase; const mockCoin = { @@ -561,7 +560,7 @@ describe('EddsaMPCv2Utils.createOfflineRound1Share', () => { sinon.assert.calledOnce(createEncryptionSession); assert.strictEqual(createEncryptionSession.getCall(0).args[0], walletPassphrase); - sinon.assert.notCalled(mockBitgo.encryptAsync as sinon.SinonStub); + sinon.assert.notCalled(mockBitgo.encrypt as sinon.SinonStub); sinon.assert.calledTwice(encrypt); sinon.assert.calledOnce(destroy); @@ -589,7 +588,7 @@ describe('EddsaMPCv2Utils.createOfflineRound1Share', () => { }); sinon.assert.notCalled(mockBitgo.createEncryptionSession as sinon.SinonStub); - sinon.assert.calledTwice(mockBitgo.encryptAsync as sinon.SinonStub); + sinon.assert.calledTwice(mockBitgo.encrypt as sinon.SinonStub); assert.ok(JSON.parse(result.encryptedRound1Session).ct, 'encryptedRound1Session should be an SJCL JSON blob'); assert.ok(JSON.parse(result.encryptedUserGpgPrvKey).ct, 'encryptedUserGpgPrvKey should be an SJCL JSON blob'); }); @@ -650,7 +649,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => { beforeEach(() => { mockBitgo = { - encrypt: sinon.stub().callsFake((params) => { + encrypt: sinon.stub().callsFake(async (params) => { const salt = randomBytes(8); const iv = randomBytes(16); return sjcl.encrypt(params.password, params.input, { @@ -664,22 +663,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => { adata: params.adata, }); }), - encryptAsync: sinon.stub().callsFake(async (params) => { - const salt = randomBytes(8); - const iv = randomBytes(16); - return sjcl.encrypt(params.password, params.input, { - salt: [bytesToWord(salt.subarray(0, 4)), bytesToWord(salt.subarray(4))], - iv: [ - bytesToWord(iv.subarray(0, 4)), - bytesToWord(iv.subarray(4, 8)), - bytesToWord(iv.subarray(8, 12)), - bytesToWord(iv.subarray(12, 16)), - ], - adata: params.adata, - }); - }), - decrypt: sinon.stub().callsFake((params) => sjcl.decrypt(params.password, params.input)), - decryptAsync: sinon.stub().callsFake(async (params) => sjcl.decrypt(params.password, params.input)), + decrypt: sinon.stub().callsFake(async (params) => sjcl.decrypt(params.password, params.input)), } as unknown as BitGoBase; const mockCoin = { @@ -766,11 +750,11 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => { bitgoGpgPrivKey ); - const decryptAsync = sinon.stub().callsFake(async (params: { input: string }) => { + const decrypt = sinon.stub().callsFake(async (params: { input: string }) => { const envelope = JSON.parse(params.input); return envelope.input; }); - mockBitgo.decryptAsync = decryptAsync; + mockBitgo.decrypt = decrypt; const round2 = await eddsaMPCv2Utils.createOfflineRound2Share({ txRequest: txRequestRound1, @@ -780,7 +764,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => { encryptedRound1Session: round1.encryptedRound1Session, }); - sinon.assert.called(mockBitgo.decryptAsync as sinon.SinonStub); + sinon.assert.called(mockBitgo.decrypt as sinon.SinonStub); assert.strictEqual(round2.signatureShareRound2.from, SignatureShareType.USER); const encryptedRound2Session = JSON.parse(round2.encryptedRound2Session); @@ -1006,21 +990,7 @@ describe('EddsaMPCv2Utils.createOfflineRound3Share', () => { beforeEach(() => { mockBitgo = { - encrypt: sinon.stub().callsFake((params) => { - const salt = randomBytes(8); - const iv = randomBytes(16); - return sjcl.encrypt(params.password, params.input, { - salt: [bytesToWord(salt.subarray(0, 4)), bytesToWord(salt.subarray(4))], - iv: [ - bytesToWord(iv.subarray(0, 4)), - bytesToWord(iv.subarray(4, 8)), - bytesToWord(iv.subarray(8, 12)), - bytesToWord(iv.subarray(12, 16)), - ], - adata: params.adata, - }); - }), - encryptAsync: sinon.stub().callsFake(async (params) => { + encrypt: sinon.stub().callsFake(async (params) => { const salt = randomBytes(8); const iv = randomBytes(16); return sjcl.encrypt(params.password, params.input, { @@ -1034,8 +1004,7 @@ describe('EddsaMPCv2Utils.createOfflineRound3Share', () => { adata: params.adata, }); }), - decrypt: sinon.stub().callsFake((params) => sjcl.decrypt(params.password, params.input)), - decryptAsync: sinon.stub().callsFake(async (params) => sjcl.decrypt(params.password, params.input)), + decrypt: sinon.stub().callsFake(async (params) => sjcl.decrypt(params.password, params.input)), } as unknown as BitGoBase; const mockCoin = { @@ -1143,11 +1112,11 @@ describe('EddsaMPCv2Utils.createOfflineRound3Share', () => { const createEncryptionSession = sinon.stub().resolves({ encrypt, destroy }); mockBitgo.createEncryptionSession = createEncryptionSession; - const decryptAsync = sinon.stub().callsFake(async (params: { input: string }) => { + const decrypt = sinon.stub().callsFake(async (params: { input: string }) => { const envelope = JSON.parse(params.input); return envelope.input; }); - mockBitgo.decryptAsync = decryptAsync; + mockBitgo.decrypt = decrypt; const { round1, round2, txRequestRound2 } = await createRound2Flow(baseTxRequest, JSON.stringify({ v: 2 })); @@ -1159,7 +1128,7 @@ describe('EddsaMPCv2Utils.createOfflineRound3Share', () => { encryptedRound2Session: round2.encryptedRound2Session, }); - assert.strictEqual((mockBitgo.decryptAsync as sinon.SinonStub).callCount, 4); + assert.strictEqual((mockBitgo.decrypt as sinon.SinonStub).callCount, 4); assert.strictEqual(round3.signatureShareRound3.from, SignatureShareType.USER); }); @@ -1728,9 +1697,9 @@ describe('EDDSAUtils.isEddsaMpcV1SigningMaterial', () => { let mockBitgo: BitGoBase; beforeEach(() => { // sdk-core has no devDependency on sdk-api/argon2, so v2 envelopes are simulated here. - // Real bitgo.decryptAsync routes v2 to Argon2id; the stub returns MPCv2 CBOR plaintext instead. + // Real bitgo.decrypt routes v2 to Argon2id; the stub returns MPCv2 CBOR plaintext instead. mockBitgo = { - decryptAsync: sinon.stub().callsFake(async (params: { input: string; password: string }) => { + decrypt: sinon.stub().callsFake(async (params: { input: string; password: string }) => { if (isV2Envelope(params.input)) { return MPCv2_CBOR_BYTES; } diff --git a/modules/sdk-core/test/unit/bitgo/wallet/ofcWalletSignTransaction.ts b/modules/sdk-core/test/unit/bitgo/wallet/ofcWalletSignTransaction.ts index fcbacf6074..6ba3a9b3b7 100644 --- a/modules/sdk-core/test/unit/bitgo/wallet/ofcWalletSignTransaction.ts +++ b/modules/sdk-core/test/unit/bitgo/wallet/ofcWalletSignTransaction.ts @@ -89,9 +89,9 @@ describe('Wallet - OFC', function () { beforeEach(function () { // compiled wallet.js: getUserPrv (sync) → decryptKeychainPrivateKey → bitgo.decrypt - // TypeScript source: getUserPrvAsync → decryptKeychainPrivateKeyAsync → bitgo.decryptAsync + // TypeScript source: getUserPrv → decryptKeychainPrivateKey → bitgo.decrypt mockBitGo.decrypt = sinon.stub().returns(prv); - mockBitGo.decryptAsync = sinon.stub().resolves(prv); + mockBitGo.decrypt = sinon.stub().resolves(prv); }); it('should decrypt the keychain with the passphrase and sign via baseCoin.signMessage', async function () { @@ -103,7 +103,7 @@ describe('Wallet - OFC', function () { const decryptCalled = mockBitGo.decrypt.calledWith({ input: encryptedPrv, password: walletPassphrase }) || - mockBitGo.decryptAsync.calledWith({ input: encryptedPrv, password: walletPassphrase }); + mockBitGo.decrypt.calledWith({ input: encryptedPrv, password: walletPassphrase }); decryptCalled.should.be.true(); signMessageStub.calledOnceWith({ prv }, payload).should.be.true(); mockBitGo.post.called.should.be.false(); diff --git a/modules/sdk-core/test/unit/bitgo/wallet/walletsEncryptionVersion.ts b/modules/sdk-core/test/unit/bitgo/wallet/walletsEncryptionVersion.ts index 4e558e0b16..13f8170b06 100644 --- a/modules/sdk-core/test/unit/bitgo/wallet/walletsEncryptionVersion.ts +++ b/modules/sdk-core/test/unit/bitgo/wallet/walletsEncryptionVersion.ts @@ -21,10 +21,10 @@ describe('Wallets - encryptionVersion threading', function () { }; mockBitGo = { - encryptAsync: sinon + encrypt: sinon .stub() .callsFake(async ({ password, input }: { password: string; input: string }) => `enc:${password}:${input}`), - decryptAsync: sinon.stub().resolves('decryptedPrv'), + decrypt: sinon.stub().resolves('decryptedPrv'), get: sinon.stub().returns({ result: sinon.stub(), query: sinon.stub().returnsThis() }), post: sinon.stub().returns({ send: sinon.stub().returns({ result: sinon.stub().resolves({}) }) }), put: sinon.stub().returns({ @@ -54,7 +54,7 @@ describe('Wallets - encryptionVersion threading', function () { }); describe('acceptShare', function () { - it('passes encryptionVersion: 2 to encryptAsync on the multiUserKeyRotationRequired path', async function () { + it('passes encryptionVersion: 2 to encrypt on the multiUserKeyRotationRequired path', async function () { mockBitGo.get.returns({ result: sinon.stub().resolves({ userMultiKeyRotationRequired: true, @@ -70,8 +70,8 @@ describe('Wallets - encryptionVersion threading', function () { encryptionVersion: 2, }); - assert.ok(mockBitGo.encryptAsync.called, 'encryptAsync should have been called'); - const call = mockBitGo.encryptAsync.firstCall; + assert.ok(mockBitGo.encrypt.called, 'encrypt should have been called'); + const call = mockBitGo.encrypt.firstCall; assert.strictEqual(call.args[0].encryptionVersion, 2); }); @@ -90,8 +90,8 @@ describe('Wallets - encryptionVersion threading', function () { userPassword: 'my-password', }); - assert.ok(mockBitGo.encryptAsync.called); - const call = mockBitGo.encryptAsync.firstCall; + assert.ok(mockBitGo.encrypt.called); + const call = mockBitGo.encrypt.firstCall; assert.strictEqual(call.args[0].encryptionVersion, undefined); }); }); @@ -113,15 +113,15 @@ describe('Wallets - encryptionVersion threading', function () { mockBitGo.get.returns({ result: sinon.stub().resolves(walletSharesList) }); }); - it('passes encryptionVersion: 2 to encryptAsync on the multiUserKeyRotationRequired path', async function () { + it('passes encryptionVersion: 2 to encrypt on the multiUserKeyRotationRequired path', async function () { await wallets.bulkAcceptShare({ walletShareIds: ['share-id'], userLoginPassword: 'login-password', encryptionVersion: 2, }); - assert.ok(mockBitGo.encryptAsync.called); - const call = mockBitGo.encryptAsync.firstCall; + assert.ok(mockBitGo.encrypt.called); + const call = mockBitGo.encrypt.firstCall; assert.strictEqual(call.args[0].encryptionVersion, 2); }); @@ -131,8 +131,8 @@ describe('Wallets - encryptionVersion threading', function () { userLoginPassword: 'login-password', }); - assert.ok(mockBitGo.encryptAsync.called); - const call = mockBitGo.encryptAsync.firstCall; + assert.ok(mockBitGo.encrypt.called); + const call = mockBitGo.encrypt.firstCall; assert.strictEqual(call.args[0].encryptionVersion, undefined); }); }); @@ -186,9 +186,9 @@ describe('Wallets - encryptionVersion threading', function () { assert.strictEqual(prepareStub.firstCall.args[3], undefined); }); - it('createBulkWalletShare passes encryptionVersion to encryptPrvForUserAsync', async function () { + it('createBulkWalletShare passes encryptionVersion to encryptPrvForUser', async function () { const encryptPrvStub = sinon - .stub(wallet, 'encryptPrvForUserAsync') + .stub(wallet, 'encryptPrvForUser') .resolves({ encryptedPrv: 'enc', pub: 'pub', fromPubKey: 'fpk', toPubKey: 'tpk', path: 'm/0' }); sinon .stub(wallet as any, 'getDecryptedKeychainForSharing') @@ -207,7 +207,7 @@ describe('Wallets - encryptionVersion threading', function () { it('createBulkWalletShare passes encryptionVersion: undefined when not set', async function () { const encryptPrvStub = sinon - .stub(wallet, 'encryptPrvForUserAsync') + .stub(wallet, 'encryptPrvForUser') .resolves({ encryptedPrv: 'enc', pub: 'pub', fromPubKey: 'fpk', toPubKey: 'tpk', path: 'm/0' }); sinon .stub(wallet as any, 'getDecryptedKeychainForSharing') diff --git a/modules/sdk-core/test/unit/bitgo/wallet/walletsWebauthn.ts b/modules/sdk-core/test/unit/bitgo/wallet/walletsWebauthn.ts index 785a736849..a21cc28acc 100644 --- a/modules/sdk-core/test/unit/bitgo/wallet/walletsWebauthn.ts +++ b/modules/sdk-core/test/unit/bitgo/wallet/walletsWebauthn.ts @@ -34,9 +34,6 @@ describe('Wallets - WebAuthn wallet creation', function () { }), }), encrypt: sinon - .stub() - .callsFake(({ password, input }: { password: string; input: string }) => `encrypted:${password}:${input}`), - encryptAsync: sinon .stub() .callsFake( async ({ password, input }: { password: string; input: string }) => `encrypted:${password}:${input}` @@ -139,7 +136,7 @@ describe('Wallets - WebAuthn wallet creation', function () { }, }); - const encryptCalls = mockBitGo.encryptAsync.getCalls(); + const encryptCalls = mockBitGo.encrypt.getCalls(); const passwordsUsed = encryptCalls.map((call: sinon.SinonSpyCall) => call.args[0].password); passwordsUsed.should.containEql(walletPassphrase); passwordsUsed.should.containEql(webauthnPassphrase);