diff --git a/api/net/sf/briar/api/protocol/MessageParser.java b/api/net/sf/briar/api/protocol/MessageParser.java deleted file mode 100644 index cd6ea58a85ebfc51a3d07b43bc47e21bd9c3353a..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/protocol/MessageParser.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.sf.briar.api.protocol; - -import java.io.IOException; -import java.security.GeneralSecurityException; - -public interface MessageParser { - - Message parseMessage(byte[] raw) throws IOException, - GeneralSecurityException; -} diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java index 5e06136cf3669dd170144947f95ffc8e0a0120d3..e9fd9d0d27e93b0ad708f2a798d08b6102f173cb 100644 --- a/api/net/sf/briar/api/serial/Reader.java +++ b/api/net/sf/briar/api/serial/Reader.java @@ -59,5 +59,5 @@ public interface Reader { boolean hasUserDefinedTag() throws IOException; int readUserDefinedTag() throws IOException; - void readUserDefinedTag(int i) throws IOException; + void readUserDefinedTag(int tag) throws IOException; } diff --git a/components/net/sf/briar/protocol/BundleReaderImpl.java b/components/net/sf/briar/protocol/BundleReaderImpl.java index 219d131161c1bf29df1594102e9ce53447ca5bb4..2474a5e08553eb55a5e51b005fee88f1c4d25ed7 100644 --- a/components/net/sf/briar/protocol/BundleReaderImpl.java +++ b/components/net/sf/briar/protocol/BundleReaderImpl.java @@ -18,12 +18,9 @@ import net.sf.briar.api.protocol.BundleReader; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Header; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageParser; -import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.Tags; +import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.FormatException; -import net.sf.briar.api.serial.Raw; -import net.sf.briar.api.serial.RawByteArray; import net.sf.briar.api.serial.Reader; class BundleReaderImpl implements BundleReader { @@ -34,19 +31,19 @@ class BundleReaderImpl implements BundleReader { private final PublicKey publicKey; private final Signature signature; private final MessageDigest messageDigest; - private final MessageParser messageParser; + private final MessageReader messageReader; private final HeaderFactory headerFactory; private final BatchFactory batchFactory; private State state = State.START; BundleReaderImpl(Reader reader, PublicKey publicKey, Signature signature, - MessageDigest messageDigest, MessageParser messageParser, + MessageDigest messageDigest, MessageReader messageParser, HeaderFactory headerFactory, BatchFactory batchFactory) { this.reader = reader; this.publicKey = publicKey; this.signature = signature; this.messageDigest = messageDigest; - this.messageParser = messageParser; + this.messageReader = messageParser; this.headerFactory = headerFactory; this.batchFactory = batchFactory; } @@ -88,6 +85,7 @@ class BundleReaderImpl implements BundleReader { // Timestamp reader.readUserDefinedTag(Tags.TIMESTAMP); long timestamp = reader.readInt64(); + if(timestamp < 0L) throw new FormatException(); reader.removeConsumer(signing); // Read and verify the signature reader.readUserDefinedTag(Tags.SIGNATURE); @@ -122,11 +120,11 @@ class BundleReaderImpl implements BundleReader { reader.addConsumer(digesting); reader.addConsumer(signing); reader.readUserDefinedTag(Tags.BATCH); - List<Raw> rawMessages = new ArrayList<Raw>(); + List<Message> messages = new ArrayList<Message>(); reader.readListStart(); while(!reader.hasListEnd()) { reader.readUserDefinedTag(Tags.MESSAGE); - rawMessages.add(new RawByteArray(reader.readRaw())); + messages.add(messageReader.readMessage(reader)); } reader.readListEnd(); reader.removeConsumer(signing); @@ -136,12 +134,6 @@ class BundleReaderImpl implements BundleReader { reader.removeConsumer(digesting); reader.removeConsumer(counting); if(!signature.verify(sig)) throw new SignatureException(); - // Parse the messages - List<Message> messages = new ArrayList<Message>(rawMessages.size()); - for(Raw r : rawMessages) { - Message m = messageParser.parseMessage(r.getBytes()); - messages.add(m); - } // Build and return the batch BatchId id = new BatchId(messageDigest.digest()); return batchFactory.createBatch(id, messages); diff --git a/components/net/sf/briar/protocol/BundleWriterImpl.java b/components/net/sf/briar/protocol/BundleWriterImpl.java index 6454192636f53d0bdb884a582bd2c71f73b14dec..5640faae10416e02a5e3a9d03c3e82b650b52aab 100644 --- a/components/net/sf/briar/protocol/BundleWriterImpl.java +++ b/components/net/sf/briar/protocol/BundleWriterImpl.java @@ -53,13 +53,17 @@ class BundleWriterImpl implements BundleWriter { // Write the data to be signed out.setSigning(true); writer.writeUserDefinedTag(Tags.HEADER); + // Acks writer.writeListStart(); for(BatchId ack : acks) ack.writeTo(writer); writer.writeListEnd(); + // Subs writer.writeListStart(); for(GroupId sub : subs) sub.writeTo(writer); writer.writeListEnd(); + // Transports writer.writeMap(transports); + // Timestamp writer.writeUserDefinedTag(Tags.TIMESTAMP); writer.writeInt64(System.currentTimeMillis()); out.setSigning(false); @@ -88,7 +92,8 @@ class BundleWriterImpl implements BundleWriter { writer.writeListStart(); for(Raw message : messages) { writer.writeUserDefinedTag(Tags.MESSAGE); - writer.writeRaw(message); + // Bypass the writer and write the raw message directly + out.write(message.getBytes()); } writer.writeListEnd(); out.setSigning(false); diff --git a/components/net/sf/briar/protocol/CopyingConsumer.java b/components/net/sf/briar/protocol/CopyingConsumer.java new file mode 100644 index 0000000000000000000000000000000000000000..f84f5a746b9f6652fa10d841df134e9686eaa5ef --- /dev/null +++ b/components/net/sf/briar/protocol/CopyingConsumer.java @@ -0,0 +1,27 @@ +package net.sf.briar.protocol; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import net.sf.briar.api.serial.Consumer; + +public class CopyingConsumer implements Consumer { + + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + byte[] getCopy() { + return out.toByteArray(); + } + + public void write(byte b) throws IOException { + out.write(b); + } + + public void write(byte[] b) throws IOException { + out.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } +} diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java index cc15a7b26c62e2ace246744fb4e633e072019162..58d52b1be405ac7f254d37924c11f8b0a58a3c8f 100644 --- a/components/net/sf/briar/protocol/MessageEncoderImpl.java +++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java @@ -12,6 +12,7 @@ import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageId; +import net.sf.briar.api.protocol.Tags; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; @@ -35,16 +36,20 @@ class MessageEncoderImpl implements MessageEncoder { byte[] encodedKey = keyPair.getPublic().getEncoded(); ByteArrayOutputStream out = new ByteArrayOutputStream(); Writer w = writerFactory.createWriter(out); - w.writeRaw(parent); - w.writeRaw(group); + parent.writeTo(w); + group.writeTo(w); + w.writeUserDefinedTag(Tags.TIMESTAMP); w.writeInt64(timestamp); + w.writeUserDefinedTag(Tags.AUTHOR); w.writeString(nick); w.writeRaw(encodedKey); + w.writeUserDefinedTag(Tags.MESSAGE_BODY); w.writeRaw(body); byte[] signable = out.toByteArray(); signature.initSign(keyPair.getPrivate()); signature.update(signable); byte[] sig = signature.sign(); + w.writeUserDefinedTag(Tags.SIGNATURE); w.writeRaw(sig); byte[] raw = out.toByteArray(); w.close(); @@ -53,10 +58,13 @@ class MessageEncoderImpl implements MessageEncoder { messageDigest.update(raw); MessageId id = new MessageId(messageDigest.digest()); // The author ID is the hash of the author's nick and public key + out.reset(); + w = writerFactory.createWriter(out); + w.writeString(nick); + w.writeRaw(encodedKey); + w.close(); messageDigest.reset(); - messageDigest.update(nick.getBytes("UTF-8")); - messageDigest.update((byte) 0); // Null separator - messageDigest.update(encodedKey); + messageDigest.update(out.toByteArray()); AuthorId author = new AuthorId(messageDigest.digest()); return new MessageImpl(id, parent, group, author, timestamp, raw); } diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java new file mode 100644 index 0000000000000000000000000000000000000000..904df2f90ea650431287db460cd714bccc7ce427 --- /dev/null +++ b/components/net/sf/briar/protocol/MessageReader.java @@ -0,0 +1,13 @@ +package net.sf.briar.protocol; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.serial.Reader; + +interface MessageReader { + + Message readMessage(Reader r) throws IOException, + GeneralSecurityException; +} diff --git a/components/net/sf/briar/protocol/MessageParserImpl.java b/components/net/sf/briar/protocol/MessageReaderImpl.java similarity index 58% rename from components/net/sf/briar/protocol/MessageParserImpl.java rename to components/net/sf/briar/protocol/MessageReaderImpl.java index dbc0d6675d160515896a353bc26437bbe73deaa7..cbc4a19e8e552e254ab4e2da14fe843e9dabcd53 100644 --- a/components/net/sf/briar/protocol/MessageParserImpl.java +++ b/components/net/sf/briar/protocol/MessageReaderImpl.java @@ -1,6 +1,5 @@ package net.sf.briar.protocol; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.MessageDigest; @@ -14,61 +13,63 @@ import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.MessageParser; +import net.sf.briar.api.protocol.Tags; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.FormatException; import net.sf.briar.api.serial.Reader; -import net.sf.briar.api.serial.ReaderFactory; -class MessageParserImpl implements MessageParser { +class MessageReaderImpl implements MessageReader { private final KeyParser keyParser; private final Signature signature; private final MessageDigest messageDigest; - private final ReaderFactory readerFactory; - MessageParserImpl(KeyParser keyParser, Signature signature, - MessageDigest messageDigest, ReaderFactory readerFactory) { + MessageReaderImpl(KeyParser keyParser, Signature signature, + MessageDigest messageDigest) { this.keyParser = keyParser; this.signature = signature; this.messageDigest = messageDigest; - this.readerFactory = readerFactory; } - public Message parseMessage(byte[] raw) throws IOException, + public Message readMessage(Reader reader) throws IOException, GeneralSecurityException { - if(raw.length > Message.MAX_SIZE) throw new FormatException(); - ByteArrayInputStream in = new ByteArrayInputStream(raw); - Reader r = readerFactory.createReader(in); + CopyingConsumer copying = new CopyingConsumer(); CountingConsumer counting = new CountingConsumer(Message.MAX_SIZE); - r.addConsumer(counting); + DigestingConsumer digesting = new DigestingConsumer(messageDigest); + messageDigest.reset(); + reader.addConsumer(copying); + reader.addConsumer(counting); // Read the parent message ID - byte[] idBytes = r.readRaw(); - if(idBytes.length != UniqueId.LENGTH) throw new FormatException(); - MessageId parent = new MessageId(idBytes); + reader.readUserDefinedTag(Tags.MESSAGE_ID); + byte[] b = reader.readRaw(); + if(b.length != UniqueId.LENGTH) throw new FormatException(); + MessageId parent = new MessageId(b); // Read the group ID - idBytes = r.readRaw(); - if(idBytes.length != UniqueId.LENGTH) throw new FormatException(); - GroupId group = new GroupId(idBytes); + reader.readUserDefinedTag(Tags.GROUP_ID); + b = reader.readRaw(); + if(b.length != UniqueId.LENGTH) throw new FormatException(); + GroupId group = new GroupId(b); // Read the timestamp - long timestamp = r.readInt64(); + reader.readUserDefinedTag(Tags.TIMESTAMP); + long timestamp = reader.readInt64(); + if(timestamp < 0L) throw new FormatException(); // Hash the author's nick and public key to get the author ID - String nick = r.readString(); - byte[] encodedKey = r.readRaw(); - messageDigest.reset(); - messageDigest.update(nick.getBytes("UTF-8")); - messageDigest.update((byte) 0); // Null separator - messageDigest.update(encodedKey); + reader.readUserDefinedTag(Tags.AUTHOR); + reader.addConsumer(digesting); + reader.readString(); + byte[] encodedKey = reader.readRaw(); + reader.removeConsumer(digesting); AuthorId author = new AuthorId(messageDigest.digest()); // Skip the message body - r.readRaw(); + reader.readUserDefinedTag(Tags.MESSAGE_BODY); + reader.readRaw(); // Record the length of the signed data int messageLength = (int) counting.getCount(); - r.removeConsumer(counting); // Read the signature - byte[] sig = r.readRaw(); - // That should be all - if(!r.eof()) throw new FormatException(); + reader.readUserDefinedTag(Tags.SIGNATURE); + byte[] sig = reader.readRaw(); + reader.removeConsumer(counting); + reader.removeConsumer(copying); // Verify the signature PublicKey publicKey; try { @@ -76,6 +77,7 @@ class MessageParserImpl implements MessageParser { } catch(InvalidKeySpecException e) { throw new FormatException(); } + byte[] raw = copying.getCopy(); signature.initVerify(publicKey); signature.update(raw, 0, messageLength); if(!signature.verify(sig)) throw new SignatureException(); diff --git a/components/net/sf/briar/protocol/ProtocolModule.java b/components/net/sf/briar/protocol/ProtocolModule.java index 08e3c1b8ebe21fc4eba98ce1db5a26cf4414c582..18d350abb31b354ad70cd1497d1a84edca4b76ae 100644 --- a/components/net/sf/briar/protocol/ProtocolModule.java +++ b/components/net/sf/briar/protocol/ProtocolModule.java @@ -3,7 +3,6 @@ package net.sf.briar.protocol; import net.sf.briar.api.protocol.BundleReader; import net.sf.briar.api.protocol.BundleWriter; import net.sf.briar.api.protocol.MessageEncoder; -import net.sf.briar.api.protocol.MessageParser; import com.google.inject.AbstractModule; @@ -16,6 +15,6 @@ public class ProtocolModule extends AbstractModule { bind(BundleWriter.class).to(BundleWriterImpl.class); bind(HeaderFactory.class).to(HeaderFactoryImpl.class); bind(MessageEncoder.class).to(MessageEncoderImpl.class); - bind(MessageParser.class).to(MessageParserImpl.class); + bind(MessageReader.class).to(MessageReaderImpl.class); } } diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java index a04112e1e31d83a18f31e00cd6123a10a26e6d00..ebdbf7af62a036f20d3c0b6ff4c6ed7f0ca5cca3 100644 --- a/components/net/sf/briar/serial/ReaderImpl.java +++ b/components/net/sf/briar/serial/ReaderImpl.java @@ -479,7 +479,7 @@ class ReaderImpl implements Reader { } } - public void readUserDefinedTag(int i) throws IOException { - if(readUserDefinedTag() != i) throw new FormatException(); + public void readUserDefinedTag(int tag) throws IOException { + if(readUserDefinedTag() != tag) throw new FormatException(); } } diff --git a/test/net/sf/briar/protocol/BundleReadWriteTest.java b/test/net/sf/briar/protocol/BundleReadWriteTest.java index 9033a68600da95799c91456d24b3b8374d8003c4..8c5245b60ceb4a062cefd1824f12051ea18da32b 100644 --- a/test/net/sf/briar/protocol/BundleReadWriteTest.java +++ b/test/net/sf/briar/protocol/BundleReadWriteTest.java @@ -32,7 +32,6 @@ import net.sf.briar.api.protocol.Header; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.MessageParser; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Raw; import net.sf.briar.api.serial.RawByteArray; @@ -71,8 +70,8 @@ public class BundleReadWriteTest extends TestCase { private final WriterFactory wf; private final KeyPair keyPair; - private final Signature sig; - private final MessageDigest dig; + private final Signature sig, sig1; + private final MessageDigest dig, dig1; private final KeyParser keyParser; private final Message message; @@ -83,7 +82,9 @@ public class BundleReadWriteTest extends TestCase { wf = i.getInstance(WriterFactory.class); keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair(); sig = Signature.getInstance(SIGNATURE_ALGO); + sig1 = Signature.getInstance(SIGNATURE_ALGO); dig = MessageDigest.getInstance(DIGEST_ALGO); + dig1 = MessageDigest.getInstance(DIGEST_ALGO); final KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGO); keyParser = new KeyParser() { public PublicKey parsePublicKey(byte[] encodedKey) @@ -123,12 +124,12 @@ public class BundleReadWriteTest extends TestCase { testWriteBundle(); - MessageParser messageParser = - new MessageParserImpl(keyParser, sig, dig, rf); + MessageReader messageReader = + new MessageReaderImpl(keyParser, sig1, dig1); FileInputStream in = new FileInputStream(bundle); Reader reader = rf.createReader(in); BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig, - dig, messageParser, new HeaderFactoryImpl(), + dig, messageReader, new HeaderFactoryImpl(), new BatchFactoryImpl()); Header h = r.getHeader(); @@ -163,12 +164,12 @@ public class BundleReadWriteTest extends TestCase { f.writeByte(b + 1); f.close(); - MessageParser messageParser = - new MessageParserImpl(keyParser, sig, dig, rf); + MessageReader messageReader = + new MessageReaderImpl(keyParser, sig1, dig1); FileInputStream in = new FileInputStream(bundle); Reader reader = rf.createReader(in); BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig, - dig, messageParser, new HeaderFactoryImpl(), + dig, messageReader, new HeaderFactoryImpl(), new BatchFactoryImpl()); Header h = r.getHeader();