Changed the message format to store the author and group inline - this

doesn't take a huge amount of space and allows every message to be
self-certifying.
This commit is contained in:
akwizgran
2011-07-25 21:14:16 +01:00
parent 586d1739ae
commit 3f61d0c3df
18 changed files with 317 additions and 127 deletions

View File

@@ -0,0 +1,42 @@
package net.sf.briar.protocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
import com.google.inject.Inject;
class AuthorFactoryImpl implements AuthorFactory {
private final CryptoComponent crypto;
private final WriterFactory writerFactory;
@Inject
AuthorFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory) {
this.crypto = crypto;
this.writerFactory = writerFactory;
}
public Author createAuthor(String name, byte[] publicKey)
throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
new AuthorImpl(null, name, publicKey).writeTo(w);
MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.reset();
messageDigest.update(out.toByteArray());
AuthorId id = new AuthorId(messageDigest.digest());
return new AuthorImpl(id, name, publicKey);
}
public Author createAuthor(AuthorId id, String name, byte[] publicKey) {
return new AuthorImpl(id, name, publicKey);
}
}

View File

@@ -0,0 +1,39 @@
package net.sf.briar.protocol;
import java.io.IOException;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Tags;
import net.sf.briar.api.serial.Writer;
class AuthorImpl implements Author {
private final AuthorId id;
private final String name;
private final byte[] publicKey;
AuthorImpl(AuthorId id, String name, byte[] publicKey) {
this.id = id;
this.name = name;
this.publicKey = publicKey;
}
public AuthorId getId() {
return id;
}
public String getName() {
return name;
}
public byte[] getPublicKey() {
return publicKey;
}
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedTag(Tags.AUTHOR);
w.writeString(name);
w.writeBytes(publicKey);
}
}

View File

@@ -0,0 +1,38 @@
package net.sf.briar.protocol;
import java.io.IOException;
import java.security.MessageDigest;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Tags;
import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.Reader;
class AuthorReader implements ObjectReader<Author> {
private final MessageDigest messageDigest;
private final AuthorFactory authorFactory;
AuthorReader(CryptoComponent crypto, AuthorFactory authorFactory) {
messageDigest = crypto.getMessageDigest();
this.authorFactory = authorFactory;
}
public Author readObject(Reader r) throws IOException {
// Initialise the consumer
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
messageDigest.reset();
// Read and digest the data
r.addConsumer(digesting);
r.readUserDefinedTag(Tags.AUTHOR);
String name = r.readString();
byte[] publicKey = r.readBytes();
r.removeConsumer(digesting);
// Build and return the author
AuthorId id = new AuthorId(messageDigest.digest());
return authorFactory.createAuthor(id, name, publicKey);
}
}

View File

@@ -4,6 +4,7 @@ import java.io.IOException;
import java.security.MessageDigest;
import java.util.List;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Message;
@@ -17,9 +18,9 @@ class BatchReader implements ObjectReader<Batch> {
private final ObjectReader<Message> messageReader;
private final BatchFactory batchFactory;
BatchReader(MessageDigest messageDigest,
ObjectReader<Message> messageReader, BatchFactory batchFactory) {
this.messageDigest = messageDigest;
BatchReader(CryptoComponent crypto, ObjectReader<Message> messageReader,
BatchFactory batchFactory) {
messageDigest = crypto.getMessageDigest();
this.messageReader = messageReader;
this.batchFactory = batchFactory;
}

View File

@@ -1,11 +1,40 @@
package net.sf.briar.protocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
import com.google.inject.Inject;
class GroupFactoryImpl implements GroupFactory {
private final CryptoComponent crypto;
private final WriterFactory writerFactory;
@Inject
GroupFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory) {
this.crypto = crypto;
this.writerFactory = writerFactory;
}
public Group createGroup(String name, byte[] publicKey) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
new GroupImpl(null, name, publicKey).writeTo(w);
MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.reset();
messageDigest.update(out.toByteArray());
GroupId id = new GroupId(messageDigest.digest());
return new GroupImpl(id, name, publicKey);
}
public Group createGroup(GroupId id, String name, byte[] publicKey) {
return new GroupImpl(id, name, publicKey);
}

View File

@@ -3,6 +3,7 @@ package net.sf.briar.protocol;
import java.io.IOException;
import java.security.MessageDigest;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId;
@@ -15,8 +16,8 @@ class GroupReader implements ObjectReader<Group> {
private final MessageDigest messageDigest;
private final GroupFactory groupFactory;
GroupReader(MessageDigest messageDigest, GroupFactory groupFactory) {
this.messageDigest = messageDigest;
GroupReader(CryptoComponent crypto, GroupFactory groupFactory) {
messageDigest = crypto.getMessageDigest();
this.groupFactory = groupFactory;
}

View File

@@ -3,13 +3,14 @@ package net.sf.briar.protocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageEncoder;
import net.sf.briar.api.protocol.MessageId;
@@ -32,8 +33,30 @@ class MessageEncoderImpl implements MessageEncoder {
this.writerFactory = writerFactory;
}
public Message encodeMessage(MessageId parent, GroupId group, String nick,
KeyPair keyPair, byte[] body) throws IOException,
public Message encodeMessage(MessageId parent, Group group, byte[] body)
throws IOException {
long timestamp = System.currentTimeMillis();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
// Write the message
w.writeUserDefinedTag(Tags.MESSAGE);
parent.writeTo(w);
group.writeTo(w);
w.writeNull(); // No author
w.writeInt64(timestamp);
w.writeBytes(body);
w.writeNull(); // No author's signature
byte[] raw = out.toByteArray();
// The message ID is the hash of the entire message
messageDigest.reset();
messageDigest.update(raw);
MessageId id = new MessageId(messageDigest.digest());
return new MessageImpl(id, parent, group.getId(), AuthorId.NONE,
timestamp, raw);
}
public Message encodeMessage(MessageId parent, Group group, Author author,
PrivateKey privateKey, byte[] body) throws IOException,
GeneralSecurityException {
long timestamp = System.currentTimeMillis();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -42,13 +65,12 @@ class MessageEncoderImpl implements MessageEncoder {
w.writeUserDefinedTag(Tags.MESSAGE);
parent.writeTo(w);
group.writeTo(w);
author.writeTo(w);
w.writeInt64(timestamp);
w.writeString(nick);
w.writeBytes(keyPair.getPublic().getEncoded());
w.writeBytes(body);
// Sign the message
byte[] signable = out.toByteArray();
signature.initSign(keyPair.getPrivate());
signature.initSign(privateKey);
signature.update(signable);
byte[] sig = signature.sign();
signable = null;
@@ -59,14 +81,14 @@ class MessageEncoderImpl implements MessageEncoder {
messageDigest.reset();
messageDigest.update(raw);
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 object
out.reset();
w = writerFactory.createWriter(out);
w.writeString(nick);
w.writeBytes(keyPair.getPublic().getEncoded());
author.writeTo(w);
messageDigest.reset();
messageDigest.update(out.toByteArray());
AuthorId authorId = new AuthorId(messageDigest.digest());
return new MessageImpl(id, parent, group, authorId, timestamp, raw);
return new MessageImpl(id, parent, group.getId(), authorId, timestamp,
raw);
}
}

View File

@@ -6,11 +6,12 @@ import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.KeyParser;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Tags;
@@ -21,15 +22,19 @@ import net.sf.briar.api.serial.Reader;
class MessageReader implements ObjectReader<Message> {
private final ObjectReader<Group> groupReader;
private final ObjectReader<Author> authorReader;
private final KeyParser keyParser;
private final Signature signature;
private final MessageDigest messageDigest;
MessageReader(KeyParser keyParser, Signature signature,
MessageDigest messageDigest) {
this.keyParser = keyParser;
this.signature = signature;
this.messageDigest = messageDigest;
MessageReader(CryptoComponent crypto, ObjectReader<Group> groupReader,
ObjectReader<Author> authorReader) {
this.groupReader = groupReader;
this.authorReader = authorReader;
keyParser = crypto.getKeyParser();
signature = crypto.getSignature();
messageDigest = crypto.getMessageDigest();
}
public Message readObject(Reader r) throws IOException {
@@ -44,49 +49,49 @@ class MessageReader implements ObjectReader<Message> {
byte[] b = r.readBytes();
if(b.length != UniqueId.LENGTH) throw new FormatException();
MessageId parent = new MessageId(b);
// Read the group ID
r.readUserDefinedTag(Tags.GROUP_ID);
b = r.readBytes();
if(b.length != UniqueId.LENGTH) throw new FormatException();
GroupId group = new GroupId(b);
// Read the group
r.addObjectReader(Tags.GROUP, groupReader);
Group group = r.readUserDefined(Tags.GROUP, Group.class);
r.removeObjectReader(Tags.GROUP);
// Read the author, if there is one
r.addObjectReader(Tags.AUTHOR, authorReader);
Author author = null;
if(r.hasNull()) r.readNull();
else author = r.readUserDefined(Tags.AUTHOR, Author.class);
r.removeObjectReader(Tags.AUTHOR);
// Read the timestamp
long timestamp = r.readInt64();
if(timestamp < 0L) throw new FormatException();
// Hash the author's nick and public key to get the author ID
DigestingConsumer digesting = new DigestingConsumer(messageDigest);
messageDigest.reset();
r.addConsumer(digesting);
r.readString();
byte[] encodedKey = r.readBytes();
r.removeConsumer(digesting);
AuthorId author = new AuthorId(messageDigest.digest());
// Skip the message body
r.readBytes();
// Record the length of the signed data
int messageLength = (int) counting.getCount();
// Read the signature
byte[] sig = r.readBytes();
// Read the author's signature, if there is one
byte[] authorSig = null;
if(author == null) r.readNull();
else authorSig = r.readBytes();
// That's all, folks
r.removeConsumer(counting);
r.removeConsumer(copying);
// Verify the signature
PublicKey publicKey;
try {
publicKey = keyParser.parsePublicKey(encodedKey);
} catch(InvalidKeySpecException e) {
throw new FormatException();
}
byte[] raw = copying.getCopy();
try {
signature.initVerify(publicKey);
signature.update(raw, 0, messageLength);
if(!signature.verify(sig)) throw new SignatureException();
} catch(GeneralSecurityException e) {
throw new FormatException();
// Verify the author's signature, if there is one
if(author != null) {
try {
PublicKey publicKey =
keyParser.parsePublicKey(author.getPublicKey());
signature.initVerify(publicKey);
signature.update(raw, 0, messageLength);
if(!signature.verify(authorSig)) throw new SignatureException();
} catch(GeneralSecurityException e) {
throw new FormatException();
}
}
// Hash the message, including the signature, to get the message ID
messageDigest.reset();
messageDigest.update(raw);
MessageId id = new MessageId(messageDigest.digest());
return new MessageImpl(id, parent, group, author, timestamp, raw);
AuthorId authorId = author == null ? AuthorId.NONE : author.getId();
return new MessageImpl(id, parent, group.getId(), authorId, timestamp,
raw);
}
}

View File

@@ -1,5 +1,6 @@
package net.sf.briar.protocol;
import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.MessageEncoder;
@@ -10,6 +11,7 @@ public class ProtocolModule extends AbstractModule {
@Override
protected void configure() {
bind(AckFactory.class).to(AckFactoryImpl.class);
bind(AuthorFactory.class).to(AuthorFactoryImpl.class);
bind(BatchFactory.class).to(BatchFactoryImpl.class);
bind(GroupFactory.class).to(GroupFactoryImpl.class);
bind(MessageEncoder.class).to(MessageEncoderImpl.class);