Messages are no longer encoded as raw byte arrays.

This commit is contained in:
akwizgran
2011-07-19 14:01:33 +01:00
parent 30fc6c1a92
commit ff984c69fb
11 changed files with 116 additions and 79 deletions

View File

@@ -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;
}

View File

@@ -59,5 +59,5 @@ public interface Reader {
boolean hasUserDefinedTag() throws IOException; boolean hasUserDefinedTag() throws IOException;
int readUserDefinedTag() throws IOException; int readUserDefinedTag() throws IOException;
void readUserDefinedTag(int i) throws IOException; void readUserDefinedTag(int tag) throws IOException;
} }

View File

@@ -18,12 +18,9 @@ import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Header; import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.Message; 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.Tags;
import net.sf.briar.api.protocol.UniqueId;
import net.sf.briar.api.serial.FormatException; 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; import net.sf.briar.api.serial.Reader;
class BundleReaderImpl implements BundleReader { class BundleReaderImpl implements BundleReader {
@@ -34,19 +31,19 @@ class BundleReaderImpl implements BundleReader {
private final PublicKey publicKey; private final PublicKey publicKey;
private final Signature signature; private final Signature signature;
private final MessageDigest messageDigest; private final MessageDigest messageDigest;
private final MessageParser messageParser; private final MessageReader messageReader;
private final HeaderFactory headerFactory; private final HeaderFactory headerFactory;
private final BatchFactory batchFactory; private final BatchFactory batchFactory;
private State state = State.START; private State state = State.START;
BundleReaderImpl(Reader reader, PublicKey publicKey, Signature signature, BundleReaderImpl(Reader reader, PublicKey publicKey, Signature signature,
MessageDigest messageDigest, MessageParser messageParser, MessageDigest messageDigest, MessageReader messageParser,
HeaderFactory headerFactory, BatchFactory batchFactory) { HeaderFactory headerFactory, BatchFactory batchFactory) {
this.reader = reader; this.reader = reader;
this.publicKey = publicKey; this.publicKey = publicKey;
this.signature = signature; this.signature = signature;
this.messageDigest = messageDigest; this.messageDigest = messageDigest;
this.messageParser = messageParser; this.messageReader = messageParser;
this.headerFactory = headerFactory; this.headerFactory = headerFactory;
this.batchFactory = batchFactory; this.batchFactory = batchFactory;
} }
@@ -88,6 +85,7 @@ class BundleReaderImpl implements BundleReader {
// Timestamp // Timestamp
reader.readUserDefinedTag(Tags.TIMESTAMP); reader.readUserDefinedTag(Tags.TIMESTAMP);
long timestamp = reader.readInt64(); long timestamp = reader.readInt64();
if(timestamp < 0L) throw new FormatException();
reader.removeConsumer(signing); reader.removeConsumer(signing);
// Read and verify the signature // Read and verify the signature
reader.readUserDefinedTag(Tags.SIGNATURE); reader.readUserDefinedTag(Tags.SIGNATURE);
@@ -122,11 +120,11 @@ class BundleReaderImpl implements BundleReader {
reader.addConsumer(digesting); reader.addConsumer(digesting);
reader.addConsumer(signing); reader.addConsumer(signing);
reader.readUserDefinedTag(Tags.BATCH); reader.readUserDefinedTag(Tags.BATCH);
List<Raw> rawMessages = new ArrayList<Raw>(); List<Message> messages = new ArrayList<Message>();
reader.readListStart(); reader.readListStart();
while(!reader.hasListEnd()) { while(!reader.hasListEnd()) {
reader.readUserDefinedTag(Tags.MESSAGE); reader.readUserDefinedTag(Tags.MESSAGE);
rawMessages.add(new RawByteArray(reader.readRaw())); messages.add(messageReader.readMessage(reader));
} }
reader.readListEnd(); reader.readListEnd();
reader.removeConsumer(signing); reader.removeConsumer(signing);
@@ -136,12 +134,6 @@ class BundleReaderImpl implements BundleReader {
reader.removeConsumer(digesting); reader.removeConsumer(digesting);
reader.removeConsumer(counting); reader.removeConsumer(counting);
if(!signature.verify(sig)) throw new SignatureException(); 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 // Build and return the batch
BatchId id = new BatchId(messageDigest.digest()); BatchId id = new BatchId(messageDigest.digest());
return batchFactory.createBatch(id, messages); return batchFactory.createBatch(id, messages);

View File

@@ -53,13 +53,17 @@ class BundleWriterImpl implements BundleWriter {
// Write the data to be signed // Write the data to be signed
out.setSigning(true); out.setSigning(true);
writer.writeUserDefinedTag(Tags.HEADER); writer.writeUserDefinedTag(Tags.HEADER);
// Acks
writer.writeListStart(); writer.writeListStart();
for(BatchId ack : acks) ack.writeTo(writer); for(BatchId ack : acks) ack.writeTo(writer);
writer.writeListEnd(); writer.writeListEnd();
// Subs
writer.writeListStart(); writer.writeListStart();
for(GroupId sub : subs) sub.writeTo(writer); for(GroupId sub : subs) sub.writeTo(writer);
writer.writeListEnd(); writer.writeListEnd();
// Transports
writer.writeMap(transports); writer.writeMap(transports);
// Timestamp
writer.writeUserDefinedTag(Tags.TIMESTAMP); writer.writeUserDefinedTag(Tags.TIMESTAMP);
writer.writeInt64(System.currentTimeMillis()); writer.writeInt64(System.currentTimeMillis());
out.setSigning(false); out.setSigning(false);
@@ -88,7 +92,8 @@ class BundleWriterImpl implements BundleWriter {
writer.writeListStart(); writer.writeListStart();
for(Raw message : messages) { for(Raw message : messages) {
writer.writeUserDefinedTag(Tags.MESSAGE); writer.writeUserDefinedTag(Tags.MESSAGE);
writer.writeRaw(message); // Bypass the writer and write the raw message directly
out.write(message.getBytes());
} }
writer.writeListEnd(); writer.writeListEnd();
out.setSigning(false); out.setSigning(false);

View File

@@ -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);
}
}

View File

@@ -12,6 +12,7 @@ import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageEncoder;
import net.sf.briar.api.protocol.MessageId; 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.Writer;
import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.api.serial.WriterFactory;
@@ -35,16 +36,20 @@ class MessageEncoderImpl implements MessageEncoder {
byte[] encodedKey = keyPair.getPublic().getEncoded(); byte[] encodedKey = keyPair.getPublic().getEncoded();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeRaw(parent); parent.writeTo(w);
w.writeRaw(group); group.writeTo(w);
w.writeUserDefinedTag(Tags.TIMESTAMP);
w.writeInt64(timestamp); w.writeInt64(timestamp);
w.writeUserDefinedTag(Tags.AUTHOR);
w.writeString(nick); w.writeString(nick);
w.writeRaw(encodedKey); w.writeRaw(encodedKey);
w.writeUserDefinedTag(Tags.MESSAGE_BODY);
w.writeRaw(body); w.writeRaw(body);
byte[] signable = out.toByteArray(); byte[] signable = out.toByteArray();
signature.initSign(keyPair.getPrivate()); signature.initSign(keyPair.getPrivate());
signature.update(signable); signature.update(signable);
byte[] sig = signature.sign(); byte[] sig = signature.sign();
w.writeUserDefinedTag(Tags.SIGNATURE);
w.writeRaw(sig); w.writeRaw(sig);
byte[] raw = out.toByteArray(); byte[] raw = out.toByteArray();
w.close(); w.close();
@@ -53,10 +58,13 @@ class MessageEncoderImpl implements MessageEncoder {
messageDigest.update(raw); messageDigest.update(raw);
MessageId id = new MessageId(messageDigest.digest()); MessageId id = new MessageId(messageDigest.digest());
// The author ID is the hash of the author's nick and public key // 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.reset();
messageDigest.update(nick.getBytes("UTF-8")); messageDigest.update(out.toByteArray());
messageDigest.update((byte) 0); // Null separator
messageDigest.update(encodedKey);
AuthorId author = new AuthorId(messageDigest.digest()); AuthorId author = new AuthorId(messageDigest.digest());
return new MessageImpl(id, parent, group, author, timestamp, raw); return new MessageImpl(id, parent, group, author, timestamp, raw);
} }

View File

@@ -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;
}

View File

@@ -1,6 +1,5 @@
package net.sf.briar.protocol; package net.sf.briar.protocol;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.MessageDigest; 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.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; 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.protocol.UniqueId;
import net.sf.briar.api.serial.FormatException; import net.sf.briar.api.serial.FormatException;
import net.sf.briar.api.serial.Reader; 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 KeyParser keyParser;
private final Signature signature; private final Signature signature;
private final MessageDigest messageDigest; private final MessageDigest messageDigest;
private final ReaderFactory readerFactory;
MessageParserImpl(KeyParser keyParser, Signature signature, MessageReaderImpl(KeyParser keyParser, Signature signature,
MessageDigest messageDigest, ReaderFactory readerFactory) { MessageDigest messageDigest) {
this.keyParser = keyParser; this.keyParser = keyParser;
this.signature = signature; this.signature = signature;
this.messageDigest = messageDigest; this.messageDigest = messageDigest;
this.readerFactory = readerFactory;
} }
public Message parseMessage(byte[] raw) throws IOException, public Message readMessage(Reader reader) throws IOException,
GeneralSecurityException { GeneralSecurityException {
if(raw.length > Message.MAX_SIZE) throw new FormatException(); CopyingConsumer copying = new CopyingConsumer();
ByteArrayInputStream in = new ByteArrayInputStream(raw);
Reader r = readerFactory.createReader(in);
CountingConsumer counting = new CountingConsumer(Message.MAX_SIZE); CountingConsumer counting = new CountingConsumer(Message.MAX_SIZE);
r.addConsumer(counting); DigestingConsumer digesting = new DigestingConsumer(messageDigest);
// Read the parent message ID
byte[] idBytes = r.readRaw();
if(idBytes.length != UniqueId.LENGTH) throw new FormatException();
MessageId parent = new MessageId(idBytes);
// Read the group ID
idBytes = r.readRaw();
if(idBytes.length != UniqueId.LENGTH) throw new FormatException();
GroupId group = new GroupId(idBytes);
// Read the timestamp
long timestamp = r.readInt64();
// Hash the author's nick and public key to get the author ID
String nick = r.readString();
byte[] encodedKey = r.readRaw();
messageDigest.reset(); messageDigest.reset();
messageDigest.update(nick.getBytes("UTF-8")); reader.addConsumer(copying);
messageDigest.update((byte) 0); // Null separator reader.addConsumer(counting);
messageDigest.update(encodedKey); // Read the parent message ID
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
reader.readUserDefinedTag(Tags.GROUP_ID);
b = reader.readRaw();
if(b.length != UniqueId.LENGTH) throw new FormatException();
GroupId group = new GroupId(b);
// Read the timestamp
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
reader.readUserDefinedTag(Tags.AUTHOR);
reader.addConsumer(digesting);
reader.readString();
byte[] encodedKey = reader.readRaw();
reader.removeConsumer(digesting);
AuthorId author = new AuthorId(messageDigest.digest()); AuthorId author = new AuthorId(messageDigest.digest());
// Skip the message body // Skip the message body
r.readRaw(); reader.readUserDefinedTag(Tags.MESSAGE_BODY);
reader.readRaw();
// Record the length of the signed data // Record the length of the signed data
int messageLength = (int) counting.getCount(); int messageLength = (int) counting.getCount();
r.removeConsumer(counting);
// Read the signature // Read the signature
byte[] sig = r.readRaw(); reader.readUserDefinedTag(Tags.SIGNATURE);
// That should be all byte[] sig = reader.readRaw();
if(!r.eof()) throw new FormatException(); reader.removeConsumer(counting);
reader.removeConsumer(copying);
// Verify the signature // Verify the signature
PublicKey publicKey; PublicKey publicKey;
try { try {
@@ -76,6 +77,7 @@ class MessageParserImpl implements MessageParser {
} catch(InvalidKeySpecException e) { } catch(InvalidKeySpecException e) {
throw new FormatException(); throw new FormatException();
} }
byte[] raw = copying.getCopy();
signature.initVerify(publicKey); signature.initVerify(publicKey);
signature.update(raw, 0, messageLength); signature.update(raw, 0, messageLength);
if(!signature.verify(sig)) throw new SignatureException(); if(!signature.verify(sig)) throw new SignatureException();

View File

@@ -3,7 +3,6 @@ package net.sf.briar.protocol;
import net.sf.briar.api.protocol.BundleReader; import net.sf.briar.api.protocol.BundleReader;
import net.sf.briar.api.protocol.BundleWriter; import net.sf.briar.api.protocol.BundleWriter;
import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageEncoder;
import net.sf.briar.api.protocol.MessageParser;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@@ -16,6 +15,6 @@ public class ProtocolModule extends AbstractModule {
bind(BundleWriter.class).to(BundleWriterImpl.class); bind(BundleWriter.class).to(BundleWriterImpl.class);
bind(HeaderFactory.class).to(HeaderFactoryImpl.class); bind(HeaderFactory.class).to(HeaderFactoryImpl.class);
bind(MessageEncoder.class).to(MessageEncoderImpl.class); bind(MessageEncoder.class).to(MessageEncoderImpl.class);
bind(MessageParser.class).to(MessageParserImpl.class); bind(MessageReader.class).to(MessageReaderImpl.class);
} }
} }

View File

@@ -479,7 +479,7 @@ class ReaderImpl implements Reader {
} }
} }
public void readUserDefinedTag(int i) throws IOException { public void readUserDefinedTag(int tag) throws IOException {
if(readUserDefinedTag() != i) throw new FormatException(); if(readUserDefinedTag() != tag) throw new FormatException();
} }
} }

View File

@@ -32,7 +32,6 @@ import net.sf.briar.api.protocol.Header;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageEncoder;
import net.sf.briar.api.protocol.MessageId; 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.protocol.UniqueId;
import net.sf.briar.api.serial.Raw; import net.sf.briar.api.serial.Raw;
import net.sf.briar.api.serial.RawByteArray; import net.sf.briar.api.serial.RawByteArray;
@@ -71,8 +70,8 @@ public class BundleReadWriteTest extends TestCase {
private final WriterFactory wf; private final WriterFactory wf;
private final KeyPair keyPair; private final KeyPair keyPair;
private final Signature sig; private final Signature sig, sig1;
private final MessageDigest dig; private final MessageDigest dig, dig1;
private final KeyParser keyParser; private final KeyParser keyParser;
private final Message message; private final Message message;
@@ -83,7 +82,9 @@ public class BundleReadWriteTest extends TestCase {
wf = i.getInstance(WriterFactory.class); wf = i.getInstance(WriterFactory.class);
keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair(); keyPair = KeyPairGenerator.getInstance(KEY_PAIR_ALGO).generateKeyPair();
sig = Signature.getInstance(SIGNATURE_ALGO); sig = Signature.getInstance(SIGNATURE_ALGO);
sig1 = Signature.getInstance(SIGNATURE_ALGO);
dig = MessageDigest.getInstance(DIGEST_ALGO); dig = MessageDigest.getInstance(DIGEST_ALGO);
dig1 = MessageDigest.getInstance(DIGEST_ALGO);
final KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGO); final KeyFactory keyFactory = KeyFactory.getInstance(KEY_PAIR_ALGO);
keyParser = new KeyParser() { keyParser = new KeyParser() {
public PublicKey parsePublicKey(byte[] encodedKey) public PublicKey parsePublicKey(byte[] encodedKey)
@@ -123,12 +124,12 @@ public class BundleReadWriteTest extends TestCase {
testWriteBundle(); testWriteBundle();
MessageParser messageParser = MessageReader messageReader =
new MessageParserImpl(keyParser, sig, dig, rf); new MessageReaderImpl(keyParser, sig1, dig1);
FileInputStream in = new FileInputStream(bundle); FileInputStream in = new FileInputStream(bundle);
Reader reader = rf.createReader(in); Reader reader = rf.createReader(in);
BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig, BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig,
dig, messageParser, new HeaderFactoryImpl(), dig, messageReader, new HeaderFactoryImpl(),
new BatchFactoryImpl()); new BatchFactoryImpl());
Header h = r.getHeader(); Header h = r.getHeader();
@@ -163,12 +164,12 @@ public class BundleReadWriteTest extends TestCase {
f.writeByte(b + 1); f.writeByte(b + 1);
f.close(); f.close();
MessageParser messageParser = MessageReader messageReader =
new MessageParserImpl(keyParser, sig, dig, rf); new MessageReaderImpl(keyParser, sig1, dig1);
FileInputStream in = new FileInputStream(bundle); FileInputStream in = new FileInputStream(bundle);
Reader reader = rf.createReader(in); Reader reader = rf.createReader(in);
BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig, BundleReader r = new BundleReaderImpl(reader, keyPair.getPublic(), sig,
dig, messageParser, new HeaderFactoryImpl(), dig, messageReader, new HeaderFactoryImpl(),
new BatchFactoryImpl()); new BatchFactoryImpl());
Header h = r.getHeader(); Header h = r.getHeader();