Some code for deterministic addresses - needs tests

This commit is contained in:
Christian Basler 2016-04-07 23:01:16 +02:00
parent 32ea3517fe
commit f70c15da38
7 changed files with 104 additions and 23 deletions

View File

@ -31,6 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.*; import java.util.concurrent.*;
@ -128,6 +129,18 @@ public class BitmessageContext {
return chan; return chan;
} }
public List<BitmessageAddress> createDeterministicAddresses(
String passphrase, int numberOfAddresses, int version, int stream, boolean shorter) {
List<BitmessageAddress> result = BitmessageAddress.deterministic(
passphrase, numberOfAddresses, version, stream, shorter);
for (int i = 0; i < result.size(); i++) {
BitmessageAddress address = result.get(i);
address.setAlias(passphrase + " (" + (i + 1) + ")");
ctx.getAddressRepository().save(address);
}
return result;
}
public void broadcast(final BitmessageAddress from, final String subject, final String message) { public void broadcast(final BitmessageAddress from, final String subject, final String message) {
Plaintext msg = new Plaintext.Builder(BROADCAST) Plaintext msg = new Plaintext.Builder(BROADCAST)
.from(from) .from(from)

View File

@ -29,7 +29,9 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import static ch.dissem.bitmessage.utils.Decode.bytes; import static ch.dissem.bitmessage.utils.Decode.bytes;
@ -41,6 +43,8 @@ import static ch.dissem.bitmessage.utils.Singleton.security;
* holding private keys. * holding private keys.
*/ */
public class BitmessageAddress implements Serializable { public class BitmessageAddress implements Serializable {
private static final long serialVersionUID = 2386328540805994064L;
private final long version; private final long version;
private final long stream; private final long stream;
private final byte[] ripe; private final byte[] ripe;
@ -115,6 +119,16 @@ public class BitmessageAddress implements Serializable {
return result; return result;
} }
public static List<BitmessageAddress> deterministic(String passphrase, int numberOfAddresses,
int version, int stream, boolean shorter) {
List<BitmessageAddress> result = new ArrayList<>(numberOfAddresses);
List<PrivateKey> privateKeys = PrivateKey.deterministic(passphrase, numberOfAddresses, version, stream, shorter);
for (PrivateKey pk : privateKeys) {
result.add(new BitmessageAddress(pk));
}
return result;
}
public BitmessageAddress(PrivateKey privateKey) { public BitmessageAddress(PrivateKey privateKey) {
this(privateKey.getPubkey()); this(privateKey.getPubkey());
this.privateKey = privateKey; this.privateKey = privateKey;

View File

@ -25,6 +25,8 @@ import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
public class InventoryVector implements Streamable, Serializable { public class InventoryVector implements Streamable, Serializable {
private static final long serialVersionUID = -7349009673063348719L;
/** /**
* Hash of the object * Hash of the object
*/ */
@ -42,7 +44,7 @@ public class InventoryVector implements Streamable, Serializable {
@Override @Override
public int hashCode() { public int hashCode() {
return hash != null ? Arrays.hashCode(hash) : 0; return hash == null ? 0 : Arrays.hashCode(hash);
} }
public byte[] getHash() { public byte[] getHash() {

View File

@ -20,6 +20,8 @@ import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
public class Label implements Serializable { public class Label implements Serializable {
private static final long serialVersionUID = 831782893630994914L;
private Object id; private Object id;
private String label; private String label;
private Type type; private Type type;

View File

@ -31,6 +31,8 @@ import java.util.Arrays;
* A node's address. It's written in IPv6 format. * A node's address. It's written in IPv6 format.
*/ */
public class NetworkAddress implements Streamable { public class NetworkAddress implements Streamable {
private static final long serialVersionUID = 2500120578167100300L;
private long time; private long time;
/** /**

View File

@ -26,6 +26,8 @@ import ch.dissem.bitmessage.utils.Decode;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import java.io.*; import java.io.*;
import java.util.ArrayList;
import java.util.List;
import static ch.dissem.bitmessage.utils.Singleton.security; import static ch.dissem.bitmessage.utils.Singleton.security;
@ -34,7 +36,10 @@ import static ch.dissem.bitmessage.utils.Singleton.security;
* {@link Pubkey} object. * {@link Pubkey} object.
*/ */
public class PrivateKey implements Streamable { public class PrivateKey implements Streamable {
private static final long serialVersionUID = 8562555470709110558L;
public static final int PRIVATE_KEY_SIZE = 32; public static final int PRIVATE_KEY_SIZE = 32;
private final byte[] privateSigningKey; private final byte[] privateSigningKey;
private final byte[] privateEncryptionKey; private final byte[] privateEncryptionKey;
@ -70,29 +75,72 @@ public class PrivateKey implements Streamable {
} }
public PrivateKey(long version, long stream, String passphrase) { public PrivateKey(long version, long stream, String passphrase) {
try { this(new Builder(version, stream, false).seed(passphrase).generate());
byte[] signingKey; }
int signingKeyNonce = 0;
byte[] encryptionKey;
int encryptionKeyNonce = 1;
byte[] passPhraseBytes = passphrase.getBytes("UTF-8");
byte[] ripe;
do {
signingKey = Bytes.truncate(security().sha512(passPhraseBytes, Encode.varInt(signingKeyNonce)), 32);
encryptionKey = Bytes.truncate(security().sha512(passPhraseBytes, Encode.varInt(encryptionKeyNonce)), 32);
byte[] publicSigningKey = security().createPublicKey(signingKey);
byte[] publicEncryptionKey = security().createPublicKey(encryptionKey);
ripe = security().ripemd160(security().sha512(publicSigningKey, publicEncryptionKey));
signingKeyNonce += 2; private PrivateKey(Builder builder) {
encryptionKeyNonce += 2; this.privateSigningKey = builder.privSK;
} while (ripe[0] != 0); this.privateEncryptionKey = builder.privEK;
this.privateSigningKey = signingKey; this.pubkey = Factory.createPubkey(builder.version, builder.stream, builder.pubSK, builder.pubEK, 0, 0);
this.privateEncryptionKey = encryptionKey; }
this.pubkey = security().createPubkey(version, stream, privateSigningKey, privateEncryptionKey, 0, 0);
} catch (IOException e) { private static class Builder {
throw new ApplicationException(e); final long version;
final long stream;
final boolean shorter;
byte[] seed;
long nextNonce;
byte[] privSK, privEK;
byte[] pubSK, pubEK;
private Builder(long version, long stream, boolean shorter) {
this.version = version;
this.stream = stream;
this.shorter = shorter;
} }
Builder seed(String passphrase) {
try {
seed = passphrase.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new ApplicationException(e);
}
return this;
}
Builder generate() {
try {
long signingKeyNonce = nextNonce;
long encryptionKeyNonce = nextNonce + 1;
byte[] ripe;
do {
privEK = Bytes.truncate(security().sha512(seed, Encode.varInt(encryptionKeyNonce)), 32);
privSK = Bytes.truncate(security().sha512(seed, Encode.varInt(signingKeyNonce)), 32);
pubSK = security().createPublicKey(privSK);
pubEK = security().createPublicKey(privEK);
ripe = security().ripemd160(security().sha512(pubSK, pubEK));
signingKeyNonce += 2;
encryptionKeyNonce += 2;
} while (ripe[0] != 0 || (shorter && ripe[1] != 0));
nextNonce = signingKeyNonce;
} catch (IOException e) {
throw new ApplicationException(e);
}
return this;
}
}
public static List<PrivateKey> deterministic(String passphrase, int numberOfAddresses, long version, long stream, boolean shorter) {
List<PrivateKey> result = new ArrayList<>(numberOfAddresses);
Builder builder = new Builder(version, stream, shorter).seed(passphrase);
for (int i = 0; i < numberOfAddresses; i++) {
builder.generate();
result.add(new PrivateKey(builder));
}
return result;
} }
public static PrivateKey read(InputStream is) throws IOException { public static PrivateKey read(InputStream is) throws IOException {

View File

@ -31,7 +31,7 @@ import static java.util.Arrays.copyOfRange;
*/ */
public class Base58 { public class Base58 {
private static final int[] INDEXES = new int[128]; private static final int[] INDEXES = new int[128];
private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
static { static {
for (int i = 0; i < INDEXES.length; i++) { for (int i = 0; i < INDEXES.length; i++) {