Skip to content

Commit 882d034

Browse files
Merge pull request #6715 from BitGo/WP-5532/example-script-rcvr-v1-password
feat(root): add wallet recovery validation script for v1 wallets
2 parents a15b5e6 + 6a3b9f4 commit 882d034

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Wallet Recovery Validation Script
4+
*
5+
* This script retrieves a wallet's recovery info from BitGo and validates it against
6+
* the user's keycard information (Box D). It helps recover the original wallet password for V1 Wallets Only.
7+
*
8+
* Usage:
9+
* ts-node wallet-recovery-validation.ts
10+
*
11+
* The script will prompt for:
12+
* - BitGo credentials (username, password, OTP)
13+
* - Wallet ID
14+
* - Encrypted private key from Box D of keycard
15+
*/
16+
17+
import { BitGoAPI } from '@bitgo/sdk-api';
18+
import { Btc, Tbtc } from '@bitgo/sdk-coin-btc';
19+
import * as readline from 'readline';
20+
21+
// Create readline interface for user input
22+
const rl = readline.createInterface({
23+
input: process.stdin,
24+
output: process.stdout,
25+
});
26+
27+
// Function to ask questions in the terminal
28+
function askQuestion(query: string): Promise<string> {
29+
return new Promise((resolve) => {
30+
rl.question(query, (answer) => {
31+
resolve(answer);
32+
});
33+
});
34+
}
35+
36+
async function main(): Promise<void> {
37+
try {
38+
console.log('BitGo Wallet Recovery Validation Tool');
39+
console.log('====================================\n');
40+
41+
// Get environment setting
42+
const envInput = await askQuestion('Enter environment (test/prod) [default: test]: ');
43+
const env = envInput.toLowerCase() === 'prod' ? 'prod' : 'test';
44+
45+
// Initialize BitGo
46+
const bitgo = new BitGoAPI({
47+
env: env,
48+
});
49+
50+
// Register appropriate coin based on environment
51+
// For prod use BTC, for test use TBTC
52+
const coinType = env === 'prod' ? 'btc' : 'tbtc';
53+
if (coinType === 'btc') {
54+
bitgo.register('btc', Btc.createInstance);
55+
console.log('Using production environment with BTC');
56+
} else {
57+
bitgo.register('tbtc', Tbtc.createInstance);
58+
console.log('Using test environment with TBTC');
59+
}
60+
61+
// Get login credentials from stdin
62+
const username = await askQuestion('\nEnter your BitGo username: ');
63+
const password = await askQuestion('Enter your BitGo password: ');
64+
const loginOtp = await askQuestion('Enter your OTP code for login: ');
65+
66+
console.log('\nAuthenticating with BitGo...');
67+
68+
// Authenticate with BitGo
69+
await bitgo.authenticate({
70+
username,
71+
password,
72+
otp: loginOtp,
73+
});
74+
75+
console.log('Authentication successful.');
76+
77+
// Get a fresh OTP for session unlock
78+
const unlockOtp = await askQuestion('\nEnter a new OTP code for session unlock: ');
79+
80+
// Unlock session
81+
console.log('Unlocking session...');
82+
await bitgo.unlock({ otp: unlockOtp });
83+
console.log('Session unlocked successfully.');
84+
85+
// Get wallet ID from user
86+
const walletId = await askQuestion('\nEnter your wallet ID: ');
87+
88+
// Retrieve wallet instance
89+
console.log(`Retrieving wallet information for ID: ${walletId}...`);
90+
const walletInstance = await bitgo.wallets().get({ id: walletId });
91+
92+
if (!walletInstance) {
93+
throw new Error('Wallet not found');
94+
}
95+
96+
console.log(`Wallet found: ${walletInstance.label()}`);
97+
98+
// Retrieve recovery info from BitGo
99+
const path = bitgo.url(`/wallet/${walletInstance.id()}/passcoderecovery`);
100+
console.log(`
101+
Fetching user keychain recovery info from ${path.toString()}`);
102+
103+
// Using the walletId to call the passcode recovery API
104+
const recoveryResponse = await bitgo.post(path.toString()).result();
105+
106+
console.log('Recovery information retrieved successfully.');
107+
108+
// Extract passcode encryption code
109+
if (!recoveryResponse.recoveryInfo || !recoveryResponse.recoveryInfo.passcodeEncryptionCode) {
110+
throw new Error('Recovery info not found or missing passcode encryption code');
111+
}
112+
113+
const { passcodeEncryptionCode, encryptedXprv } = recoveryResponse.recoveryInfo;
114+
115+
console.log(`
116+
Recovery information received:
117+
- Passcode Encryption Code: ${passcodeEncryptionCode}
118+
- Encrypted xprv is available for validation`);
119+
120+
// Get encrypted private key from Box D
121+
const encryptedPrv = await askQuestion('\nEnter your encrypted private key from Box D of your keycard: ');
122+
123+
if (!encryptedPrv) {
124+
throw new Error('Encrypted private key is required');
125+
}
126+
127+
console.log('\nDecrypting wallet password using recovery information...');
128+
129+
// Decrypt the original password
130+
const decryptedPassword = bitgo.decrypt({
131+
password: passcodeEncryptionCode,
132+
input: encryptedPrv,
133+
});
134+
135+
console.log('Successfully decrypted the original password.');
136+
137+
// Validate the decrypted password against the wallet's encrypted xprv
138+
console.log('\nValidating the decrypted password against wallet keys...');
139+
140+
// Get the coin instance
141+
const coin = bitgo.coin(coinType);
142+
143+
try {
144+
// This will throw if the key is invalid
145+
coin.assertIsValidKey({
146+
encryptedPrv: encryptedXprv,
147+
walletPassphrase: decryptedPassword,
148+
});
149+
150+
console.log('\n✅ SUCCESS: The password is valid for this wallet.');
151+
console.log(`
152+
Your original wallet password is: ${decryptedPassword}
153+
154+
Please store this password securely as it provides access to your wallet.
155+
Do not share this password with anyone.`);
156+
} catch (error) {
157+
console.error('\n❌ VALIDATION FAILED: The recovered password could not validate the wallet key.');
158+
console.error('Please check that you entered the correct encrypted private key from Box D.');
159+
console.error(`Error details: ${error.message}`);
160+
}
161+
} catch (error) {
162+
console.error(`\nError: ${error.message}`);
163+
if (error.status) {
164+
console.error(`Status code: ${error.status}`);
165+
}
166+
console.error('Please check your credentials and try again.');
167+
} finally {
168+
rl.close();
169+
}
170+
}
171+
172+
// Run the main function
173+
main().catch((error) => {
174+
console.error(`Fatal error: ${error.message}`);
175+
process.exit(1);
176+
});

0 commit comments

Comments
 (0)