From 3bb483742f2f640d3695bd74392b40a0f4067eea Mon Sep 17 00:00:00 2001 From: Mohammad Al Faiyaz Date: Wed, 29 Jul 2026 19:30:25 -0400 Subject: [PATCH] fix(examples): use API to retrieve passcodeEncryptionCode in passphrase recovery Replace manual activation code prompt with BitGo API authentication flow. Retrieve passcodeEncryptionCode via /wallet/{id}/passcoderecovery endpoint. Add coin registration, wallet retrieval, and key validation step. WCN-1331 Co-Authored-By: Claude Opus 4.6 --- .../ts/btc/v1/wallet-passphrase-recovery.ts | 113 +++++++++++++++--- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/examples/ts/btc/v1/wallet-passphrase-recovery.ts b/examples/ts/btc/v1/wallet-passphrase-recovery.ts index e4e1fd8e1e..0cb70bf67c 100644 --- a/examples/ts/btc/v1/wallet-passphrase-recovery.ts +++ b/examples/ts/btc/v1/wallet-passphrase-recovery.ts @@ -2,15 +2,17 @@ /** * Wallet Passphrase Recovery Script * - * This script takes box D information in the keycard and recovers the wallet passphrase. + * This script recovers the wallet passphrase for V1 Wallets by authenticating with BitGo, + * retrieving the passcodeEncryptionCode from the API, and using it to decrypt Box D of the keycard. * * The script will prompt for: * - Environment (test/prod) - * - Activation code + * - BitGo credentials (username, password, OTP) + * - Wallet ID * - Encrypted wallet passphrase from Box D of keycard - * + * * You need to install node and BitGoJS SDK to run this script. - * + * * To install node, you can follow the instructions here: https://nodejs.org/en/download * * To install BitGoJS SDK, you can use the following command: @@ -23,6 +25,7 @@ */ import { BitGoAPI } from '@bitgo/sdk-api'; +import { Btc, Tbtc } from '@bitgo/sdk-coin-btc'; import * as readline from 'readline'; // Create readline interface for user input @@ -46,9 +49,7 @@ async function main(): Promise { console.log('====================================\n'); // Get environment setting - const envInput = await askQuestion( - 'Enter environment (test/prod) [default: test]: ', - ); + const envInput = await askQuestion('Enter environment (test/prod) [default: test]: '); const env = envInput.toLowerCase() === 'prod' ? 'prod' : 'test'; // Initialize BitGo @@ -56,21 +57,105 @@ async function main(): Promise { env: env, }); - // Get activation code - const activationCode = await askQuestion('Enter activation code: '); + // Register appropriate coin based on environment + const coinType = env === 'prod' ? 'btc' : 'tbtc'; + if (coinType === 'btc') { + bitgo.register('btc', Btc.createInstance); + console.log('Using production environment with BTC'); + } else { + bitgo.register('tbtc', Tbtc.createInstance); + console.log('Using test environment with TBTC'); + } + + // Get login credentials from stdin + const username = await askQuestion('\nEnter your BitGo username: '); + const password = await askQuestion('Enter your BitGo password: '); + const loginOtp = await askQuestion('Enter your OTP code for login: '); + + console.log('\nAuthenticating with BitGo...'); + + // Authenticate with BitGo + await bitgo.authenticate({ + username, + password, + otp: loginOtp, + }); + + console.log('Authentication successful.'); + + // Get a fresh OTP for session unlock + const unlockOtp = await askQuestion('\nEnter a new OTP code for session unlock: '); + + // Unlock session + console.log('Unlocking session...'); + await bitgo.unlock({ otp: unlockOtp }); + console.log('Session unlocked successfully.'); + + // Get wallet ID from user + const walletId = await askQuestion('\nEnter your wallet ID: '); + + // Retrieve wallet instance + console.log(`Retrieving wallet information for ID: ${walletId}...`); + const walletInstance = await bitgo.wallets().get({ id: walletId }); + + if (!walletInstance) { + throw new Error('Wallet not found'); + } + + console.log(`Wallet found: ${walletInstance.label()}`); + + // Retrieve recovery info from BitGo + const path = bitgo.url(`/wallet/${walletInstance.id()}/passcoderecovery`); + console.log(`\nFetching recovery info from ${path.toString()}`); + + const recoveryResponse = await bitgo.post(path.toString()).result(); + + console.log('Recovery information retrieved successfully.'); + + // Extract passcode encryption code + if (!recoveryResponse.recoveryInfo || !recoveryResponse.recoveryInfo.passcodeEncryptionCode) { + throw new Error('Recovery info not found or missing passcode encryption code'); + } + + const { passcodeEncryptionCode, encryptedXprv } = recoveryResponse.recoveryInfo; // Get encrypted wallet passphrase from Box D - const encryptedWalletPassphrase = await askQuestion( - 'Enter encrypted wallet passphrase from Box D: ', - ); + const encryptedWalletPassphrase = await askQuestion('\nEnter encrypted wallet passphrase from Box D: '); + + if (!encryptedWalletPassphrase) { + throw new Error('Encrypted wallet passphrase is required'); + } + + console.log('\nDecrypting wallet passphrase using recovery information...'); // Decrypt the wallet passphrase const walletPassphrase = bitgo.decrypt({ input: encryptedWalletPassphrase, - password: activationCode, + password: passcodeEncryptionCode, }); - console.log(`\n✅ SUCCESS: the decrypted passphrase is: ${walletPassphrase}`); + console.log('Successfully decrypted the wallet passphrase.'); + + // Validate the decrypted passphrase against the wallet's encrypted xprv + console.log('\nValidating the decrypted passphrase against wallet keys...'); + + const coin = bitgo.coin(coinType); + + try { + coin.assertIsValidKey({ + encryptedPrv: encryptedXprv, + walletPassphrase: walletPassphrase, + }); + + console.log(`\n✅ SUCCESS: the decrypted passphrase is: ${walletPassphrase}`); + console.log(` +Please store this passphrase securely as it provides access to your wallet. +Do not share this passphrase with anyone.`); + } catch (error) { + console.error('\n❌ VALIDATION FAILED: The recovered passphrase could not validate the wallet key.'); + console.error('Please check that you entered the correct encrypted passphrase from Box D.'); + console.error(`Error details: ${error.message}`); + } } catch (error) { console.error(`\nError: ${error.message}`); if (error.status) {