Skip to content

Commit d78dead

Browse files
committed
more refactoring
1 parent 4b0422a commit d78dead

File tree

1 file changed

+101
-117
lines changed

1 file changed

+101
-117
lines changed

src/main/java/prof7bit/bitcoin/wallettool/fileformats/WalletDatHandler.xtend

Lines changed: 101 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ import java.math.BigInteger
1010
import java.nio.ByteBuffer
1111
import java.nio.ByteOrder
1212
import java.nio.channels.FileChannel
13-
import java.util.ArrayList
1413
import java.util.Arrays
1514
import java.util.HashMap
16-
import java.util.List
1715
import java.util.Map
1816
import org.slf4j.LoggerFactory
1917
import org.spongycastle.crypto.InvalidCipherTextException
@@ -28,93 +26,21 @@ import prof7bit.bitcoin.wallettool.core.KeyObject
2826
import prof7bit.bitcoin.wallettool.exceptions.FormatFoundNeedPasswordException
2927

3028
class WalletDatHandler extends AbstractImportExportHandler {
31-
val log = LoggerFactory.getLogger(WalletDatHandler)
29+
val log = LoggerFactory.getLogger(this.class)
3230

33-
/**
34-
* this list will contain all items from the bdb file,
35-
* its alternating key and value data. It has an even
36-
* count of entries, alternating key and their associated
37-
* value: { k1, v1, k2, v2, ... , kn, vn }
38-
*/
39-
val List<ByteBuffer> bdbKeyValueItems = new ArrayList
40-
41-
/**
42-
* this object will contain all bitcoin keys, it will
43-
* be populated when parseBitcoinData is going through
44-
* the bdbKeyValueItems list.
45-
*/
46-
val rawKeyList = new WalletDatRawKeyDataList
47-
48-
// all bitcoin private keys are encrypted with the mkey
49-
// and the mkey itself is encrypted with the password.
50-
var int mkey_nID
51-
var byte[] mkey_encrypted_key
52-
var byte[] mkey_salt
53-
var int mkey_nDerivationMethod
54-
var int mkey_nDerivationIterations
55-
var String mkey_other_params
5631

5732
/**
5833
* Try to parse keys from a bitcoin-core wallet.dat file.
5934
* Reverse engineered with inspiration from pywallet.py,
6035
* db_page.h, db_dump and a hex editor.
6136
*/
6237
override load(File file, String password, String password2) throws Exception {
63-
var RandomAccessFile raf
64-
try {
65-
log.info("opening file {}", file)
66-
raf = new RandomAccessFile(file, "r")
67-
68-
parseBerkeleyFile(raf)
69-
parseBitcoinData
70-
decryptAndImport(password)
7138

72-
} finally {
73-
raf.close
74-
}
75-
}
76-
77-
//
78-
// ************* decrypt and import
79-
//
39+
val wallet = new WalletDat(file)
40+
wallet.decrypt(password)
8041

81-
/**
82-
* Decrypt (if encrypted) the keys that we have parsed
83-
* from the wallet and import them into walletKeyTool
84-
*/
85-
private def decryptAndImport(String password) throws Exception {
86-
if (mkey_encrypted_key != null){
87-
if (password == null){
88-
throw new FormatFoundNeedPasswordException
89-
}
90-
91-
val crypter = new WalletDatCrypter
92-
93-
// first we decrypt the encrypted master key...
94-
val mkey = crypter.decrypt(
95-
mkey_encrypted_key,
96-
password,
97-
mkey_salt,
98-
mkey_nDerivationIterations,
99-
mkey_nDerivationMethod
100-
)
101-
102-
// ...then we use that to decrypt all the individual keys
103-
for (key : rawKeyList.keyData.values){
104-
if (key.encrypted_private_key != null){
105-
key.private_key = crypter.decrypt(
106-
key.encrypted_private_key,
107-
mkey,
108-
key.public_key
109-
)
110-
}
111-
}
112-
}
113-
114-
// At this point we have all keys unencrypted in rawKeyList.
115-
// import them into the current walletKeyTool instance
11642
var count = 0
117-
for (rawKey : rawKeyList.keyData.values){
43+
for (rawKey : wallet.keys){
11844

11945
var ECKey ecKey
12046
if (rawKey.private_key.length > 32){
@@ -140,14 +66,14 @@ class WalletDatHandler extends AbstractImportExportHandler {
14066
if (rawKey.pool){
14167
wktKey.label = "(reserve)"
14268
} else {
143-
val label = rawKeyList.getName(addr)
69+
val label = wallet.getAddrLabel(addr)
14470
wktKey.label = label
14571
if (label == ""){
14672
wktKey.label = "(change)"
14773
}
14874
}
14975
walletKeyTool.add(wktKey)
150-
walletKeyTool.reportProgress(100 * count / rawKeyList.keyData.keySet.length, addr)
76+
walletKeyTool.reportProgress(100 * count / wallet.keys.length, addr)
15177
count = count + 1
15278

15379
} else {
@@ -159,35 +85,58 @@ class WalletDatHandler extends AbstractImportExportHandler {
15985
log.info("import complete :-)")
16086
}
16187

162-
def getParams(){
88+
private def getParams(){
16389
if (walletKeyTool.params == null){
16490
walletKeyTool.params = new MainNetParams
16591
}
16692
return walletKeyTool.params
16793
}
16894

169-
//
170-
// ************* parsing the wallet contents from the key/value list
171-
//
95+
/**
96+
* Save the keys to the wallet.dat file. This will always
97+
* throw an exception because it will not be implemented.
98+
*/
99+
override save(File file, String password, String password2) throws Exception {
100+
throw new UnsupportedOperationException("Writing wallet.dat is not supported")
101+
}
102+
}
103+
104+
class WalletDat {
105+
val log = LoggerFactory.getLogger(this.class)
106+
107+
var RandomAccessFile raf
108+
var ByteBuffer currentKey = null
172109

173110
/**
174-
* Parse the key/value pairs. After the bdb parsing is done we
175-
* have a collection of all key/value pairs from the database.
176-
* This method will go through all of them to parse relevant
177-
* data from it. While it is running it will populate the
178-
* rawKeyList with all keys and their meta data.
111+
* this object will contain all bitcoin keys, it will
112+
* be populated while going through the bdb key/value
113+
* pairs in the file.
179114
*/
180-
private def parseBitcoinData(){
181-
rawKeyList.clear
182-
var i = 0
183-
while (i < bdbKeyValueItems.length - 1) {
184-
val key = bdbKeyValueItems.get(i)
185-
val value = bdbKeyValueItems.get(i + 1)
186-
parseKeyValuePair(key, value)
187-
i = i + 2
115+
val rawKeyList = new WalletDatRawKeyDataList
116+
117+
// all bitcoin private keys are encrypted with the mkey
118+
// and the mkey itself is encrypted with the password.
119+
var int mkey_nID
120+
var byte[] mkey_encrypted_key
121+
var byte[] mkey_salt
122+
var int mkey_nDerivationMethod
123+
var int mkey_nDerivationIterations
124+
var String mkey_other_params
125+
126+
new (File file) throws Exception {
127+
try {
128+
log.info("opening file {}", file)
129+
raf = new RandomAccessFile(file, "r")
130+
parseBerkeleyFile
131+
} finally {
132+
raf.close
188133
}
189134
}
190135

136+
//
137+
// Bitcoin stuff
138+
//
139+
191140
/**
192141
* parse an individual key/value pair and see if it contains
193142
* relevant information, extract this information (we only
@@ -279,11 +228,6 @@ class WalletDatHandler extends AbstractImportExportHandler {
279228
log.trace("found: type 'pool' n={}", n)
280229
}
281230

282-
283-
//
284-
// ************* reading stuff from a ByteBuffer in the way bitcoin likes to encode it
285-
//
286-
287231
private def readString(ByteBuffer buf) {
288232
return new String(buf.readSizePrefixedByteArray)
289233
}
@@ -318,11 +262,53 @@ class WalletDatHandler extends AbstractImportExportHandler {
318262
}
319263
}
320264

265+
266+
/**
267+
* Decrypt (if encrypted) the keys in the raw key list,
268+
* do nothing if they are not encrypted
269+
*/
270+
def decrypt(String password) throws Exception {
271+
if (mkey_encrypted_key != null){
272+
if (password == null){
273+
throw new FormatFoundNeedPasswordException
274+
}
275+
276+
val crypter = new WalletDatCrypter
277+
278+
// first we decrypt the encrypted master key...
279+
val mkey = crypter.decrypt(
280+
mkey_encrypted_key,
281+
password,
282+
mkey_salt,
283+
mkey_nDerivationIterations,
284+
mkey_nDerivationMethod
285+
)
286+
287+
// ...then we use that to decrypt all the individual keys
288+
for (key : rawKeyList.keyData.values){
289+
if (key.encrypted_private_key != null){
290+
key.private_key = crypter.decrypt(
291+
key.encrypted_private_key,
292+
mkey,
293+
key.public_key
294+
)
295+
}
296+
}
297+
}
298+
}
299+
300+
def getKeys() {
301+
rawKeyList.keyData.values
302+
}
303+
304+
def getAddrLabel(String addr){
305+
rawKeyList.getName(addr)
306+
}
307+
321308
//
322-
// ************* reading Berkeley-specific structures from the file
309+
// Berkeley-db stuff
323310
//
324311

325-
326312
/**
327313
* Parse the wallet.dat file, find all b-tree leaf
328314
* pages in the file and put all their items into
@@ -331,7 +317,7 @@ class WalletDatHandler extends AbstractImportExportHandler {
331317
* no next page. When this function has returned we
332318
* have all key/value items in the bdbKeyValueItems list.
333319
*/
334-
private def parseBerkeleyFile(RandomAccessFile raf) throws Exception {
320+
private def parseBerkeleyFile() throws Exception {
335321
// these are the only ones we support
336322
val MAGIC = 0x53162
337323
val VERSION = 9
@@ -345,15 +331,13 @@ class WalletDatHandler extends AbstractImportExportHandler {
345331
val pagesize = head.pageSize
346332
val last_pgno = head.lastPgno
347333

348-
bdbKeyValueItems.clear
349334
for (pgno : 0..last_pgno) {
350335
// find a root leaf
351336
val page = new BerkeleyDBLeafPage(raf, pgno, pagesize)
352337
if (page.isLBTREE && page.isRootPage){
353338
readAllLeafPages(page)
354339
}
355340
}
356-
log.debug("parsing done, found {} key/value pairs in db file", bdbKeyValueItems.length / 2)
357341
}
358342

359343
/**
@@ -379,19 +363,19 @@ class WalletDatHandler extends AbstractImportExportHandler {
379363
log.debug("page {} contains {} entries", page.pgno , count)
380364
for (i : 0..<count){
381365
if (page.getItemType(i) == 1) {
382-
val data = page.getItemData(i)
383-
bdbKeyValueItems.add(data)
366+
val item = page.getItemData(i)
367+
if (currentKey == null){
368+
//even number, item is a key
369+
currentKey = item
370+
} else {
371+
// odd number, item is a value,
372+
// previous item was its key
373+
parseKeyValuePair(currentKey, item)
374+
currentKey = null
375+
}
384376
}
385377
}
386378
}
387-
388-
//
389-
// ************* saving (won't be implemented)
390-
//
391-
392-
override save(File file, String password, String password2) throws Exception {
393-
throw new UnsupportedOperationException("Writing wallet.dat is not supported")
394-
}
395379
}
396380

397381
abstract class BerkeleyDBPage {

0 commit comments

Comments
 (0)