diff --git a/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 9f03926..2b68f57 100644 --- a/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -17,10 +17,7 @@ package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.*; -import ch.dissem.bitmessage.entity.payload.Broadcast; -import ch.dissem.bitmessage.entity.payload.Msg; -import ch.dissem.bitmessage.entity.payload.ObjectPayload; -import ch.dissem.bitmessage.entity.payload.ObjectType; +import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; @@ -117,9 +114,17 @@ public class BitmessageContext { return identity; } - public void addDistributedMailingList(String address, String alias) { - // TODO - throw new ApplicationException("not implemented"); + public BitmessageAddress joinChan(String passphrase, String address) { + BitmessageAddress chan = BitmessageAddress.chan(address, passphrase); + ctx.getAddressRepository().save(chan); + return chan; + } + + public BitmessageAddress createChan(String passphrase) { + // FIXME: hardcoded stream number + BitmessageAddress chan = BitmessageAddress.chan(1, passphrase); + ctx.getAddressRepository().save(chan); + return chan; } public void broadcast(final BitmessageAddress from, final String subject, final String message) { diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java b/core/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java index 62fabc3..ff44d0f 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -57,6 +57,7 @@ public class BitmessageAddress implements Serializable { private String alias; private boolean subscribed; + private boolean chan; BitmessageAddress(long version, long stream, byte[] ripe) { try { @@ -93,6 +94,27 @@ public class BitmessageAddress implements Serializable { this.pubkey = publicKey; } + public BitmessageAddress(String address, String passphrase) { + this(address); + this.privateKey = new PrivateKey(this, passphrase); + if (!Arrays.equals(ripe, privateKey.getPubkey().getRipe())) { + throw new IllegalArgumentException("Wrong address or passphrase"); + } + } + + public static BitmessageAddress chan(String address, String passphrase) { + BitmessageAddress result = new BitmessageAddress(address, passphrase); + result.chan = true; + return result; + } + + public static BitmessageAddress chan(long stream, String passphrase) { + PrivateKey privateKey = new PrivateKey(Pubkey.LATEST_VERSION, stream, passphrase); + BitmessageAddress result = new BitmessageAddress(privateKey); + result.chan = true; + return result; + } + public BitmessageAddress(PrivateKey privateKey) { this(privateKey.getPubkey()); this.privateKey = privateKey; @@ -221,4 +243,8 @@ public class BitmessageAddress implements Serializable { public void setSubscribed(boolean subscribed) { this.subscribed = subscribed; } + + public boolean isChan() { + return chan; + } } diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java index e058308..6348add 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java @@ -16,6 +16,7 @@ package ch.dissem.bitmessage.entity.valueobject; +import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Streamable; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.exception.ApplicationException; @@ -64,14 +65,32 @@ public class PrivateKey implements Streamable { this.pubkey = pubkey; } - public PrivateKey(long version, long stream, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { + public PrivateKey(BitmessageAddress address, String passphrase) { + this(address.getVersion(), address.getStream(), passphrase); + } + + public PrivateKey(long version, long stream, String passphrase) { 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, stream, privateSigningKey, privateEncryptionKey, - nonceTrialsPerByte, extraBytes, features); - } catch (UnsupportedEncodingException e) { + 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; + encryptionKeyNonce += 2; + } while (ripe[0] != 0); + this.privateSigningKey = signingKey; + this.privateEncryptionKey = encryptionKey; + this.pubkey = security().createPubkey(version, stream, privateSigningKey, privateEncryptionKey, 0, 0); + } catch (IOException e) { throw new ApplicationException(e); } } diff --git a/core/src/main/java/ch/dissem/bitmessage/utils/Encode.java b/core/src/main/java/ch/dissem/bitmessage/utils/Encode.java index 2cdc262..f5eac43 100644 --- a/core/src/main/java/ch/dissem/bitmessage/utils/Encode.java +++ b/core/src/main/java/ch/dissem/bitmessage/utils/Encode.java @@ -41,6 +41,34 @@ public class Encode { varInt(value, stream, null); } + public static byte[] varInt(long value) throws IOException { + final byte[] result; + if (value < 0) { + // This is due to the fact that Java doesn't really support unsigned values. + // Please be aware that this might be an error due to a smaller negative value being cast to long. + // Normally, negative values shouldn't occur within the protocol, and I large enough longs + // to being recognized as negatives aren't realistic. + ByteBuffer buffer = ByteBuffer.allocate(9); + buffer.put((byte) 0xff); + result = buffer.putLong(value).array(); + } else if (value < 0xfd) { + result = new byte[]{(byte) value}; + } else if (value <= 0xffffL) { + ByteBuffer buffer = ByteBuffer.allocate(3); + buffer.put((byte) 0xfd); + result = buffer.putShort((short) value).array(); + } else if (value <= 0xffffffffL) { + ByteBuffer buffer = ByteBuffer.allocate(5); + buffer.put((byte) 0xfe); + result = buffer.putInt((int) value).array(); + } else { + ByteBuffer buffer = ByteBuffer.allocate(9); + buffer.put((byte) 0xff); + result = buffer.putLong(value).array(); + } + return result; + } + public static void varInt(long value, OutputStream stream, AccessCounter counter) throws IOException { if (value < 0) { // This is due to the fact that Java doesn't really support unsigned values. @@ -81,7 +109,7 @@ public class Encode { } public static void int16(long value, OutputStream stream, AccessCounter counter) throws IOException { - stream.write(ByteBuffer.allocate(4).putInt((int) value).array(), 2, 2); + stream.write(ByteBuffer.allocate(2).putShort((short) value).array()); inc(counter, 2); } diff --git a/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java b/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java index 890c2c3..a1991f8 100644 --- a/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java +++ b/core/src/test/java/ch/dissem/bitmessage/BitmessageContextTest.java @@ -39,7 +39,7 @@ import static ch.dissem.bitmessage.entity.payload.ObjectType.*; import static ch.dissem.bitmessage.utils.MessageMatchers.object; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; @@ -206,4 +206,21 @@ public class BitmessageContextTest { .build(); ctx.send(msg); } + + @Test + public void ensureChanIsJoined() { + String chanAddress = "BM-2cW67GEKkHGonXKZLCzouLLxnLym3azS8r"; + BitmessageAddress chan = ctx.joinChan("general", chanAddress); + assertNotNull(chan); + assertEquals(chan.getAddress(), chanAddress); + assertTrue(chan.isChan()); + } + + @Test + public void ensureChanIsCreated() { + BitmessageAddress chan = ctx.createChan("test"); + assertNotNull(chan); + assertEquals(chan.getVersion(), Pubkey.LATEST_VERSION); + assertTrue(chan.isChan()); + } } diff --git a/core/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java b/core/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java index 495aea9..9b22589 100644 --- a/core/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/core/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -20,7 +20,10 @@ import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.utils.*; +import ch.dissem.bitmessage.utils.Base58; +import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.Strings; +import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; import java.io.IOException;