]>
Commit | Line | Data |
---|---|---|
8b78a819 T |
1 | #include <iostream> |
2 | #include <string> | |
3 | ||
4 | // Include local headers | |
5 | #include "wallet/walletdb.h" | |
6 | #include "util.h" | |
7 | #include "base58.h" | |
8 | #include "wallet/crypter.h" | |
9 | #include <boost/foreach.hpp> | |
10 | ||
11 | ||
12 | void show_help() | |
13 | { | |
14 | std::cout << | |
15 | "This program outputs Bitcoin addresses and private keys from a wallet.dat file" << std::endl | |
16 | << std::endl | |
17 | << "Usage and options: " | |
18 | << std::endl | |
19 | << " -datadir=<directory> to tell the program where your wallet is" | |
20 | << std::endl | |
21 | << " -wallet=<name> (Optional) if your wallet is not named wallet.dat" | |
22 | << std::endl | |
23 | << " -regtest or -testnet (Optional) dumps addresses from regtest/testnet" | |
24 | << std::endl | |
25 | << " -dumppass (Optional)if you want to extract private keys associated with addresses" | |
26 | << std::endl | |
27 | << " -pass=<walletpassphrase> if you have encrypted private keys stored in your wallet" | |
28 | << std::endl; | |
29 | } | |
30 | ||
31 | ||
32 | class WalletUtilityDB : public CDB | |
33 | { | |
34 | private: | |
35 | typedef std::map<unsigned int, CMasterKey> MasterKeyMap; | |
36 | MasterKeyMap mapMasterKeys; | |
37 | unsigned int nMasterKeyMaxID; | |
38 | SecureString mPass; | |
39 | std::vector<CKeyingMaterial> vMKeys; | |
40 | ||
41 | public: | |
42 | WalletUtilityDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnClose = true) : CDB(strFilename, pszMode, fFlushOnClose) | |
43 | { | |
44 | nMasterKeyMaxID = 0; | |
45 | mPass.reserve(100); | |
46 | } | |
47 | ||
48 | std::string getAddress(CDataStream ssKey); | |
49 | std::string getKey(CDataStream ssKey, CDataStream ssValue); | |
50 | std::string getCryptedKey(CDataStream ssKey, CDataStream ssValue, std::string masterPass); | |
51 | bool updateMasterKeys(CDataStream ssKey, CDataStream ssValue); | |
52 | bool parseKeys(bool dumppriv, std::string masterPass); | |
53 | ||
54 | bool DecryptSecret(const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); | |
55 | bool Unlock(); | |
56 | bool DecryptKey(const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key); | |
57 | }; | |
58 | ||
59 | ||
60 | /* | |
61 | * Address from a public key in base58 | |
62 | */ | |
63 | std::string WalletUtilityDB::getAddress(CDataStream ssKey) | |
64 | { | |
65 | CPubKey vchPubKey; | |
66 | ssKey >> vchPubKey; | |
67 | CKeyID id = vchPubKey.GetID(); | |
68 | std::string strAddr = CBitcoinAddress(id).ToString(); | |
69 | ||
70 | return strAddr; | |
71 | } | |
72 | ||
73 | ||
74 | /* | |
75 | * Non encrypted private key in WIF | |
76 | */ | |
77 | std::string WalletUtilityDB::getKey(CDataStream ssKey, CDataStream ssValue) | |
78 | { | |
79 | std::string strKey; | |
80 | CPubKey vchPubKey; | |
81 | ssKey >> vchPubKey; | |
82 | CPrivKey pkey; | |
83 | CKey key; | |
84 | ||
85 | ssValue >> pkey; | |
86 | if (key.Load(pkey, vchPubKey, true)) | |
87 | strKey = CBitcoinSecret(key).ToString(); | |
88 | ||
89 | return strKey; | |
90 | } | |
91 | ||
92 | ||
93 | bool WalletUtilityDB::DecryptSecret(const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) | |
94 | { | |
95 | CCrypter cKeyCrypter; | |
96 | std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); | |
97 | memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); | |
98 | ||
99 | BOOST_FOREACH(const CKeyingMaterial vMKey, vMKeys) | |
100 | { | |
101 | if(!cKeyCrypter.SetKey(vMKey, chIV)) | |
102 | continue; | |
103 | if (cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext))) | |
104 | return true; | |
105 | } | |
106 | return false; | |
107 | } | |
108 | ||
109 | ||
110 | bool WalletUtilityDB::Unlock() | |
111 | { | |
112 | CCrypter crypter; | |
113 | CKeyingMaterial vMasterKey; | |
114 | ||
115 | BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) | |
116 | { | |
117 | if(!crypter.SetKeyFromPassphrase(mPass, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) | |
118 | return false; | |
119 | if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) | |
120 | continue; // try another master key | |
121 | vMKeys.push_back(vMasterKey); | |
122 | } | |
123 | return true; | |
124 | } | |
125 | ||
126 | ||
127 | bool WalletUtilityDB::DecryptKey(const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key) | |
128 | { | |
129 | CKeyingMaterial vchSecret; | |
130 | if(!DecryptSecret(vchCryptedSecret, vchPubKey.GetHash(), vchSecret)) | |
131 | return false; | |
132 | ||
133 | if (vchSecret.size() != 32) | |
134 | return false; | |
135 | ||
136 | key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); | |
137 | return true; | |
138 | } | |
139 | ||
140 | ||
141 | /* | |
142 | * Encrypted private key in WIF format | |
143 | */ | |
144 | std::string WalletUtilityDB::getCryptedKey(CDataStream ssKey, CDataStream ssValue, std::string masterPass) | |
145 | { | |
146 | mPass = masterPass.c_str(); | |
147 | CPubKey vchPubKey; | |
148 | ssKey >> vchPubKey; | |
149 | CKey key; | |
150 | ||
151 | std::vector<unsigned char> vKey; | |
152 | ssValue >> vKey; | |
153 | ||
154 | if (!Unlock()) | |
155 | return ""; | |
156 | ||
157 | if(!DecryptKey(vKey, vchPubKey, key)) | |
158 | return ""; | |
159 | ||
160 | std::string strKey = CBitcoinSecret(key).ToString(); | |
161 | return strKey; | |
162 | } | |
163 | ||
164 | ||
165 | /* | |
166 | * Master key derivation | |
167 | */ | |
168 | bool WalletUtilityDB::updateMasterKeys(CDataStream ssKey, CDataStream ssValue) | |
169 | { | |
170 | unsigned int nID; | |
171 | ssKey >> nID; | |
172 | CMasterKey kMasterKey; | |
173 | ssValue >> kMasterKey; | |
174 | if (mapMasterKeys.count(nID) != 0) | |
175 | { | |
176 | std::cout << "Error reading wallet database: duplicate CMasterKey id " << nID << std::endl; | |
177 | return false; | |
178 | } | |
179 | mapMasterKeys[nID] = kMasterKey; | |
180 | ||
181 | if (nMasterKeyMaxID < nID) | |
182 | nMasterKeyMaxID = nID; | |
183 | ||
184 | return true; | |
185 | } | |
186 | ||
187 | ||
188 | /* | |
189 | * Look at all the records and parse keys for addresses and private keys | |
190 | */ | |
191 | bool WalletUtilityDB::parseKeys(bool dumppriv, std::string masterPass) | |
192 | { | |
193 | DBErrors result = DB_LOAD_OK; | |
194 | std::string strType; | |
195 | bool first = true; | |
196 | ||
197 | try { | |
198 | Dbc* pcursor = GetCursor(); | |
199 | if (!pcursor) | |
200 | { | |
201 | LogPrintf("Error getting wallet database cursor\n"); | |
202 | result = DB_CORRUPT; | |
203 | } | |
204 | ||
205 | if (dumppriv) | |
206 | { | |
207 | while (result == DB_LOAD_OK && true) | |
208 | { | |
209 | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | |
210 | CDataStream ssValue(SER_DISK, CLIENT_VERSION); | |
211 | int result = ReadAtCursor(pcursor, ssKey, ssValue); | |
212 | ||
213 | if (result == DB_NOTFOUND) { | |
214 | break; | |
215 | } | |
216 | else if (result != 0) | |
217 | { | |
218 | LogPrintf("Error reading next record from wallet database\n"); | |
219 | result = DB_CORRUPT; | |
220 | break; | |
221 | } | |
222 | ||
223 | ssKey >> strType; | |
224 | if (strType == "mkey") | |
225 | { | |
226 | updateMasterKeys(ssKey, ssValue); | |
227 | } | |
228 | } | |
229 | pcursor->close(); | |
230 | pcursor = GetCursor(); | |
231 | } | |
232 | ||
233 | while (result == DB_LOAD_OK && true) | |
234 | { | |
235 | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | |
236 | CDataStream ssValue(SER_DISK, CLIENT_VERSION); | |
237 | int ret = ReadAtCursor(pcursor, ssKey, ssValue); | |
238 | ||
239 | if (ret == DB_NOTFOUND) | |
240 | { | |
241 | std::cout << " ]" << std::endl; | |
242 | first = true; | |
243 | break; | |
244 | } | |
245 | else if (ret != DB_LOAD_OK) | |
246 | { | |
247 | LogPrintf("Error reading next record from wallet database\n"); | |
248 | result = DB_CORRUPT; | |
249 | break; | |
250 | } | |
251 | ||
252 | ssKey >> strType; | |
253 | ||
254 | if (strType == "key" || strType == "ckey") | |
255 | { | |
256 | std::string strAddr = getAddress(ssKey); | |
257 | std::string strKey = ""; | |
258 | ||
259 | ||
260 | if (dumppriv && strType == "key") | |
261 | strKey = getKey(ssKey, ssValue); | |
262 | if (dumppriv && strType == "ckey") | |
263 | { | |
264 | if (masterPass == "") | |
265 | { | |
266 | std::cout << "Encrypted wallet, please provide a password. See help below" << std::endl; | |
267 | show_help(); | |
268 | result = DB_LOAD_FAIL; | |
269 | break; | |
270 | } | |
271 | strKey = getCryptedKey(ssKey, ssValue, masterPass); | |
272 | } | |
273 | ||
274 | if (strAddr != "") | |
275 | { | |
276 | if (first) | |
277 | std::cout << "[ "; | |
278 | else | |
279 | std::cout << ", "; | |
280 | } | |
281 | ||
282 | if (dumppriv) | |
283 | { | |
284 | std::cout << "{\"addr\" : \"" + strAddr + "\", " | |
285 | << "\"pkey\" : \"" + strKey + "\"}" | |
286 | << std::flush; | |
287 | } | |
288 | else | |
289 | { | |
290 | std::cout << "\"" + strAddr + "\""; | |
291 | } | |
292 | ||
293 | first = false; | |
294 | } | |
295 | } | |
296 | ||
297 | pcursor->close(); | |
298 | } catch (DbException &e) { | |
299 | std::cout << "DBException caught " << e.get_errno() << std::endl; | |
300 | } catch (std::exception &e) { | |
301 | std::cout << "Exception caught " << std::endl; | |
302 | } | |
303 | ||
304 | if (result == DB_LOAD_OK) | |
305 | return true; | |
306 | else | |
307 | return false; | |
308 | } | |
309 | ||
310 | ||
311 | int main(int argc, char* argv[]) | |
312 | { | |
313 | ParseParameters(argc, argv); | |
314 | std::string walletFile = GetArg("-wallet", "wallet.dat"); | |
315 | std::string masterPass = GetArg("-pass", ""); | |
316 | bool fDumpPass = GetBoolArg("-dumppass", false); | |
317 | bool help = GetBoolArg("-h", false); | |
318 | bool result = false; | |
319 | ||
320 | if (help) | |
321 | { | |
322 | show_help(); | |
323 | return 0; | |
324 | } | |
325 | ||
326 | try { | |
327 | SelectParamsFromCommandLine(); | |
328 | result = WalletUtilityDB(walletFile, "r").parseKeys(fDumpPass, masterPass); | |
329 | } | |
330 | catch (const std::exception& e) { | |
331 | std::cout << "Error opening wallet file " << walletFile << std::endl; | |
332 | std::cout << e.what() << std::endl; | |
333 | } | |
334 | ||
335 | if (result) | |
336 | return 0; | |
337 | else | |
338 | return -1; | |
339 | } |