Skip to content

Commit

Permalink
v 0.6.2, minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
PawelGorny committed Dec 3, 2020
1 parent d2ec40f commit b9bde04
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 23 deletions.
9 changes: 8 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.pawelgorny</groupId>
<artifactId>lostword</artifactId>
<version>0.6.1</version>
<version>0.6.2</version>
<packaging>jar</packaging>

<dependencies>
Expand All @@ -25,6 +25,13 @@
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.26</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
31 changes: 30 additions & 1 deletion src/main/java/com/pawelgorny/lostword/Configuration.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.pawelgorny.lostword;

import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.params.MainNetParams;
Expand All @@ -10,7 +12,7 @@

public final class Configuration {
final static String COMMENT_CHAR = "#";
final static String UNKNOWN_CHAR = "?";
public final static String UNKNOWN_CHAR = "?";
final static MnemonicCode MNEMONIC_CODE = getMnemonicCode();
private final NetworkParameters NETWORK_PARAMETERS = MainNetParams.get();
private final String DEFAULT_PATH = "m/0/0";
Expand All @@ -25,6 +27,9 @@ public final class Configuration {
private Script.ScriptType DBscriptType = Script.ScriptType.P2PKH;
private int knownStart = 0;

private LegacyAddress legacyAddress;
private SegwitAddress segwitAddress;

private final WORK work;

public Configuration(WORK work, String targetAddress, String path, List<String> words, int knownStarter) {
Expand Down Expand Up @@ -69,6 +74,14 @@ private void parseScript(String targetAddress) {
DBscriptType = Script.ScriptType.P2WPKH;
}
}
switch (getDBscriptType()){
case P2PKH:
legacyAddress = LegacyAddress.fromBase58(getNETWORK_PARAMETERS(), getTargetAddress());
break;
case P2WPKH:
segwitAddress = SegwitAddress.fromBech32(getNETWORK_PARAMETERS(), getTargetAddress());
break;
}
System.out.println("Using script " + DBscriptType);
}

Expand Down Expand Up @@ -137,4 +150,20 @@ public WORK getWork() {
public int getKnownStart() {
return knownStart;
}

public LegacyAddress getLegacyAddress() {
return legacyAddress;
}

public void setLegacyAddress(LegacyAddress legacyAddress) {
this.legacyAddress = legacyAddress;
}

public SegwitAddress getSegwitAddress() {
return segwitAddress;
}

public void setSegwitAddress(SegwitAddress segwitAddress) {
this.segwitAddress = segwitAddress;
}
}
68 changes: 53 additions & 15 deletions src/main/java/com/pawelgorny/lostword/Worker.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package com.pawelgorny.lostword;

import com.google.common.base.Preconditions;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Utils;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.crypto.MnemonicException;
import org.bitcoinj.crypto.PBKDF2SHA512;
import org.bitcoinj.crypto.*;
import org.bitcoinj.script.Script;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -24,9 +23,12 @@ public class Worker {
protected int THREADS = 2;
protected final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

private final byte[] BITCOIN_SEED_BYTES = "Bitcoin seed".getBytes();
private final HMac SHA_512_DIGEST;
private final long CREATION_SECONDS = Utils.currentTimeSeconds();
private static final String SALT = "mnemonic";

public Worker(Configuration configuration) {
public Worker(Configuration configuration) {
this.configuration = configuration;
int procs = Runtime.getRuntime().availableProcessors();
if (procs > 1) {
Expand All @@ -35,9 +37,10 @@ public Worker(Configuration configuration) {
}
THREADS = procs;
}
SHA_512_DIGEST = createHmacSha512Digest();
}

void run() throws InterruptedException, MnemonicException {
public void run() throws InterruptedException, MnemonicException {
Worker worker = null;
switch (configuration.getWork()){
case ONE_UNKNOWN:
Expand All @@ -62,20 +65,55 @@ void run() throws InterruptedException, MnemonicException {
}

protected boolean check(final List<String> mnemonic) throws MnemonicException {
return check(mnemonic, null);
}

protected boolean check(final List<String> mnemonic, HMac SHA512DIGEST) throws MnemonicException {
try {
Configuration.MNEMONIC_CODE.check(mnemonic);
} catch (MnemonicException.MnemonicChecksumException checksumException) {
return false;
}
byte[] seed = PBKDF2SHA512.derive(Utils.SPACE_JOINER.join(mnemonic), SALT, 2048, 64);
DeterministicKey deterministicKey = HDKeyDerivation.createMasterPrivateKey(seed);
DeterministicKey deterministicKey = createMasterPrivateKey(seed, SHA512DIGEST==null?this.SHA_512_DIGEST:SHA512DIGEST);
DeterministicKey receiving = HDKeyDerivation.deriveChildKey(deterministicKey, configuration.getDPchild0());
DeterministicKey new_address_key = HDKeyDerivation.deriveChildKey(receiving, configuration.getDPchild1());
if (configuration.getTargetAddress().equalsIgnoreCase(Address.fromKey(configuration.getNETWORK_PARAMETERS(), new_address_key, configuration.getDBscriptType()).toString())) {
System.out.println(mnemonic);
return true;
if (configuration.getDBscriptType().equals(Script.ScriptType.P2WPKH)){
return Address.fromKey(configuration.getNETWORK_PARAMETERS(), new_address_key, configuration.getDBscriptType()).equals(configuration.getSegwitAddress());
}
return false;
return Address.fromKey(configuration.getNETWORK_PARAMETERS(), new_address_key, configuration.getDBscriptType()).equals(configuration.getLegacyAddress());
// if (configuration.getTargetAddress().equalsIgnoreCase(Address.fromKey(configuration.getNETWORK_PARAMETERS(), new_address_key, configuration.getDBscriptType()).toString())) {
//// System.out.println(mnemonic);
// return true;
// }
// return false;
}

public HMac createHmacSha512Digest() {
SHA512Digest digest = new SHA512Digest();
HMac hMac = new HMac(digest);
hMac.init(new KeyParameter(BITCOIN_SEED_BYTES));
return hMac;
}
private byte[] hmacSha512(HMac hmacSha512, byte[] input) {
hmacSha512.reset();
hmacSha512.update(input, 0, input.length);
byte[] out = new byte[64];
hmacSha512.doFinal(out, 0);
return out;
}

private DeterministicKey createMasterPrivateKey(byte[] seed, HMac SHA512DIGEST) throws HDDerivationException {
byte[] i = hmacSha512(SHA512DIGEST, seed);
Preconditions.checkState(i.length == 64, Integer.valueOf(i.length));
byte[] il = Arrays.copyOfRange(i, 0, 32);
byte[] ir = Arrays.copyOfRange(i, 32, 64);
Arrays.fill(i, (byte)0);
DeterministicKey masterPrivKey = HDKeyDerivation.createMasterPrivKeyFromBytes(il, ir);
Arrays.fill(il, (byte)0);
Arrays.fill(ir, (byte)0);
masterPrivKey.setCreationTimeSeconds(CREATION_SECONDS);
return masterPrivKey;
}

protected List<List<String>> split() {
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/com/pawelgorny/lostword/WorkerKnownPosition.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.pawelgorny.lostword;

import org.bitcoinj.crypto.MnemonicException;
import org.bouncycastle.crypto.macs.HMac;

import java.util.ArrayList;
import java.util.Date;
Expand All @@ -19,7 +20,7 @@ public WorkerKnownPosition(Configuration configuration) {
super(configuration);
}

void run() throws InterruptedException, MnemonicException {
public void run() throws InterruptedException, MnemonicException {
check();
}

Expand Down Expand Up @@ -73,11 +74,12 @@ private void checkUnknown(int position) throws InterruptedException {
if (REPORTER) {
start = System.currentTimeMillis();
}
final HMac SHA_512_DIGEST = createHmacSha512Digest();
try {
int WORKING_POSITION_PLUS = WORKING_POSITION+1;
for (int bipPosition = 0; RESULT == null && bipPosition < WORDS_TO_WORK.size(); bipPosition++) {
SEED.set(WORKING_POSITION, WORDS_TO_WORK.get(bipPosition));
processSeed(SEED, 2, WORKING_POSITION_PLUS, REPORTER);
processSeed(SEED, 2, WORKING_POSITION_PLUS, REPORTER, SHA_512_DIGEST);
}
} catch (Exception e) {
Thread.currentThread().interrupt();
Expand All @@ -90,9 +92,10 @@ private void checkUnknown(int position) throws InterruptedException {
}
}

private void processSeed(final List<String> seed, int depth, int positionStartSearch, boolean reporter) throws MnemonicException {
private void processSeed(final List<String> seed, int depth, int positionStartSearch, boolean reporter, HMac SHA_512_DIGEST) throws MnemonicException {
if (NUMBER_UNKNOWN==depth){
if (check(seed)){
if (check(seed, SHA_512_DIGEST)){
System.out.println(seed);
RESULT = new Result(seed);
return;
}
Expand All @@ -109,7 +112,7 @@ private void processSeed(final List<String> seed, int depth, int positionStartSe
}
for (int w = 0; RESULT==null && w<DICTIONARY_SIZE; w++){
seed.set(position, Configuration.MNEMONIC_CODE.getWordList().get(w));
processSeed(seed, nextDepth, positionStartNextSearch, reporter);
processSeed(seed, nextDepth, positionStartNextSearch, reporter, SHA_512_DIGEST);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/pawelgorny/lostword/WorkerUnknown.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public WorkerUnknown(Configuration configuration) {
super(configuration);
}

void run() throws InterruptedException {
public void run() throws InterruptedException {
for (int position = 0; position <= configuration.getSIZE() && RESULT == null; position++) {
processPosition(position);
}
Expand Down
95 changes: 95 additions & 0 deletions src/test/java/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import com.google.common.base.Stopwatch;
import com.pawelgorny.lostword.Configuration;
import com.pawelgorny.lostword.WORK;
import com.pawelgorny.lostword.Worker;
import org.bitcoinj.crypto.MnemonicException;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertTrue;

public class Test {

private static final int LOOP_SIZE = 500;
private static final String TARGET = "1AcuLxsQSMTi6fLEbJ6F6sNsZ4NyqnUNSo";
private static final List<String> RESULT = new ArrayList(){{add("brother");add("canal");add("medal");
add("remove");add("pitch");add("hill");}};
private static final String PATH = "m/0/2'";

private static final String TARGET2 = "bc1qfnmxy77huxfyashpjquxhgv9urw506s2nmzs5t";
private static final List<String> RESULT2 = new ArrayList(){{add("abandon");add("ability");add("ability");
add("home");add("car");add("test");}};
private static final String PATH2 = "m/0/1";

@org.junit.Test
public void testCheck() throws MnemonicException {
Configuration configuration = new Configuration(null, TARGET, PATH, RESULT, 0);
WorkerTester workerTester = new WorkerTester(configuration);
assertTrue(workerTester.check(RESULT));
System.out.println("worker.check OK");
Stopwatch stopwatch = Stopwatch.createStarted();
for (int i=0; i<LOOP_SIZE; i++){
workerTester.check(RESULT);
}
stopwatch.stop();
System.out.println(stopwatch.elapsed());
}

@org.junit.Test
public void testONE_UNKNOWN() throws MnemonicException, InterruptedException {
List<String> words = RESULT.subList(1,RESULT.size());
Configuration configuration = new Configuration(WORK.ONE_UNKNOWN, TARGET, "m/0/2'", words, 0);
WorkerTester workerTester = new WorkerTester(configuration);
((Worker)workerTester).run();
assertTrue(workerTester.isResult());
}

@org.junit.Test
public void testKNOWN_POSITION1() throws MnemonicException, InterruptedException {
List<String> words = new ArrayList<>(RESULT);
words.set(0,Configuration.UNKNOWN_CHAR);
Configuration configuration = new Configuration(WORK.KNOWN_POSITION, TARGET, PATH, words, 0);
WorkerTester workerTester = new WorkerTester(configuration);
((Worker)workerTester).run();
assertTrue(workerTester.isResult());
}

@org.junit.Test
public void testKNOWN_POSITION2() throws MnemonicException, InterruptedException {
List<String> words = new ArrayList<>(RESULT2);
words.set(2,Configuration.UNKNOWN_CHAR);
words.set(1,Configuration.UNKNOWN_CHAR);
Configuration configuration = new Configuration(WORK.KNOWN_POSITION, TARGET2, PATH2, words, 0);
WorkerTester workerTester = new WorkerTester(configuration);
((Worker)workerTester).run();
assertTrue(workerTester.isResult());
}

private class WorkerTester extends Worker{
public WorkerTester(Configuration configuration) {
super(configuration);
}

@Override
protected void processPosition(int position) throws InterruptedException {
super.processPosition(position);
}

@Override
protected List<List<String>> split() {
return super.split();
}

@Override
protected boolean check(List<String> mnemonic) throws MnemonicException {
return super.check(mnemonic);
}

public boolean isResult(){
return RESULT!=null;
}


}
}

0 comments on commit b9bde04

Please sign in to comment.