From 6b3b361aa332810881d636ebb33b3c10775429ff Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 22 May 2015 20:51:57 +0200 Subject: [PATCH] A simple command line application (WIP), and a few tests. Unfotrunately, receiving messages doesn't seem to work yet. --- .../dissem/bitmessage/demo/Application.java | 291 ++++++++++++++++++ .../java/ch/dissem/bitmessage/demo/Main.java | 39 +-- .../dissem/bitmessage/BitmessageContext.java | 28 +- .../bitmessage/DefaultMessageListener.java | 34 +- .../ch/dissem/bitmessage/InternalContext.java | 48 +++ .../bitmessage/entity/BitmessageAddress.java | 16 + .../bitmessage/entity/NetworkMessage.java | 14 +- .../dissem/bitmessage/entity/Plaintext.java | 71 ++++- .../bitmessage/entity/payload/CryptoBox.java | 9 +- .../bitmessage/entity/payload/Pubkey.java | 6 + .../bitmessage/entity/payload/V3Pubkey.java | 22 +- .../bitmessage/entity/payload/V4Pubkey.java | 40 ++- .../entity/valueobject/PrivateKey.java | 24 +- .../ch/dissem/bitmessage/factory/Factory.java | 18 +- .../ch/dissem/bitmessage/ports/Inventory.java | 2 +- .../ch/dissem/bitmessage/utils/Security.java | 3 +- .../ch/dissem/bitmessage/utils/Strings.java | 11 + .../ch/dissem/bitmessage/EncryptionTest.java | 27 +- .../ch/dissem/bitmessage/SignatureTest.java | 28 +- .../entity/BitmessageAddressTest.java | 1 - .../bitmessage/entity/SerializationTest.java | 17 + .../dissem/bitmessage/utils/SecurityTest.java | 11 +- .../ch/dissem/bitmessage/utils/TestUtils.java | 24 ++ ...2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey | Bin 0 -> 272 bytes domain/src/test/resources/V1Msg.payload | Bin 1324 -> 444 bytes .../repository/JdbcAddressRepository.java | 32 +- .../bitmessage/repository/JdbcHelper.java | 17 +- .../bitmessage/repository/JdbcInventory.java | 25 +- .../repository/JdbcMessageRepository.java | 16 +- .../repository/JdbcNodeRegistry.java | 2 +- .../repository/SimpleInventory.java | 61 ---- .../repository/SimpleNodeRegistry.java | 37 --- .../db/migration/V1.0__Create_node_table.sql | 12 +- .../V1.1__Create_inventory_table.sql | 12 +- .../migration/V1.2__Create_address_table.sql | 10 +- .../migration/V1.3__Create_message_table.sql | 47 +-- 36 files changed, 757 insertions(+), 298 deletions(-) create mode 100644 demo/src/main/java/ch/dissem/bitmessage/demo/Application.java create mode 100644 domain/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey delete mode 100644 repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleInventory.java delete mode 100644 repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleNodeRegistry.java diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java new file mode 100644 index 0000000..f0aff97 --- /dev/null +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -0,0 +1,291 @@ +package ch.dissem.bitmessage.demo; + +import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.networking.NetworkNode; +import ch.dissem.bitmessage.repository.JdbcAddressRepository; +import ch.dissem.bitmessage.repository.JdbcInventory; +import ch.dissem.bitmessage.repository.JdbcMessageRepository; +import ch.dissem.bitmessage.repository.JdbcNodeRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.Scanner; + +/** + * A simple command line Bitmessage application + */ +public class Application { + private final static Logger LOG = LoggerFactory.getLogger(Application.class); + private final Scanner scanner; + + private BitmessageContext ctx; + + public Application() { + ctx = new BitmessageContext.Builder() + .addressRepo(new JdbcAddressRepository()) + .inventory(new JdbcInventory()) + .nodeRegistry(new JdbcNodeRegistry()) + .networkHandler(new NetworkNode()) + .messageRepo(new JdbcMessageRepository()) + .port(48444) + .streams(1) + .build(); + + ctx.startup(new BitmessageContext.Listener() { + @Override + public void receive(Plaintext plaintext) { + try { + System.out.println(new String(plaintext.getMessage(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + LOG.error(e.getMessage(), e); + } + } + }); + + scanner = new Scanner(System.in); + + String command; + do { + System.out.println(); + System.out.println("available commands:"); + System.out.println("i) identities"); + System.out.println("c) contacts"); + System.out.println("m) messages"); + System.out.println("e) Exit"); + + command = nextCommand(); + try { + switch (command) { + case "i": { + identities(); + break; + } + case "c": + contacts(); + break; + case "m": + messages(); + break; + case "e": + break; + default: + System.out.println("Unknown command. Please try again."); + } + } catch (Exception e) { + LOG.debug(e.getMessage()); + } + } while (!"e".equals(command)); + LOG.info("Shutting down client"); + ctx.shutdown(); + } + + private String nextCommand() { + return scanner.nextLine().trim().toLowerCase(); + } + + private void identities() { + String command; + List identities = ctx.addresses().getIdentities(); + do { + System.out.println(); + int i = 0; + for (BitmessageAddress identity : identities) { + i++; + System.out.print(i + ") "); + if (identity.getAlias() != null) { + System.out.println(identity.getAlias() + " (" + identity.getAddress() + ")"); + } else { + System.out.println(identity.getAddress()); + } + } + if (i == 0) { + System.out.println("You have no identities yet."); + } + System.out.println("a) create identity"); + System.out.println("b) back"); + + command = nextCommand(); + switch (command) { + case "a": + addIdentity(); + break; + case "b": + return; + default: + try { + int index = Integer.parseInt(command) - 1; + address(identities.get(index)); + } catch (NumberFormatException e) { + System.out.println("Unknown command. Please try again."); + } + } + } while (!"b".equals(command)); + } + + private void addIdentity() { + System.out.println(); + BitmessageAddress identity = ctx.createIdentity(yesNo("would you like a shorter address? This will take some time to calculate."), Pubkey.Feature.DOES_ACK); + System.out.println("Please enter an alias for this identity, or an empty string for none"); + String alias = nextCommand(); + if (alias.length() > 0) { + identity.setAlias(alias); + } + ctx.addresses().save(identity); + } + + private void contacts() { + String command; + List contacts = ctx.addresses().getContacts(); + do { + System.out.println(); + int i = 0; + for (BitmessageAddress contact : contacts) { + i++; + System.out.print(i + ") "); + if (contact.getAlias() != null) { + System.out.println(contact.getAlias() + " (" + contact.getAddress() + ")"); + } else { + System.out.println(contact.getAddress()); + } + } + if (i == 0) { + System.out.println("You have no contacts yet."); + } + System.out.println(); + System.out.println("a) add contact"); + System.out.println("b) back"); + + command = nextCommand(); + switch (command) { + case "a": + addContact(); + break; + case "b": + return; + default: + try { + int index = Integer.parseInt(command) - 1; + address(contacts.get(index)); + } catch (NumberFormatException e) { + System.out.println("Unknown command. Please try again."); + } + } + } while (!"b".equals(command)); + } + + private void addContact() { + System.out.println(); + System.out.println("Please enter the Bitmessage address you want to add"); + try { + BitmessageAddress address = new BitmessageAddress(scanner.nextLine().trim()); + System.out.println("Please enter an alias for this address, or an empty string for none"); + String alias = scanner.nextLine().trim(); + if (alias.length() > 0) { + address.setAlias(alias); + } + ctx.addContact(address); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + + private void address(BitmessageAddress address) { + System.out.println(); + if (address.getAlias() != null) + System.out.println(address.getAlias()); + System.out.println(address.getAddress()); + System.out.println("Stream: " + address.getStream()); + System.out.println("Version: " + address.getVersion()); + + } + + private void messages() { + String command; + List messages = ctx.messages().findMessages(Plaintext.Status.RECEIVED); + do { + System.out.println(); + int i = 0; + for (Plaintext message : messages) { + i++; + System.out.print(i + ") From: " + message.getFrom() + "; Subject: " + message.getSubject()); + } + if (i == 0) { + System.out.println("You have no messages."); + } + System.out.println(); + System.out.println("c) compose message"); + System.out.println("b) back"); + + command = scanner.nextLine().trim(); + switch (command) { + case "c": + compose(); + break; + case "b": + return; + default: + try { + int index = Integer.parseInt(command); + show(messages.get(index)); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + System.out.println("Unknown command. Please try again."); + } + } + } while (!"b".equalsIgnoreCase(command)); + } + + private void show(Plaintext message) { + System.out.println(); + System.out.println("From: " + message.getFrom()); + System.out.println("To: " + message.getTo()); + System.out.println("Subject: " + message.getSubject()); + System.out.println(); + System.out.println(message.getText()); + System.out.println(); + String command; + do { + System.out.printf("r) reply"); + System.out.println("d) delete"); + System.out.printf("b) back"); + command = nextCommand(); + switch (command) { + case "r": + compose(message.getTo(), message.getFrom(), "RE: " + message.getSubject()); + break; + case "d": + ctx.messages().remove(message); + case "b": + return; + default: + System.out.println("Unknown command. Please try again."); + } + } while (!"b".equalsIgnoreCase(command)); + } + + private void compose() { + System.out.println(); + System.out.println("TODO"); + // TODO + } + + private void compose(BitmessageAddress from, BitmessageAddress to, String subject) { + System.out.println(); + System.out.println("TODO"); + // TODO + } + + private boolean yesNo(String question) { + String answer; + do { + System.out.println(question + " (y/n)"); + answer = scanner.nextLine(); + if ("y".equalsIgnoreCase(answer)) return true; + if ("n".equalsIgnoreCase(answer)) return false; + } while (true); + } +} diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java index 4fc9e9d..1e7344b 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -18,11 +18,12 @@ package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.networking.NetworkNode; import ch.dissem.bitmessage.repository.JdbcAddressRepository; import ch.dissem.bitmessage.repository.JdbcInventory; import ch.dissem.bitmessage.repository.JdbcMessageRepository; import ch.dissem.bitmessage.repository.JdbcNodeRegistry; -import ch.dissem.bitmessage.networking.NetworkNode; import ch.dissem.bitmessage.utils.Base58; import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Security; @@ -31,48 +32,18 @@ import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Scanner; /** * Created by chris on 06.04.15. */ public class Main { - private final static Logger LOG = LoggerFactory.getLogger(Main.class); public static void main(String[] args) throws IOException { final BitmessageAddress address = new BitmessageAddress("BM-87hJ99tPAXxtetvnje7Z491YSvbEtBJVc5e"); - BitmessageContext ctx = new BitmessageContext.Builder() - .addressRepo(new JdbcAddressRepository()) - .inventory(new JdbcInventory()) - .nodeRegistry(new JdbcNodeRegistry()) - .networkHandler(new NetworkNode()) - .messageRepo(new JdbcMessageRepository()) - .port(48444) - .streams(1) - .build(); - -// ctx.startup(new BitmessageContext.Listener() { -// @Override -// public void receive(Plaintext plaintext) { -// // TODO -// try { -// System.out.println(new String(plaintext.getMessage(), "UTF-8")); -// } catch (UnsupportedEncodingException e) { -// LOG.error(e.getMessage(), e); -// } -// } -// }); - - -// Scanner scanner = new Scanner(System.in); -//// System.out.println("Press Enter to request pubkey for address " + address); -//// scanner.nextLine(); -//// ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000); -// -// System.out.println("Press Enter to exit"); -// scanner.nextLine(); -// LOG.info("Shutting down client"); -// ctx.shutdown(); + new Application(); // // // List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY); diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index c408b59..a592c4c 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -17,16 +17,17 @@ package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.BitmessageAddress; -import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext.Encoding; import ch.dissem.bitmessage.entity.payload.GetPubkey; 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.Pubkey.Feature; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.ports.*; +import ch.dissem.bitmessage.ports.NetworkHandler.MessageListener; import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; @@ -34,10 +35,12 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Collection; -import java.util.List; import java.util.TreeSet; import static ch.dissem.bitmessage.entity.Plaintext.Status.*; +import static ch.dissem.bitmessage.entity.payload.ObjectType.GET_PUBKEY; +import static ch.dissem.bitmessage.entity.payload.ObjectType.MSG; +import static ch.dissem.bitmessage.entity.payload.ObjectType.PUBKEY; import static ch.dissem.bitmessage.utils.UnixTime.DAY; /** @@ -53,8 +56,12 @@ public class BitmessageContext { ctx = new InternalContext(builder); } - public List<BitmessageAddress> getIdentities() { - return ctx.getAddressRepo().getIdentities(); + public AddressRepository addresses() { + return ctx.getAddressRepo(); + } + + public MessageRepository messages() { + return ctx.getMessageRepository(); } public BitmessageAddress createIdentity(boolean shorter, Feature... features) { @@ -66,6 +73,7 @@ public class BitmessageContext { features )); ctx.getAddressRepo().save(identity); + ctx.sendPubkey(identity, identity.getStream()); return identity; } @@ -134,13 +142,23 @@ public class BitmessageContext { } public void startup(Listener listener) { - ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener)); + MessageListener messageListener = new DefaultMessageListener(ctx, listener); + for (ObjectMessage object : ctx.getInventory().getObjects(0, 0, PUBKEY, MSG)) { + messageListener.receive(object); + } + ctx.getNetworkHandler().start(messageListener); } public void shutdown() { ctx.getNetworkHandler().stop(); } + public void addContact(BitmessageAddress contact) { + ctx.getAddressRepo().save(contact); + // TODO: search pubkey in inventory + ctx.requestPubkey(contact); + } + public interface Listener { void receive(Plaintext plaintext); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java index dad500b..81b7330 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java +++ b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java @@ -17,13 +17,10 @@ package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.BitmessageAddress; -import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.ports.NetworkHandler; -import ch.dissem.bitmessage.utils.Security; -import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +44,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { @Override public void receive(ObjectMessage object) { ObjectPayload payload = object.getPayload(); + if (payload.getType() == null) return; + switch (payload.getType()) { case GET_PUBKEY: { receive(object, (GetPubkey) payload); @@ -70,28 +69,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { protected void receive(ObjectMessage object, GetPubkey getPubkey) { BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag()); if (identity != null && identity.getPrivateKey() != null) { - try { - long expires = UnixTime.now(+28 * DAY); - LOG.info("Expires at " + expires); - ObjectMessage response = new ObjectMessage.Builder() - .stream(object.getStream()) - .version(identity.getVersion()) - .expiresTime(expires) - .payload(identity.getPubkey()) - .build(); - Security.doProofOfWork(response, ctx.getProofOfWorkEngine(), - ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); - if (response.isSigned()) { - response.sign(identity.getPrivateKey()); - } - if (response instanceof Encrypted) { - response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false)); - } - ctx.getInventory().storeObject(response); - ctx.getNetworkHandler().offer(response.getInventoryVector()); - } catch (IOException e) { - throw new RuntimeException(e); - } + LOG.debug("Got pubkey request for identity " + identity); + ctx.sendPubkey(identity, object.getStream()); } } @@ -109,8 +88,9 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { } if (address != null) { address.setPubkey(pubkey); + LOG.debug("Got pubkey for contact " + address); List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); - for (Plaintext msg:messages){ + for (Plaintext msg : messages) { // TODO: send messages enqueued for this address msg.setStatus(DOING_PROOF_OF_WORK); ctx.getMessageRepository().save(msg); @@ -140,7 +120,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { ctx.getMessageRepository().save(msg.getPlaintext()); listener.receive(msg.getPlaintext()); break; - } catch (IOException ignore) { + } catch (IOException | RuntimeException ignore) { } } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 4d51423..1ebf86d 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -19,6 +19,7 @@ package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.GetPubkey; import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.utils.Security; @@ -29,6 +30,8 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.TreeSet; +import static ch.dissem.bitmessage.utils.UnixTime.DAY; + /** * The internal context should normally only be used for port implementations. If you need it in your client * implementation, you're either doing something wrong, something very weird, or the BitmessageContext should @@ -160,6 +163,51 @@ public class InternalContext { } } + public void sendPubkey(BitmessageAddress identity, long targetStream) { + try { + long expires = UnixTime.now(+28 * DAY); + LOG.info("Expires at " + expires); + ObjectMessage response = new ObjectMessage.Builder() + .stream(targetStream) + .version(identity.getVersion()) + .expiresTime(expires) + .payload(identity.getPubkey()) + .build(); + response.sign(identity.getPrivateKey()); + response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false)); + Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); + if (response.isSigned()) { + response.sign(identity.getPrivateKey()); + } + if (response instanceof Encrypted) { + response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false)); + } + inventory.storeObject(response); + networkHandler.offer(response.getInventoryVector()); + // TODO: save that the pubkey was just sent, and on which stream! + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void requestPubkey(BitmessageAddress contact) { + try { + long expires = UnixTime.now(+2 * DAY); + LOG.info("Expires at " + expires); + ObjectMessage response = new ObjectMessage.Builder() + .stream(contact.getStream()) + .version(contact.getVersion()) + .expiresTime(expires) + .payload(new GetPubkey(contact)) + .build(); + Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); + inventory.storeObject(response); + networkHandler.offer(response.getInventoryVector()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public interface ContextHolder { void setContext(InternalContext context); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java index 83303b7..767b709 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; +import java.util.Objects; import static ch.dissem.bitmessage.utils.Decode.bytes; import static ch.dissem.bitmessage.utils.Decode.varInt; @@ -180,4 +181,19 @@ public class BitmessageAddress { public byte[] getTag() { return tag; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BitmessageAddress address = (BitmessageAddress) o; + return Objects.equals(version, address.version) && + Objects.equals(stream, address.stream) && + Arrays.equals(ripe, address.ripe); + } + + @Override + public int hashCode() { + return Arrays.hashCode(ripe); + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java index 8c2fb29..c10f942 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java @@ -60,15 +60,15 @@ public class NetworkMessage implements Streamable { } @Override - public void write(OutputStream stream) throws IOException { + public void write(OutputStream out) throws IOException { // magic - Encode.int32(MAGIC, stream); + Encode.int32(MAGIC, out); // ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected) String command = payload.getCommand().name().toLowerCase(); - stream.write(command.getBytes("ASCII")); + out.write(command.getBytes("ASCII")); for (int i = command.length(); i < 12; i++) { - stream.write('\0'); + out.write('\0'); } ByteArrayOutputStream payloadStream = new ByteArrayOutputStream(); @@ -78,16 +78,16 @@ public class NetworkMessage implements Streamable { // Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would // ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are // larger than this. - Encode.int32(payloadBytes.length, stream); + Encode.int32(payloadBytes.length, out); // checksum try { - stream.write(getChecksum(payloadBytes)); + out.write(getChecksum(payloadBytes)); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } // message payload - stream.write(payloadBytes); + out.write(payloadBytes); } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java index b8b8f52..c91f5f6 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -22,10 +22,7 @@ import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Encode; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.util.*; /** @@ -121,8 +118,8 @@ public class Plaintext implements Streamable { Encode.varInt(from.getVersion(), out); Encode.varInt(from.getStream(), out); Encode.int32(from.getPubkey().getBehaviorBitfield(), out); - out.write(from.getPubkey().getSigningKey()); - out.write(from.getPubkey().getEncryptionKey()); + out.write(from.getPubkey().getSigningKey(), 1, 64); + out.write(from.getPubkey().getEncryptionKey(), 1, 64); Encode.varInt(from.getPubkey().getNonceTrialsPerByte(), out); Encode.varInt(from.getPubkey().getExtraBytes(), out); out.write(to.getRipe()); @@ -167,6 +164,30 @@ public class Plaintext implements Streamable { this.status = status; } + public String getSubject() { + Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8"); + String firstLine = s.nextLine(); + if (encoding == 2) { + return firstLine.substring("Subject:".length()).trim(); + } else if (firstLine.length() > 50) { + return firstLine.substring(0, 50).trim() + "..."; + } else { + return firstLine; + } + } + + public String getText() { + try { + String text = new String(message, "UTF-8"); + if (encoding == 2) { + return text.substring(text.indexOf("\nBody:") + 6); + } + return text; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + public enum Encoding { IGNORE(0), TRIVIAL(1), SIMPLE(2); @@ -176,23 +197,21 @@ public class Plaintext implements Streamable { this.code = code; } - public static Encoding fromCode(long code) { - for (Encoding e : values()) { - if (e.getCode() == code) return e; - } - return null; - } - public long getCode() { return code; } } public enum Status { + // For sent messages PUBKEY_REQUESTED, DOING_PROOF_OF_WORK, SENT, - ACKNOWLEDGED + SENT_ACKNOWLEDGED, + + // For received messages + RECEIVED, + READ } public static final class Builder { @@ -340,7 +359,7 @@ public class Plaintext implements Streamable { publicEncryptionKey, nonceTrialsPerByte, extraBytes, - Pubkey.Feature.features(behaviorBitfield) + behaviorBitfield )); } if (to == null) { @@ -349,4 +368,26 @@ public class Plaintext implements Streamable { return new Plaintext(this); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Plaintext plaintext = (Plaintext) o; + return Objects.equals(encoding, plaintext.encoding) && + Objects.equals(from, plaintext.from) && + Arrays.equals(message, plaintext.message) && + Arrays.equals(ack, plaintext.ack) && + Arrays.equals(to.getRipe(), plaintext.to.getRipe()) && + Arrays.equals(signature, plaintext.signature) && + Objects.equals(status, plaintext.status) && + Objects.equals(sent, plaintext.sent) && + Objects.equals(received, plaintext.received) && + Objects.equals(labels, plaintext.labels); + } + + @Override + public int hashCode() { + return Objects.hash(from, encoding, message, ack, to, signature, status, sent, received, labels); + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java index bb606cb..d4cbfdb 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java @@ -28,16 +28,17 @@ import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.math.ec.ECPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.*; import java.math.BigInteger; import java.util.Arrays; -/** - * Created by chris on 09.04.15. - */ public class CryptoBox implements Streamable { + private static final Logger LOG = LoggerFactory.getLogger(CryptoBox.class); + private final byte[] initializationVector; private final int curveType; private final ECPoint R; @@ -182,7 +183,7 @@ public class CryptoBox implements Streamable { } public Builder curveType(int curveType) { - if (curveType != 0x2CA) System.out.println("Unexpected curve type " + curveType); + if (curveType != 0x2CA) LOG.debug("Unexpected curve type " + curveType); this.curveType = curveType; return this; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java index 0e1736c..3438ea3 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java @@ -16,6 +16,8 @@ package ch.dissem.bitmessage.entity.payload; +import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; import static ch.dissem.bitmessage.utils.Security.ripemd160; @@ -51,6 +53,10 @@ public abstract class Pubkey extends ObjectPayload { return 0; } + public void writeUnencrypted(OutputStream out) throws IOException { + write(out); + } + protected byte[] add0x04(byte[] key) { if (key.length == 65) return key; byte[] result = new byte[65]; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java index 1a12ed6..f36e2d6 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java @@ -22,6 +22,8 @@ import ch.dissem.bitmessage.utils.Encode; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; +import java.util.Objects; /** * A version 3 public key. @@ -100,7 +102,7 @@ public class V3Pubkey extends V2Pubkey { private byte[] publicEncryptionKey; private long nonceTrialsPerByte; private long extraBytes; - private byte[] signature; + private byte[] signature = new byte[0]; public Builder() { } @@ -144,4 +146,22 @@ public class V3Pubkey extends V2Pubkey { return new V3Pubkey(this); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + V3Pubkey pubkey = (V3Pubkey) o; + return Objects.equals(nonceTrialsPerByte, pubkey.nonceTrialsPerByte) && + Objects.equals(extraBytes, pubkey.extraBytes) && + stream == pubkey.stream && + behaviorBitfield == pubkey.behaviorBitfield && + Arrays.equals(publicSigningKey, pubkey.publicSigningKey) && + Arrays.equals(publicEncryptionKey, pubkey.publicEncryptionKey); + } + + @Override + public int hashCode() { + return Objects.hash(nonceTrialsPerByte, extraBytes); + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java index e3ba31b..1b2f579 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java @@ -23,6 +23,7 @@ import ch.dissem.bitmessage.utils.Decode; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; /** * A version 4 public key. When version 4 pubkeys are created, most of the data in the pubkey is encrypted. This is @@ -43,15 +44,18 @@ public class V4Pubkey extends Pubkey implements Encrypted { } public V4Pubkey(V3Pubkey decrypted) { + this.decrypted = decrypted; this.stream = decrypted.stream; this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe()); - this.decrypted = decrypted; } - public static V4Pubkey read(InputStream in, long stream, int length) throws IOException { - return new V4Pubkey(stream, - Decode.bytes(in, 32), - CryptoBox.read(in, length - 32)); + public static V4Pubkey read(InputStream in, long stream, int length, boolean encrypted) throws IOException { + if (encrypted) + return new V4Pubkey(stream, + Decode.bytes(in, 32), + CryptoBox.read(in, length - 32)); + else + return new V4Pubkey(V3Pubkey.read(in, stream)); } @Override @@ -76,6 +80,11 @@ public class V4Pubkey extends Pubkey implements Encrypted { encrypted.write(stream); } + @Override + public void writeUnencrypted(OutputStream out) throws IOException { + decrypted.write(out); + } + @Override public void writeBytesToSign(OutputStream out) throws IOException { out.write(tag); @@ -141,4 +150,25 @@ public class V4Pubkey extends Pubkey implements Encrypted { public long getExtraBytes() { return decrypted.getExtraBytes(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + V4Pubkey v4Pubkey = (V4Pubkey) o; + + if (stream != v4Pubkey.stream) return false; + if (!Arrays.equals(tag, v4Pubkey.tag)) return false; + return !(decrypted != null ? !decrypted.equals(v4Pubkey.decrypted) : v4Pubkey.decrypted != null); + + } + + @Override + public int hashCode() { + int result = (int) (stream ^ (stream >>> 32)); + result = 31 * result + Arrays.hashCode(tag); + result = 31 * result + (decrypted != null ? decrypted.hashCode() : 0); + return result; + } } 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 5fb7a39..d2482a1 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 @@ -18,6 +18,8 @@ package ch.dissem.bitmessage.entity.valueobject; import ch.dissem.bitmessage.entity.Streamable; import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.payload.V3Pubkey; +import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Decode; @@ -76,7 +78,7 @@ public class PrivateKey implements Streamable { int version = (int) Decode.varInt(is); long stream = Decode.varInt(is); int len = (int) Decode.varInt(is); - Pubkey pubkey = Factory.readPubkey(version, stream, is, len); + Pubkey pubkey = Factory.readPubkey(version, stream, is, len, false); len = (int) Decode.varInt(is); byte[] signingKey = Decode.bytes(is, len); len = (int) Decode.varInt(is); @@ -97,16 +99,16 @@ public class PrivateKey implements Streamable { } @Override - public void write(OutputStream os) throws IOException { - Encode.varInt(pubkey.getVersion(), os); - Encode.varInt(pubkey.getStream(), os); + public void write(OutputStream out) throws IOException { + Encode.varInt(pubkey.getVersion(), out); + Encode.varInt(pubkey.getStream(), out); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - pubkey.write(baos); - Encode.varInt(baos.size(), os); - os.write(baos.toByteArray()); - Encode.varInt(privateSigningKey.length, os); - os.write(privateSigningKey); - Encode.varInt(privateEncryptionKey.length, os); - os.write(privateEncryptionKey); + pubkey.writeUnencrypted(baos); + Encode.varInt(baos.size(), out); + out.write(baos.toByteArray()); + Encode.varInt(privateSigningKey.length, out); + out.write(privateSigningKey); + Encode.varInt(privateEncryptionKey.length, out); + out.write(privateEncryptionKey); } } 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 fb1d5cf..bdf7170 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -56,6 +56,12 @@ public class Factory { public static Pubkey createPubkey(long version, long stream, byte[] publicSigningKey, byte[] publicEncryptionKey, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { + return createPubkey(version, stream, publicSigningKey, publicEncryptionKey, nonceTrialsPerByte, extraBytes, + Pubkey.Feature.bitfield(features)); + } + + public static Pubkey createPubkey(long version, long stream, byte[] publicSigningKey, byte[] publicEncryptionKey, + long nonceTrialsPerByte, long extraBytes, int behaviourBitfield) { if (publicSigningKey.length != 64 && publicSigningKey.length != 65) throw new IllegalArgumentException("64 bytes signing key expected, but it was " + publicSigningKey.length + " bytes long."); @@ -69,14 +75,14 @@ public class Factory { .stream(stream) .publicSigningKey(publicSigningKey) .publicEncryptionKey(publicEncryptionKey) - .behaviorBitfield(Pubkey.Feature.bitfield(features)) + .behaviorBitfield(behaviourBitfield) .build(); case 3: return new V3Pubkey.Builder() .stream(stream) .publicSigningKey(publicSigningKey) .publicEncryptionKey(publicEncryptionKey) - .behaviorBitfield(Pubkey.Feature.bitfield(features)) + .behaviorBitfield(behaviourBitfield) .nonceTrialsPerByte(nonceTrialsPerByte) .extraBytes(extraBytes) .build(); @@ -86,7 +92,7 @@ public class Factory { .stream(stream) .publicSigningKey(publicSigningKey) .publicEncryptionKey(publicEncryptionKey) - .behaviorBitfield(Pubkey.Feature.bitfield(features)) + .behaviorBitfield(behaviourBitfield) .nonceTrialsPerByte(nonceTrialsPerByte) .extraBytes(extraBytes) .build() @@ -125,21 +131,21 @@ public class Factory { return GetPubkey.read(stream, streamNumber, length, version); } - public static Pubkey readPubkey(long version, long stream, InputStream is, int length) throws IOException { + public static Pubkey readPubkey(long version, long stream, InputStream is, int length, boolean encrypted) throws IOException { switch ((int) version) { case 2: return V2Pubkey.read(is, stream); case 3: return V3Pubkey.read(is, stream); case 4: - return V4Pubkey.read(is, stream, length); + return V4Pubkey.read(is, stream, length, encrypted); } LOG.debug("Unexpected pubkey version " + version + ", handling as generic payload object"); return null; } private static ObjectPayload parsePubkey(long version, long streamNumber, InputStream stream, int length) throws IOException { - Pubkey pubkey = readPubkey(version, streamNumber, stream, length); + Pubkey pubkey = readPubkey(version, streamNumber, stream, length, true); return pubkey != null ? pubkey : GenericPayload.read(stream, streamNumber, length); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java index 31e0e8d..934a4d0 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java @@ -32,7 +32,7 @@ public interface Inventory { ObjectMessage getObject(InventoryVector vector); - List<ObjectMessage> getObjects(long stream, long version, ObjectType type); + List<ObjectMessage> getObjects(long stream, long version, ObjectType... types); void storeObject(ObjectMessage object); 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 f5413f4..6364753 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java @@ -17,7 +17,7 @@ package ch.dissem.bitmessage.utils; import ch.dissem.bitmessage.entity.ObjectMessage; -import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -35,6 +35,7 @@ import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.math.BigInteger; import java.security.*; +import java.security.PrivateKey; import java.security.spec.KeySpec; import java.util.Arrays; diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java index 8529f4e..51f89cc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java @@ -16,6 +16,8 @@ package ch.dissem.bitmessage.utils; +import ch.dissem.bitmessage.entity.payload.ObjectType; + /** * Created by chris on 13.04.15. */ @@ -38,6 +40,15 @@ public class Strings { return streamList; } + public static StringBuilder join(ObjectType... types) { + StringBuilder streamList = new StringBuilder(); + for (int i = 0; i < types.length; i++) { + if (i > 0) streamList.append(", "); + streamList.append(types[i].getNumber()); + } + return streamList; + } + public static StringBuilder join(Object... objects) { StringBuilder streamList = new StringBuilder(); for (int i = 0; i < objects.length; i++) { diff --git a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java index 508d34d..038d528 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java @@ -16,21 +16,23 @@ package ch.dissem.bitmessage; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.CryptoBox; import ch.dissem.bitmessage.entity.payload.GenericPayload; +import ch.dissem.bitmessage.entity.payload.Msg; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.utils.Security; -import org.junit.Ignore; +import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; import java.io.IOException; -import java.security.KeyPair; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; -/** - * Created by chris on 10.05.15. - */ public class EncryptionTest { @Test public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException { @@ -43,4 +45,19 @@ public class EncryptionTest { assertEquals(before, after); } + + @Test + public void ensureMessageCanBeDecrypted() throws IOException { + PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey")); + BitmessageAddress identity = new BitmessageAddress(privateKey); + assertEquals("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8", identity.getAddress()); + + ObjectMessage object = TestUtils.loadObjectMessage(3, "V1Msg.payload"); + Msg msg = (Msg) object.getPayload(); + msg.decrypt(privateKey.getPrivateEncryptionKey()); + Plaintext plaintext = msg.getPlaintext(); + assertNotNull(plaintext); + assertEquals("Test", plaintext.getSubject()); + assertEquals("Hallo, das ist ein Test von der v4-Adresse", plaintext.getText()); + } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java index 7985b4f..6b3a02f 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java @@ -18,15 +18,19 @@ package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.payload.Msg; import ch.dissem.bitmessage.entity.payload.ObjectType; 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.utils.TestUtils; import org.junit.Test; import java.io.IOException; +import java.util.Date; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; public class SignatureTest { @Test @@ -39,7 +43,6 @@ public class SignatureTest { @Test public void ensureSigningWorks() throws IOException { PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000); - BitmessageAddress address = new BitmessageAddress(privateKey); ObjectMessage objectMessage = new ObjectMessage.Builder() .objectType(ObjectType.PUBKEY) @@ -51,4 +54,25 @@ public class SignatureTest { assertTrue(objectMessage.isSignatureValid(privateKey.getPubkey())); } + + @Test + public void ensureMessageIsProperlySigned() throws IOException { + BitmessageAddress identity = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"); + + ObjectMessage object = TestUtils.loadObjectMessage(3, "V1Msg.payload"); + Msg msg = (Msg) object.getPayload(); + msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey()); + Plaintext plaintext = msg.getPlaintext(); + assertEquals(0, object.getExpiresTime()); + assertEquals(loadPubkey(), plaintext.getFrom().getPubkey()); + assertNotNull(plaintext); + assertTrue(object.isSignatureValid(plaintext.getFrom().getPubkey())); + } + + private V4Pubkey loadPubkey() throws IOException { + BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h"); + ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload"); + object.decrypt(address.getPubkeyDecryptionKey()); + return (V4Pubkey) object.getPayload(); + } } 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 609282a..f208b44 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -17,7 +17,6 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.Pubkey; -import ch.dissem.bitmessage.entity.payload.V3Pubkey; import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.utils.*; diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java index 1e941af..a2fa39d 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java @@ -75,6 +75,23 @@ public class SerializationTest { doTest("V1MsgStrangeData.payload", 1, GenericPayload.class); } + @Test + public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws IOException { + Plaintext p1 = new Plaintext.Builder() + .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .to(TestUtils.loadContact()) + .encoding(Plaintext.Encoding.SIMPLE) + .message("Subject", "Message") + .ack("ack".getBytes()) + .signature(new byte[0]) + .build(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + p1.write(out); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Plaintext p2 = Plaintext.read(in); + assertEquals(p1, p2); + } + private void doTest(String resourceName, int version, Class<?> expectedPayloadType) throws IOException { byte[] data = TestUtils.getBytes(resourceName); InputStream in = new ByteArrayInputStream(data); diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java index 064857e..029fe05 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java @@ -25,9 +25,8 @@ import javax.xml.bind.DatatypeConverter; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.KeyPairGenerator; -import java.util.Calendar; -import java.util.GregorianCalendar; +import static ch.dissem.bitmessage.utils.UnixTime.DAY; import static org.junit.Assert.assertArrayEquals; /** @@ -72,7 +71,8 @@ public class SecurityTest { public void testProofOfWorkFails() throws IOException { ObjectMessage objectMessage = new ObjectMessage.Builder() .nonce(new byte[8]) - .expiresTime(UnixTime.now() + 300) // 5 minutes + .expiresTime(UnixTime.now(+2 * DAY)) // 5 minutes + .objectType(0) .payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0)) .build(); Security.checkProofOfWork(objectMessage, 1000, 1000); @@ -80,11 +80,10 @@ public class SecurityTest { @Test public void testDoProofOfWork() throws IOException { - Calendar expires = new GregorianCalendar(); - expires.add(1, Calendar.HOUR); ObjectMessage objectMessage = new ObjectMessage.Builder() .nonce(new byte[8]) - .expiresTime(expires.getTimeInMillis() / 1000) + .expiresTime(UnixTime.now(+2 * DAY)) + .objectType(0) .payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0)) .build(); Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000); diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java b/domain/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java index 53370b5..12f77be 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java @@ -16,7 +16,10 @@ package ch.dissem.bitmessage.utils; +import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.V4Pubkey; +import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.factory.Factory; import java.io.ByteArrayInputStream; @@ -24,6 +27,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import static org.junit.Assert.assertEquals; + /** * If there's ever a need for this in production code, it should be rewritten to be more efficient. */ @@ -51,4 +56,23 @@ public class TestUtils { } return out.toByteArray(); } + + public static InputStream getResource(String resourceName) { + return TestUtils.class.getClassLoader().getResourceAsStream(resourceName); + } + + public static BitmessageAddress loadIdentity(String address) throws IOException { + PrivateKey privateKey = PrivateKey.read(TestUtils.getResource(address + ".privkey")); + BitmessageAddress identity = new BitmessageAddress(privateKey); + assertEquals(address, identity.getAddress()); + return identity; + } + + public static BitmessageAddress loadContact() throws IOException { + BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h"); + ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload"); + object.decrypt(address.getPubkeyDecryptionKey()); + address.setPubkey((V4Pubkey) object.getPayload()); + return address; + } } diff --git a/domain/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey b/domain/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey new file mode 100644 index 0000000000000000000000000000000000000000..31d210c70661a1bc352802de3086a5dd3ec5ead2 GIT binary patch literal 272 zcmV+r0q_0<0gHeD005~-5nUA5hcO<Qnxdb_s?J$+yt40<dv++gz!*<yHLf5C9eYT< zx0E*NiIk*x{FYh*05OKAA~Er6mZXP7j|wW{lnAcNqTXDw;G}@X!hnfG+P%PwwIqkx zZM#Oem{7I^^&1=$?D;6uOEF=^Id-n@L_W$vxrZ81AiqZxA5;AU==}re06?MUuX^Ku z^zK#!ki;t1EeSE@fx1&dj-|6Gh3sjxIcL$#iPi`v9G!bx9;HF^AECFpiJ}w8`$Jj0 zS=mW$k%mB5jCY=Xkpjg8M(nq#5NA3nKDsHX`qZc4qn9U&30BN>6I(3h3rQIFQ%W!_ Who~m53J7+rn24@Ltmn56_!O_&uYu$M literal 0 HcmV?d00001 diff --git a/domain/src/test/resources/V1Msg.payload b/domain/src/test/resources/V1Msg.payload index 22da6c8dc2429276862e488590c4f5c759f18367..ddf21cda61a888ea022b4b8424eb96ee1db39ffe 100644 GIT binary patch literal 444 zcmV;t0Ym-(00008xvxtA00010gc-#E000630k}!J&+7f>uEI8@F)<M3;sVM5AW{Ja z{;1*UtMz4O)sr?YMu97^{!^c06!iQy{9Ok=9{?ch9l298EQ(V75O-+S9!h1*x~vFA zJ$I@8<m<pzWp?fIRZW_3N>kNn;2iFat;p{^0*meHzJ*{^Fy|8tEC&7fCXYCRC6p;- zrwSGz0<?F)O9Uf?=Y(u*Lf@Nc+Vn`!YYO<563VhGrFeHD@p=T1(w;=Hh%}>Ho|#|o zEVfmjrCC-W%N^r5)7#?V5j#CZa%OaP0g&+7Iut0XVpU6GrzwGJMd0*0#;F)6j2?3m zpz&%fvq&-208*9RwkBU10Rk0w2I?uhUA$B$(U5H_f%duH^Cj_3xbpYV?c%<HE-+kA z`#Pb71nW@JujX|pYp04R$~(+7n#qR5Vuz#06@4l%oXSrPIzFL!K|QPj)R`aSAW_6) z0B0I{H{T!2Xy_;F{U@j_6{NoCW1Q*9!^J>~y>8-%+dazRVTC$s5mxXUXECZIKhWTo mu6-%(T=mLftk~cJZQV_Vr9WEM=TwI`1+jj(Azs6mYB0@}ZPQNx literal 1324 zcmV+{1=IQf00005**ww!00010Pol;E000630id{h@GOM9KUK0%7!OwZbppx&ATd~h z{5~PK&3w-8PGgXf$wsLSTOFO05sC+F%^;_u%K#u>V9AswCu8!1*p%Kt8WD3ha7q`J z@=G^WeD_FEUQB3b$XJN53iLmZwLP<bG052{bP$TLhN?LJ-{FaQ`Zj2norbYO_LsyU znOO@`)h`2OGC95(DO%ZQ;k7z$GzA=7P<a$+@=51My)^;VyXN2)h3YB{$^Z`8tN7{l zM#VvwV&_N_90BLZZZrh{l*_UBc-}n6K}*6o@}_ert!PJuF*F}y+d63+vMo4pz+(;G zf?c$+^S&JtOAUI*G@L^JK2=T2;?`kv`T^xt>EExwsFx34HHn%sA_y@w4{L4LXdK6D z#J6~)7|rv~R2Liotg@g&)YDWi`UBO5`k?^i$yWgdY}9>^>a#iH9StUR($SU5H_C^h zJXSI5gCP^%iZ6INyX3YGCe>mxL4HdYLZ~@Z<Y&whxvkbA-1!?=)7^f#M(W1|&r;-R zR<AW(8AXf}7Anx8nbYYC48lEdM!V%k0zOt(ClS<E+B_C4i~!d<HT@x$Je1$(pui8_ z!Of&v&CylAP(nDc&TJuWeqm@zA$NvNq-ZlfOC^!cxGeeOb$;V$cy(BF)sfGJWqAMo z@ySJw(kM(LRq;#dg=~4mQ=!X21+)*4i_Nz>B3--2qQN!^T8O&*C;pZeW;@_*@}sA% zpUukuV>8f-Q%dG{mmT2$<3>S^a;(phnQovh&5Xij(dR8D5nk2;Se(%{cru{^12&4Z zp|?%Y67c+q?IUO1R(1VFyy9Q`oCtqRnpWlHTRzj~)wYDmmubFeGtxfN%0<T`m03XE z6k*Z*7$m9#{@p_2OuGev5LrrXbTO8UZ-t+m_r8US2T%wg6$JfMSH?~rQ|}iKOLi!r zX7wz};C$c2W?|eDH0#BgA=`D~F(r|=z?{me5YD~*9<{7z5&?a0fYK_%wcXb9NRg2< zP7`F7U^Neg4`OvHuFz5w=t|tr5ICvYj|u~2{4&V>q>~#-%bQQ#grv-zlv>{b9PjnI zXHJpYTSaGuu6segoM%Sife>m8v#?Lu2OrJlueyp7d-v|Nf=|by89&PT3bO9Ow5We4 zqg8P}MG0oc%kmRtl~rcPnu`pEu*h#vW@JoTWj)+KO{)8oGu+-`PW#pOY|(@|2I}~` zTsXdSNZV!#HAgLjd2iNb(*sIIL9bmG<D1p%wvKValOs_&E=wjDA5i|3`eCq2krvEg zFNokB;;9Xt0y{ob-=lpQNJa1L)4AYdtQU&EQuFQZ6Rrulb2OWT&}Bkn_du@yq!5lQ z<-@ZwJSVxEIH~e!rCLxmIR!#9quF<9xVk|(6-A0A=)&Q#S^!=Ioe?M{r~OtPlBR_F z3!V$A##}WyU}q^hEU}o75zS$h#T!=tg+xMZ0(={7aTHHM8p6^;k1gdce6!KjF2%{{ zf3dK#1r@g_KPNY{8*COAb`o+10BHt*b8svLL0Re&oIK!<v3W!uFeb^9q7zT#juz&l z_EyB%3^@3i<<Lj9lacRJ%wjg!)>cHO8E`H5#k&>_5bqpR<kfZombiTjco|9kbF!|| zjt6;pf06@)fQf^BKn^DPvD%k@Lm5NPL~>bFgHUQ256)Mg!;T-sZ=B#2`9A*sv%A=^ iaQ6{HefhIHKB`d^Z&NBVo}5s?88hnr>1C8>%GjysyM4s~ diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java index c26a7d4..3efdec3 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java @@ -18,12 +18,16 @@ package ch.dissem.bitmessage.repository; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.payload.V3Pubkey; +import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.AddressRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.sql.*; import java.util.Arrays; @@ -62,7 +66,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito @Override public List<BitmessageAddress> getIdentities() { - return find("private_signing_key IS NOT NULL"); + return find("private_key IS NOT NULL"); } @Override @@ -72,7 +76,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito @Override public List<BitmessageAddress> getContacts() { - return find("private_signing_key IS NULL"); + return find("private_key IS NULL"); } private List<BitmessageAddress> find(String where) { @@ -91,7 +95,10 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito Blob publicKeyBlob = rs.getBlob("public_key"); if (publicKeyBlob != null) { Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), - publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length()); + publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length(), false); + if (address.getVersion() == 4) { + pubkey = new V4Pubkey((V3Pubkey) pubkey); + } address.setPubkey(pubkey); } } @@ -111,7 +118,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito Statement stmt = getConnection().createStatement(); ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'"); rs.next(); - return rs.getInt(0) > 0; + return rs.getInt(1) > 0; } catch (SQLException e) { LOG.error(e.getMessage(), e); } @@ -136,21 +143,32 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito "UPDATE Address SET address=?, alias=?, public_key=?, private_key=?"); ps.setString(1, address.getAddress()); ps.setString(2, address.getAlias()); - writeBlob(ps, 3, address.getPubkey()); + writePubkey(ps, 3, address.getPubkey()); writeBlob(ps, 4, address.getPrivateKey()); ps.executeUpdate(); } private void insert(BitmessageAddress address) throws IOException, SQLException { PreparedStatement ps = getConnection().prepareStatement( - "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?, ?)"); + "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?)"); ps.setString(1, address.getAddress()); ps.setString(2, address.getAlias()); - writeBlob(ps, 3, address.getPubkey()); + writePubkey(ps, 3, address.getPubkey()); writeBlob(ps, 4, address.getPrivateKey()); ps.executeUpdate(); } + protected void writePubkey(PreparedStatement ps, int parameterIndex, Pubkey data) throws SQLException, IOException { + if (data != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + data.writeUnencrypted(out); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + ps.setBlob(parameterIndex, in); + } else { + ps.setBlob(parameterIndex, (Blob) null); + } + } + @Override public void remove(BitmessageAddress address) { try { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java index 693e571..732f276 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -24,10 +24,7 @@ import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; +import java.sql.*; /** * Helper class that does Flyway migration, provides JDBC connections and some helper methods. @@ -47,10 +44,14 @@ abstract class JdbcHelper { } protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - data.write(os); - ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); - ps.setBlob(parameterIndex, is); + if (data != null) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + data.write(os); + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + ps.setBlob(parameterIndex, is); + } else { + ps.setBlob(parameterIndex, (Blob) null); + } } protected Connection getConnection() { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index 1956e17..396e70d 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -52,10 +52,23 @@ public class JdbcInventory extends JdbcHelper implements Inventory { } return result; } + private List<InventoryVector> getFullInventory(long... streams) { + List<InventoryVector> result = new LinkedList<>(); + try { + Statement stmt = getConnection().createStatement(); + ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE stream IN (" + join(streams) + ")"); + while (rs.next()) { + result.add(new InventoryVector(rs.getBytes("hash"))); + } + } catch (SQLException e) { + LOG.error(e.getMessage(), e); + } + return result; + } @Override public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { - offer.removeAll(getInventory(streams)); + offer.removeAll(getFullInventory(streams)); return offer; } @@ -78,17 +91,17 @@ public class JdbcInventory extends JdbcHelper implements Inventory { } @Override - public List<ObjectMessage> getObjects(long stream, long version, ObjectType type) { + public List<ObjectMessage> getObjects(long stream, long version, ObjectType... types) { try { StringBuilder query = new StringBuilder("SELECT data, version FROM Inventory WHERE 1=1"); - if (stream >= 0) { + if (stream > 0) { query.append(" AND stream = ").append(stream); } - if (version >= 0) { + if (version > 0) { query.append(" AND version = ").append(version); } - if (type != null) { - query.append(" AND type = ").append(type.getNumber()); + if (types.length > 0) { + query.append(" AND type IN (").append(join(types)).append(")"); } Statement stmt = getConnection().createStatement(); ResultSet rs = stmt.executeQuery(query.toString()); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index 6c7dfed..3c9aeea 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -41,7 +41,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito List<String> result = new LinkedList<>(); try { Statement stmt = getConnection().createStatement(); - ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY order"); + ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY ord"); while (rs.next()) { result.add(rs.getString("label")); } @@ -58,7 +58,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito @Override public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) { - return find("status='" + status.name() + "' AND to='" + recipient.getAddress() + "'"); + return find("status='" + status.name() + "' AND recipient='" + recipient.getAddress() + "'"); } @Override @@ -70,14 +70,14 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito List<Plaintext> result = new LinkedList<>(); try { Statement stmt = getConnection().createStatement(); - ResultSet rs = stmt.executeQuery("SELECT \"id\", \"from\", \"to\", \"data\", \"sent\", \"received\", \"status\" FROM Message WHERE " + where); + ResultSet rs = stmt.executeQuery("SELECT id, sender, recipient, data, sent, received, status FROM Message WHERE " + where); while (rs.next()) { Blob data = rs.getBlob("data"); Plaintext.Builder builder = Plaintext.readWithoutSignature(data.getBinaryStream()); long id = rs.getLong("id"); builder.id(id); - builder.from(ctx.getAddressRepo().getAddress(rs.getString("from"))); - builder.to(ctx.getAddressRepo().getAddress(rs.getString("to"))); + builder.from(ctx.getAddressRepo().getAddress(rs.getString("sender"))); + builder.to(ctx.getAddressRepo().getAddress(rs.getString("recipient"))); builder.sent(rs.getLong("sent")); builder.received(rs.getLong("received")); builder.status(Plaintext.Status.valueOf(rs.getString("status"))); @@ -94,7 +94,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito List<Label> result = new ArrayList<>(); try { Statement stmt = getConnection().createStatement(); - ResultSet rs = stmt.executeQuery("SELECT \"label\", \"color\" FROM Label WHERE id IN SELECT label_id FROM Message_Label WHERE message_id=" + messageId); + ResultSet rs = stmt.executeQuery("SELECT label, color FROM Label WHERE id IN SELECT label_id FROM Message_Label WHERE message_id=" + messageId); while (rs.next()) { result.add(new Label(rs.getString("label"), rs.getInt("color"))); } @@ -151,7 +151,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito private void insert(Connection connection, Plaintext message) throws SQLException, IOException { PreparedStatement ps = connection.prepareStatement( - "INSERT INTO Message (\"from\", \"to\", \"data\", \"sent\", \"received\", \"status\") VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + "INSERT INTO Message (sender, recipient, data, sent, received, status) VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); ps.setString(1, message.getFrom().getAddress()); ps.setString(2, message.getTo().getAddress()); writeBlob(ps, 3, message); @@ -168,7 +168,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito private void update(Connection connection, Plaintext message) throws SQLException, IOException { PreparedStatement ps = connection.prepareStatement( - "UPDATE Message SET \"sent\"=?, \"received\"=?, \"status\"=?"); + "UPDATE Message SET sent=?, received=?, status=?"); ps.setLong(1, message.getSent()); ps.setLong(2, message.getReceived()); ps.setString(3, message.getStatus().name()); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcNodeRegistry.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcNodeRegistry.java index 1a9bc39..9c2e250 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcNodeRegistry.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcNodeRegistry.java @@ -38,7 +38,7 @@ public class JdbcNodeRegistry extends JdbcHelper implements NodeRegistry { List<NetworkAddress> result = new LinkedList<>(); try { Statement stmt = getConnection().createStatement(); - ResultSet rs = stmt.executeQuery("SELECT * FROM Node WHERE Stream IN (" + join(streams) + ")"); + ResultSet rs = stmt.executeQuery("SELECT * FROM Node WHERE stream IN (" + join(streams) + ")"); while (rs.next()) { // result.add(new NetworkAddress.Builder() // .ipv6(rs.getBytes("ip")) diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleInventory.java deleted file mode 100644 index 32ed44a..0000000 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleInventory.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2015 Christian Basler - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ch.dissem.bitmessage.repository; - -import ch.dissem.bitmessage.entity.ObjectMessage; -import ch.dissem.bitmessage.entity.payload.ObjectType; -import ch.dissem.bitmessage.entity.valueobject.InventoryVector; -import ch.dissem.bitmessage.ports.Inventory; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; - -import java.util.LinkedList; -import java.util.List; - -/** - * Created by chris on 06.04.15. - */ -public class SimpleInventory implements Inventory { - @Override - public List<InventoryVector> getInventory(long... streams) { - return new LinkedList<>(); - } - - @Override - public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { - return offer; - } - - @Override - public ObjectMessage getObject(InventoryVector vector) { - throw new NotImplementedException(); - } - - @Override - public List<ObjectMessage> getObjects(long stream, long version, ObjectType type) { - return new LinkedList<>(); - } - - @Override - public void storeObject(ObjectMessage object) { - throw new NotImplementedException(); - } - - @Override - public void cleanup() { - throw new NotImplementedException(); - } -} diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleNodeRegistry.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleNodeRegistry.java deleted file mode 100644 index d3dadd2..0000000 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/SimpleNodeRegistry.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 Christian Basler - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package ch.dissem.bitmessage.repository; - -import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; -import ch.dissem.bitmessage.ports.NodeRegistry; - -import java.util.Collections; -import java.util.List; - -/** - * Created by chris on 06.04.15. - */ -public class SimpleNodeRegistry implements NodeRegistry { - @Override - public List<NetworkAddress> getKnownAddresses(int limit, long... streams) { - return Collections.singletonList(new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(8444).build()); - } - - @Override - public void offerAddresses(List<NetworkAddress> addresses) { - } -} diff --git a/repositories/src/main/resources/db/migration/V1.0__Create_node_table.sql b/repositories/src/main/resources/db/migration/V1.0__Create_node_table.sql index 32868ea..2472634 100644 --- a/repositories/src/main/resources/db/migration/V1.0__Create_node_table.sql +++ b/repositories/src/main/resources/db/migration/V1.0__Create_node_table.sql @@ -1,9 +1,9 @@ CREATE TABLE Node ( - "ip" BINARY(16) NOT NULL, - "port" INT NOT NULL, - "stream" BIGINT NOT NULL, - "services" BIGINT NOT NULL, - "time" BIGINT NOT NULL, + ip BINARY(16) NOT NULL, + port INT NOT NULL, + stream BIGINT NOT NULL, + services BIGINT NOT NULL, + time BIGINT NOT NULL, - PRIMARY KEY ("ip", "port", "stream") + PRIMARY KEY (ip, port, stream) ); \ No newline at end of file diff --git a/repositories/src/main/resources/db/migration/V1.1__Create_inventory_table.sql b/repositories/src/main/resources/db/migration/V1.1__Create_inventory_table.sql index 0e5d9b5..83653bf 100644 --- a/repositories/src/main/resources/db/migration/V1.1__Create_inventory_table.sql +++ b/repositories/src/main/resources/db/migration/V1.1__Create_inventory_table.sql @@ -1,8 +1,8 @@ CREATE TABLE Inventory ( - "hash" BINARY(32) NOT NULL PRIMARY KEY, - "stream" BIGINT NOT NULL, - "expires" BIGINT NOT NULL, - "data" BLOB NOT NULL, - "type" BIGINT NOT NULL, - "version" BIGINT NOT NULL + hash BINARY(32) NOT NULL PRIMARY KEY, + stream BIGINT NOT NULL, + expires BIGINT NOT NULL, + data BLOB NOT NULL, + type BIGINT NOT NULL, + version BIGINT NOT NULL ); \ No newline at end of file diff --git a/repositories/src/main/resources/db/migration/V1.2__Create_address_table.sql b/repositories/src/main/resources/db/migration/V1.2__Create_address_table.sql index 2898544..821c8be 100644 --- a/repositories/src/main/resources/db/migration/V1.2__Create_address_table.sql +++ b/repositories/src/main/resources/db/migration/V1.2__Create_address_table.sql @@ -1,7 +1,7 @@ CREATE TABLE Address ( - "address" VARCHAR(40) NOT NULL PRIMARY KEY, - "alias" VARCHAR(255), - "public_key" BLOB, - "private_key" BLOB, - "subscribed" BIT DEFAULT '0' + address VARCHAR(40) NOT NULL PRIMARY KEY, + alias VARCHAR(255), + public_key BLOB, + private_key BLOB, + subscribed BIT DEFAULT '0' ); \ No newline at end of file diff --git a/repositories/src/main/resources/db/migration/V1.3__Create_message_table.sql b/repositories/src/main/resources/db/migration/V1.3__Create_message_table.sql index 275d927..80c4cad 100644 --- a/repositories/src/main/resources/db/migration/V1.3__Create_message_table.sql +++ b/repositories/src/main/resources/db/migration/V1.3__Create_message_table.sql @@ -1,32 +1,35 @@ CREATE TABLE Message ( - "id" BIGINT AUTO_INCREMENT PRIMARY KEY, - "from" VARCHAR(40) NOT NULL, - "to" VARCHAR(40) NOT NULL, - "data" BLOB NOT NULL, - "sent" BIGINT, - "received" BIGINT, - "status" VARCHAR(20) NOT NULL + id BIGINT AUTO_INCREMENT PRIMARY KEY, + sender VARCHAR(40) NOT NULL, + recipient VARCHAR(40) NOT NULL, + data BLOB NOT NULL, + sent BIGINT, + received BIGINT, + status VARCHAR(20) NOT NULL, + + FOREIGN KEY (sender) REFERENCES Address (address), + FOREIGN KEY (recipient) REFERENCES Address (address) ); CREATE TABLE Label ( - "id" BIGINT AUTO_INCREMENT PRIMARY KEY, - "label" VARCHAR(255) NOT NULL, - "color" INT, - "order" BIGINT, - CONSTRAINT UC_label UNIQUE ("label"), - CONSTRAINT UC_order UNIQUE ("order") + id BIGINT AUTO_INCREMENT PRIMARY KEY, + label VARCHAR(255) NOT NULL, + color INT, + ord BIGINT, + CONSTRAINT UC_label UNIQUE (label), + CONSTRAINT UC_order UNIQUE (ord) ); CREATE TABLE Message_Label ( - "message_id" BIGINT NOT NULL, - "label_id" BIGINT NOT NULL, + message_id BIGINT NOT NULL, + label_id BIGINT NOT NULL, - PRIMARY KEY ("message_id", "label_id"), - FOREIGN KEY ("message_id") REFERENCES Message ("id"), - FOREIGN KEY ("label_id") REFERENCES Label ("id") + PRIMARY KEY (message_id, label_id), + FOREIGN KEY (message_id) REFERENCES Message (id), + FOREIGN KEY (label_id) REFERENCES Label (id) ); -INSERT INTO Label("label", "order") VALUES ('Inbox', 0); -INSERT INTO Label("label", "order") VALUES ('Sent', 10); -INSERT INTO Label("label", "order") VALUES ('Drafts', 20); -INSERT INTO Label("label", "order") VALUES ('Trash', 100); +INSERT INTO Label(label, ord) VALUES ('Inbox', 0); +INSERT INTO Label(label, ord) VALUES ('Sent', 10); +INSERT INTO Label(label, ord) VALUES ('Drafts', 20); +INSERT INTO Label(label, ord) VALUES ('Trash', 100);