diff --git a/demo/build.gradle b/demo/build.gradle index dbc81ea..7adbed2 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -12,5 +12,5 @@ dependencies { compile project(':networking') compile project(':inventory') compile 'org.slf4j:slf4j-simple:1.7.12' - testCompile group: 'junit', name: 'junit', version: '4.11' + testCompile 'junit:junit:4.11' } \ No newline at end of file diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java index 644382e..6776750 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java @@ -35,25 +35,25 @@ public class PrivateKey implements Streamable { private final Pubkey pubkey; - public PrivateKey(long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { + public PrivateKey(long stream, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { this.privateSigningKey = Security.randomBytes(64); this.privateEncryptionKey = Security.randomBytes(64); - this.pubkey = Security.createPubkey(Pubkey.LATEST_VERSION, privateSigningKey, privateEncryptionKey, + this.pubkey = Security.createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } - private PrivateKey(byte[] privateSigningKey, byte[] privateEncryptionKey, Pubkey pubkey) { + public PrivateKey(byte[] privateSigningKey, byte[] privateEncryptionKey, Pubkey pubkey) { this.privateSigningKey = privateSigningKey; this.privateEncryptionKey = privateEncryptionKey; this.pubkey = pubkey; } - public PrivateKey(long version, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { + public PrivateKey(long version, long stream, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { try { // FIXME: this is most definitely wrong this.privateSigningKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); this.privateEncryptionKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); - this.pubkey = Security.createPubkey(version, privateSigningKey, privateEncryptionKey, + this.pubkey = Security.createPubkey(version, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java index e19ce83..8d0e804 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -54,7 +54,7 @@ public class Factory { } } - public static Pubkey createPubkey(long version, byte[] publicSigningKey, byte[] publicEncryptionKey, + public static Pubkey createPubkey(long version, long stream, byte[] publicSigningKey, byte[] publicEncryptionKey, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { if (publicSigningKey.length != 64) throw new IllegalArgumentException("64 bytes signing key expected, but it was " @@ -66,12 +66,14 @@ public class Factory { switch ((int) version) { case 2: return new V2Pubkey.Builder() + .stream(stream) .publicSigningKey(publicSigningKey) .publicEncryptionKey(publicEncryptionKey) .behaviorBitfield(Pubkey.Feature.bitfield(features)) .build(); case 3: return new V3Pubkey.Builder() + .stream(stream) .publicSigningKey(publicSigningKey) .publicEncryptionKey(publicEncryptionKey) .behaviorBitfield(Pubkey.Feature.bitfield(features)) @@ -81,6 +83,7 @@ public class Factory { case 4: return new V4Pubkey( new V3Pubkey.Builder() + .stream(stream) .publicSigningKey(publicSigningKey) .publicEncryptionKey(publicEncryptionKey) .behaviorBitfield(Pubkey.Feature.bitfield(features)) @@ -93,8 +96,8 @@ public class Factory { } } - public static BitmessageAddress generatePrivateAddress(Pubkey.Feature... features) { - return new BitmessageAddress(new PrivateKey(1000, 1000, features)); + public static BitmessageAddress generatePrivateAddress(long stream, Pubkey.Feature... features) { + return new BitmessageAddress(new PrivateKey(stream, 1000, 1000, features)); } static ObjectPayload getObjectPayload(long objectType, long version, long streamNumber, InputStream stream, int length) throws IOException { diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java index 7a466c2..07b4e3c 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java @@ -99,4 +99,27 @@ public class Bytes { System.arraycopy(bytes, 0, result, 1, bytes.length); return result; } + + public static byte[] fromHex(String hex) { + if (hex.length() % 2 != 0) throw new IllegalArgumentException("expected even number of characters"); + byte[] result = new byte[hex.length() / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = (byte) (hexValue(hex.charAt(i * 2)) * 16); + result[i] += hexValue(hex.charAt(i * 2 + 1)); + } + return result; + } + + private static int hexValue(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return 10 + c - 'a'; + } + if (c >= 'A' && c <= 'F') { + return 10 + c - 'A'; + } + throw new IllegalArgumentException("'" + c + "' is not a valid hex value"); + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java index 2036604..a078f94 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java @@ -129,15 +129,17 @@ public class Security { } } - public static Pubkey createPubkey(long version, byte[] privateSigningKey, byte[] privateEncryptionKey, + public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { byte[] publicSigningKey = EC_PARAMETERS.getG().multiply(keyToBigInt(privateSigningKey)).getEncoded(false); byte[] publicEncryptionKey = EC_PARAMETERS.getG().multiply(keyToBigInt(privateEncryptionKey)).getEncoded(false); - return Factory.createPubkey(version, Bytes.subArray(publicSigningKey, 1, 64), Bytes.subArray(publicEncryptionKey, 1, 64), + return Factory.createPubkey(version, stream, // publicSigningKey, publicEncryptionKey, + Bytes.subArray(publicSigningKey, 1, publicSigningKey.length - 1), + Bytes.subArray(publicEncryptionKey, 1, publicEncryptionKey.length - 1), nonceTrialsPerByte, extraBytes, features); } private static BigInteger keyToBigInt(byte[] key) { - return new BigInteger(Bytes.concatenate((byte) 0x00, key)); + return new BigInteger(1, key); } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java index d49b84e..3da1811 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -17,6 +17,9 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import ch.dissem.bitmessage.utils.Base58; +import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.Security; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -42,7 +45,37 @@ public class BitmessageAddressTest { @Test public void testCreateAddress() { - BitmessageAddress address = new BitmessageAddress(new PrivateKey(0, 0)); + BitmessageAddress address = new BitmessageAddress(new PrivateKey(0, 0, 0)); assertNotNull(address.getPubkey()); } + + @Test + public void testV3Import() { + assertEquals(3, new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn").getVersion()); + assertEquals(1, new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn").getStream()); + + byte[] privsigningkey = Base58.decode("5KU2gbe9u4rKJ8PHYb1rvwMnZnAJj4gtV5GLwoYckeYzygWUzB9"); + byte[] privencryptionkey = Base58.decode("5KHd4c6cavd8xv4kzo3PwnVaYuBgEfg7voPQ5V97aZKgpYBXGck"); + assertEquals((byte) 0x80, privsigningkey[0]); + assertEquals((byte) 0x80, privencryptionkey[0]); + privsigningkey = Bytes.subArray(privsigningkey, 1, privsigningkey.length - 5); + privencryptionkey = Bytes.subArray(privencryptionkey, 1, privencryptionkey.length - 5); + + privsigningkey = Bytes.expand(privsigningkey, 32); + privencryptionkey = Bytes.expand(privencryptionkey, 32); + + BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey, + Security.createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000))); + assertEquals("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn", address.getAddress()); + } + + @Test + public void testV4Import() { + assertEquals(4, new BitmessageAddress("BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke").getVersion()); + byte[] privsigningkey = Base58.decode("5KMWqfCyJZGFgW6QrnPJ6L9Gatz25B51y7ErgqNr1nXUVbtZbdU"); + byte[] privencryptionkey = Base58.decode("5JXXWEuhHQEPk414SzEZk1PHDRi8kCuZd895J7EnKeQSahJPxGz"); + BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey, + Security.createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000))); + assertEquals("BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke", address.getAddress()); + } }