Skip to content

Commit

Permalink
v 0.13.0, support for ethereum
Browse files Browse the repository at this point in the history
  • Loading branch information
PawelGorny committed Aug 12, 2021
1 parent 388d88d commit 980ca84
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 9 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ By default program uses P2PKH script semantics for addresses like "1..." and P2W

If derivation path is not specified, by default program is using "m/0/0" (BIP32 for P2PKH and BIP141 for P2WPKH). If you want to do calculations for BIP44 or BIP84, please use the proper derivation path, for example "m/44'/0'/0'/0/0" or "m/84'/0'/0'/0/0", where the last two digits are the most important (account & number of address).

Program supports also Ethereum (path m/44'/60'/0'/0/0)

Program could be launched in 2 modes:
<ol>
<li>ONE_UNKNOWN</li>
Expand Down Expand Up @@ -124,6 +126,7 @@ Configuration file example (seed with 6 words, one word unknown, known possible

</ol>

For BTC only (not ETH):
It is possible to specify the derivation path as a range. For example "m/0/0-4" if you want to launch search on the first five addresses or "m/0/1-2'" if you want to check second and third hardened addresses.
Example of search with a range:

Expand Down
11 changes: 11 additions & 0 deletions examples/example_21.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#example for ethereum
ONE_UNKNOWN
# correct seed: brother canal medal remove pitch hill
0x3287D5AA5Ba43Edd13a61Dd6Baf3AEF6E8Dd6596
6
brother
medal
remove
pitch
hill
m/44'/60'/0'/0/0
9 changes: 7 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@

<groupId>com.pawelgorny</groupId>
<artifactId>lostword</artifactId>
<version>0.12.0</version>
<version>0.13.0</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>org.bitcoinj</groupId>
<artifactId>bitcoinj-core</artifactId>
<version>0.15.8</version>
<version>0.15.10</version>
</dependency>
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
Expand Down
44 changes: 37 additions & 7 deletions src/main/java/com/pawelgorny/lostword/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.HDUtils;
import org.bitcoinj.crypto.MnemonicCode;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.script.Script;
Expand All @@ -15,6 +16,7 @@
public final class Configuration {
final static String COMMENT_CHAR = "#";
public final static String UNKNOWN_CHAR = "?";
public final static Integer ETHEREUM = 60;
final static MnemonicCode MNEMONIC_CODE = getMnemonicCode();
private final NetworkParameters NETWORK_PARAMETERS = MainNetParams.get();
private final String DEFAULT_PATH = "m/0/0";
Expand All @@ -30,9 +32,13 @@ public final class Configuration {
private int knownStart = 0;

private List<ChildNumber> derivationPath;
private String derivationPathFull;
private Integer coin;
private List<ChildNumber> keyPath;

private LegacyAddress legacyAddress;
private SegwitAddress segwitAddress;
private String ethereumAddress;

private final WORK work;

Expand Down Expand Up @@ -74,15 +80,22 @@ private void parseScript(String targetAddress) {
if (this.targetAddress.startsWith("bc1")) {
DBscriptType = Script.ScriptType.P2WPKH;
}
if (this.targetAddress.startsWith("0x")){
this.targetAddress = this.targetAddress.substring(2);
this.ethereumAddress = getTargetAddress().toLowerCase();
this.coin = Configuration.ETHEREUM;
}
}
if (!WORK.ONE_UNKNOWN_CHECK_ALL.equals(work) && !WORK.PERMUTATION.equals(work)){
switch (getDBscriptType()){
case P2PKH:
legacyAddress = LegacyAddress.fromBase58(getNETWORK_PARAMETERS(), getTargetAddress());
break;
case P2WPKH:
segwitAddress = SegwitAddress.fromBech32(getNETWORK_PARAMETERS(), getTargetAddress());
break;
if (this.ethereumAddress==null){
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 All @@ -93,6 +106,7 @@ private void parsePath(String path) {
parsePath(this.DEFAULT_PATH);
return;
}
derivationPathFull = path.replaceAll("'", "H").toUpperCase();
String[] dpath = path.replaceAll("'", "").split("/");
try {
Integer.parseInt(dpath[dpath.length-2]);
Expand Down Expand Up @@ -122,6 +136,10 @@ private void parsePath(String path) {
for (int i=1; i<dpath.length-2; i++){
String x = dpath[i].replaceAll("'","");
derivationPath.add(new ChildNumber(Integer.parseInt(x), true));
if (i==2){
this.coin = Integer.parseInt(x);
this.keyPath = HDUtils.parsePath(this.derivationPathFull);
}
}
}
derivationPath.add(new ChildNumber(getDPaccount(), false));
Expand Down Expand Up @@ -206,4 +224,16 @@ public void setWORDS_POOL(List<List<String>> WORDS_POOL) {
this.WORDS_POOL = WORDS_POOL;
this.SIZE = WORDS_POOL.size();
}

public Integer getCoin() {
return coin;
}

public List<ChildNumber> getKeyPath() {
return keyPath;
}

public String getEthereumAddress() {
return ethereumAddress;
}
}
16 changes: 16 additions & 0 deletions src/main/java/com/pawelgorny/lostword/Worker.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import org.bitcoinj.core.Utils;
import org.bitcoinj.crypto.*;
import org.bitcoinj.script.Script;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.web3j.crypto.Keys;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
Expand Down Expand Up @@ -94,6 +97,11 @@ protected boolean check(final List<String> mnemonic, HMac SHA512DIGEST, MessageD
if (!checksumCheck(mnemonic, sha256)){
return false;
}

if (Configuration.ETHEREUM.equals(configuration.getCoin())){
return checkEthereum(mnemonic);
}

byte[] seed = PBKDF2SHA512.derive(Utils.SPACE_JOINER.join(mnemonic).getBytes(StandardCharsets.UTF_8), SALT, 2048, 64);
boolean result;
int addressToWork = configuration.getDPaddress();
Expand Down Expand Up @@ -125,6 +133,14 @@ protected boolean check(final List<String> mnemonic, HMac SHA512DIGEST, MessageD
return false;
}

protected boolean checkEthereum(final List<String> mnemonic) throws MnemonicException {
DeterministicSeed seed = new DeterministicSeed(mnemonic, null, "", System.currentTimeMillis());
DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
DeterministicKey key = chain.getKeyByPath(configuration.getKeyPath(), true);
String address = Keys.getAddress(key.decompress().getPublicKeyAsHex().substring(2));
return configuration.getEthereumAddress().equals(address);
}

private void displayRealDerivationPath(int a){
String dp = "m/";
for (int i=0; i<configuration.getDerivationPath().size()-1; i++){
Expand Down

0 comments on commit 980ca84

Please sign in to comment.