Superclass for validating messages that are BDF lists.

This commit is contained in:
akwizgran
2016-02-29 21:57:02 +00:00
parent 05ec49e45b
commit cc7ffee28d
8 changed files with 290 additions and 322 deletions

View File

@@ -0,0 +1,114 @@
package org.briarproject.clients;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageValidator;
import org.briarproject.api.system.Clock;
import org.briarproject.util.StringUtils;
import java.util.logging.Logger;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
public abstract class BdfMessageValidator implements MessageValidator {
protected static final Logger LOG =
Logger.getLogger(BdfMessageValidator.class.getName());
protected final ClientHelper clientHelper;
protected final MetadataEncoder metadataEncoder;
protected final Clock clock;
protected BdfMessageValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) {
this.clientHelper = clientHelper;
this.metadataEncoder = metadataEncoder;
this.clock = clock;
}
protected abstract BdfDictionary validateMessage(BdfList message, Group g,
long timestamp) throws FormatException;
@Override
public Metadata validateMessage(Message m, Group g) {
// Reject the message if it's too far in the future
long now = clock.currentTimeMillis();
if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) {
LOG.info("Timestamp is too far in the future");
return null;
}
byte[] raw = m.getRaw();
try {
BdfList message = clientHelper.toList(raw, MESSAGE_HEADER_LENGTH,
raw.length - MESSAGE_HEADER_LENGTH);
BdfDictionary meta = validateMessage(message, g, m.getTimestamp());
if (meta == null) {
LOG.info("Invalid message");
return null;
}
return metadataEncoder.encode(meta);
} catch (FormatException e) {
LOG.info("Invalid message");
return null;
}
}
protected void checkLength(String s, int minLength, int maxLength)
throws FormatException {
if (s != null) {
int length = StringUtils.toUtf8(s).length;
if (length < minLength) throw new FormatException();
if (length > maxLength) throw new FormatException();
}
}
protected void checkLength(String s, int length) throws FormatException {
if (s != null && StringUtils.toUtf8(s).length != length)
throw new FormatException();
}
protected void checkLength(byte[] b, int minLength, int maxLength)
throws FormatException {
if (b != null) {
if (b.length < minLength) throw new FormatException();
if (b.length > maxLength) throw new FormatException();
}
}
protected void checkLength(byte[] b, int length) throws FormatException {
if (b != null && b.length != length) throw new FormatException();
}
protected void checkSize(BdfList list, int minSize, int maxSize)
throws FormatException {
if (list != null) {
if (list.size() < minSize) throw new FormatException();
if (list.size() > maxSize) throw new FormatException();
}
}
protected void checkSize(BdfList list, int size) throws FormatException {
if (list != null && list.size() != size) throw new FormatException();
}
protected void checkSize(BdfDictionary dictionary, int minSize,
int maxSize) throws FormatException {
if (dictionary != null) {
if (dictionary.size() < minSize) throw new FormatException();
if (dictionary.size() > maxSize) throw new FormatException();
}
}
protected void checkSize(BdfDictionary dictionary, int size)
throws FormatException {
if (dictionary != null && dictionary.size() != size)
throw new FormatException();
}
}

View File

@@ -1,69 +1,47 @@
package org.briarproject.forum; package org.briarproject.forum;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Message; import org.briarproject.api.system.Clock;
import org.briarproject.api.sync.MessageValidator; import org.briarproject.clients.BdfMessageValidator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.logging.Logger;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
class ForumListValidator implements MessageValidator { class ForumListValidator extends BdfMessageValidator {
private static final Logger LOG = ForumListValidator(ClientHelper clientHelper,
Logger.getLogger(ForumListValidator.class.getName()); MetadataEncoder metadataEncoder, Clock clock) {
super(clientHelper, metadataEncoder, clock);
private final BdfReaderFactory bdfReaderFactory;
private final MetadataEncoder metadataEncoder;
ForumListValidator(BdfReaderFactory bdfReaderFactory,
MetadataEncoder metadataEncoder) {
this.bdfReaderFactory = bdfReaderFactory;
this.metadataEncoder = metadataEncoder;
} }
@Override @Override
public Metadata validateMessage(Message m, Group g) { public BdfDictionary validateMessage(BdfList message, Group g,
try { long timestamp) throws FormatException {
// Parse the message body // Version, forum list
byte[] raw = m.getRaw(); checkSize(message, 2);
ByteArrayInputStream in = new ByteArrayInputStream(raw, // Version
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH); long version = message.getLong(0);
BdfReader r = bdfReaderFactory.createReader(in); if (version < 0) throw new FormatException();
r.readListStart(); // Forum list
long version = r.readLong(); BdfList forumList = message.getList(1);
if (version < 0) throw new FormatException(); for (int i = 0; i < forumList.size(); i++) {
r.readListStart(); BdfList forum = forumList.getList(i);
while (!r.hasListEnd()) { // Name, salt
r.readListStart(); checkSize(forum, 2);
String name = r.readString(MAX_FORUM_NAME_LENGTH); String name = forum.getString(0);
if (name.length() == 0) throw new FormatException(); checkLength(name, 1, MAX_FORUM_NAME_LENGTH);
byte[] salt = r.readRaw(FORUM_SALT_LENGTH); byte[] salt = forum.getRaw(1);
if (salt.length != FORUM_SALT_LENGTH) checkLength(salt, FORUM_SALT_LENGTH);
throw new FormatException();
r.readListEnd();
}
r.readListEnd();
r.readListEnd();
if (!r.eof()) throw new FormatException();
// Return the metadata
BdfDictionary d = new BdfDictionary();
d.put("version", version);
d.put("local", false);
return metadataEncoder.encode(d);
} catch (IOException e) {
LOG.info("Invalid forum list");
return null;
} }
// Return the metadata
BdfDictionary meta = new BdfDictionary();
meta.put("version", version);
meta.put("local", false);
return meta;
} }
} }

View File

@@ -3,16 +3,14 @@ package org.briarproject.forum;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.data.ObjectReader;
import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostFactory; import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
@@ -29,13 +27,10 @@ public class ForumModule extends AbstractModule {
@Provides @Singleton @Provides @Singleton
ForumPostValidator getForumPostValidator( ForumPostValidator getForumPostValidator(
ValidationManager validationManager, CryptoComponent crypto, ValidationManager validationManager, CryptoComponent crypto,
BdfReaderFactory bdfReaderFactory, AuthorFactory authorFactory, ClientHelper clientHelper,
BdfWriterFactory bdfWriterFactory, MetadataEncoder metadataEncoder, Clock clock) {
ObjectReader<Author> authorReader, MetadataEncoder metadataEncoder,
Clock clock) {
ForumPostValidator validator = new ForumPostValidator(crypto, ForumPostValidator validator = new ForumPostValidator(crypto,
bdfReaderFactory, bdfWriterFactory, authorReader, authorFactory, clientHelper, metadataEncoder, clock);
metadataEncoder, clock);
validationManager.registerMessageValidator( validationManager.registerMessageValidator(
ForumManagerImpl.CLIENT_ID, validator); ForumManagerImpl.CLIENT_ID, validator);
return validator; return validator;
@@ -43,11 +38,10 @@ public class ForumModule extends AbstractModule {
@Provides @Singleton @Provides @Singleton
ForumListValidator getForumListValidator( ForumListValidator getForumListValidator(
ValidationManager validationManager, ValidationManager validationManager, ClientHelper clientHelper,
BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder, Clock clock) {
MetadataEncoder metadataEncoder) { ForumListValidator validator = new ForumListValidator(clientHelper,
ForumListValidator validator = new ForumListValidator(bdfReaderFactory, metadataEncoder, clock);
metadataEncoder);
validationManager.registerMessageValidator( validationManager.registerMessageValidator(
ForumSharingManagerImpl.CLIENT_ID, validator); ForumSharingManagerImpl.CLIENT_ID, validator);
return validator; return validator;

View File

@@ -2,164 +2,114 @@ package org.briarproject.forum;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.UniqueId; import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser; import org.briarproject.api.crypto.KeyParser;
import org.briarproject.api.crypto.PublicKey; import org.briarproject.api.crypto.PublicKey;
import org.briarproject.api.crypto.Signature; import org.briarproject.api.crypto.Signature;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.BdfWriter;
import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.data.ObjectReader;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageValidator;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfMessageValidator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import static org.briarproject.api.forum.ForumConstants.MAX_CONTENT_TYPE_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_CONTENT_TYPE_LENGTH;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
class ForumPostValidator implements MessageValidator { class ForumPostValidator extends BdfMessageValidator {
private static final Logger LOG =
Logger.getLogger(ForumPostValidator.class.getName());
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final BdfReaderFactory bdfReaderFactory; private final AuthorFactory authorFactory;
private final BdfWriterFactory bdfWriterFactory;
private final ObjectReader<Author> authorReader;
private final MetadataEncoder metadataEncoder;
private final Clock clock;
private final KeyParser keyParser;
ForumPostValidator(CryptoComponent crypto, ForumPostValidator(CryptoComponent crypto, AuthorFactory authorFactory,
BdfReaderFactory bdfReaderFactory, ClientHelper clientHelper, MetadataEncoder metadataEncoder,
BdfWriterFactory bdfWriterFactory, Clock clock) {
ObjectReader<Author> authorReader, super(clientHelper, metadataEncoder, clock);
MetadataEncoder metadataEncoder, Clock clock) {
this.crypto = crypto; this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory; this.authorFactory = authorFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.authorReader = authorReader;
this.metadataEncoder = metadataEncoder;
this.clock = clock;
keyParser = crypto.getSignatureKeyParser();
} }
@Override @Override
public Metadata validateMessage(Message m, Group g) { protected BdfDictionary validateMessage(BdfList message, Group g,
// Reject the message if it's too far in the future long timestamp) throws FormatException {
long now = clock.currentTimeMillis(); // Parent ID, author, content type, forum post body, signature
if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) { checkSize(message, 5);
LOG.info("Timestamp is too far in the future"); // Parent ID is optional
byte[] parent = message.getOptionalRaw(0);
checkLength(parent, UniqueId.LENGTH);
// Author is optional
Author author = null;
BdfList authorList = message.getOptionalList(1);
if (authorList != null) {
// Name, public key
checkSize(authorList, 2);
String name = authorList.getString(0);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = authorList.getRaw(1);
checkLength(publicKey, 0, MAX_PUBLIC_KEY_LENGTH);
author = authorFactory.createAuthor(name, publicKey);
}
// Content type
String contentType = message.getString(2);
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
// Forum post body
byte[] body = message.getRaw(3);
checkLength(body, 0, MAX_FORUM_POST_BODY_LENGTH);
// Signature is optional
byte[] sig = message.getOptionalRaw(4);
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
// If there's an author there must be a signature and vice versa
if (author != null && sig == null) {
LOG.info("Author without signature");
return null; return null;
} }
try { if (author == null && sig != null) {
// Parse the message body LOG.info("Signature without author");
byte[] raw = m.getRaw(); return null;
ByteArrayInputStream in = new ByteArrayInputStream(raw, }
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH); // Verify the signature, if any
BdfReader r = bdfReaderFactory.createReader(in); if (author != null) {
MessageId parent = null; try {
Author author = null;
byte[] sig = null;
r.readListStart();
// Read the parent ID, if any
if (r.hasRaw()) {
byte[] id = r.readRaw(UniqueId.LENGTH);
if (id.length < UniqueId.LENGTH) throw new FormatException();
parent = new MessageId(id);
} else {
r.readNull();
}
// Read the author, if any
if (r.hasList()) author = authorReader.readObject(r);
else r.readNull();
// Read the content type
String contentType = r.readString(MAX_CONTENT_TYPE_LENGTH);
// Read the forum post body
byte[] postBody = r.readRaw(MAX_FORUM_POST_BODY_LENGTH);
// Read the signature, if any
if (r.hasRaw()) sig = r.readRaw(MAX_SIGNATURE_LENGTH);
else r.readNull();
r.readListEnd();
if (!r.eof()) throw new FormatException();
// If there's an author there must be a signature and vice versa
if (author != null && sig == null) {
LOG.info("Author without signature");
return null;
}
if (author == null && sig != null) {
LOG.info("Signature without author");
return null;
}
// Verify the signature, if any
if (author != null) {
// Parse the public key // Parse the public key
KeyParser keyParser = crypto.getSignatureKeyParser();
PublicKey key = keyParser.parsePublicKey(author.getPublicKey()); PublicKey key = keyParser.parsePublicKey(author.getPublicKey());
// Serialise the data to be signed // Serialise the data to be signed
ByteArrayOutputStream out = new ByteArrayOutputStream(); BdfList signed = BdfList.of(g.getId(), timestamp, parentId,
BdfWriter w = bdfWriterFactory.createWriter(out); authorList, contentType, body);
w.writeListStart();
w.writeRaw(m.getGroupId().getBytes());
w.writeLong(m.getTimestamp());
if (parent == null) w.writeNull();
else w.writeRaw(parent.getBytes());
writeAuthor(w, author);
w.writeString(contentType);
w.writeRaw(postBody);
w.writeListEnd();
// Verify the signature // Verify the signature
Signature signature = crypto.getSignature(); Signature signature = crypto.getSignature();
signature.initVerify(key); signature.initVerify(key);
signature.update(out.toByteArray()); signature.update(clientHelper.toByteArray(signed));
if (!signature.verify(sig)) { if (!signature.verify(sig)) {
LOG.info("Invalid signature"); LOG.info("Invalid signature");
return null; return null;
} }
} catch (GeneralSecurityException e) {
LOG.info("Invalid public key");
return null;
} }
// Return the metadata
BdfDictionary d = new BdfDictionary();
d.put("timestamp", m.getTimestamp());
if (parent != null) d.put("parent", parent.getBytes());
if (author != null) {
BdfDictionary d1 = new BdfDictionary();
d1.put("id", author.getId().getBytes());
d1.put("name", author.getName());
d1.put("publicKey", author.getPublicKey());
d.put("author", d1);
}
d.put("contentType", contentType);
d.put("read", false);
return metadataEncoder.encode(d);
} catch (IOException e) {
LOG.info("Invalid forum post");
return null;
} catch (GeneralSecurityException e) {
LOG.info("Invalid public key");
return null;
} }
} // Return the metadata
BdfDictionary meta = new BdfDictionary();
private void writeAuthor(BdfWriter w, Author a) throws IOException { meta.put("timestamp", timestamp);
w.writeListStart(); if (parentId != null) meta.put("parent", parentId);
w.writeString(a.getName()); if (author != null) {
w.writeRaw(a.getPublicKey()); BdfDictionary authorMeta = new BdfDictionary();
w.writeListEnd(); authorMeta.put("id", author.getId().getBytes());
authorMeta.put("name", author.getName());
authorMeta.put("publicKey", author.getPublicKey());
meta.put("author", authorMeta);
}
meta.put("contentType", contentType);
meta.put("read", false);
return meta;
} }
} }

View File

@@ -3,8 +3,8 @@ package org.briarproject.messaging;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageFactory;
@@ -24,10 +24,10 @@ public class MessagingModule extends AbstractModule {
@Provides @Singleton @Provides @Singleton
PrivateMessageValidator getValidator(ValidationManager validationManager, PrivateMessageValidator getValidator(ValidationManager validationManager,
BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder, ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock) { Clock clock) {
PrivateMessageValidator validator = new PrivateMessageValidator( PrivateMessageValidator validator = new PrivateMessageValidator(
bdfReaderFactory, metadataEncoder, clock); clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, validator);
return validator; return validator;
} }

View File

@@ -2,83 +2,45 @@ package org.briarproject.messaging;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.UniqueId; import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageValidator;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfMessageValidator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.logging.Logger;
import static org.briarproject.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH; import static org.briarproject.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH; import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
class PrivateMessageValidator implements MessageValidator { class PrivateMessageValidator extends BdfMessageValidator {
private static final Logger LOG = PrivateMessageValidator(ClientHelper clientHelper,
Logger.getLogger(PrivateMessageValidator.class.getName());
private final BdfReaderFactory bdfReaderFactory;
private final MetadataEncoder metadataEncoder;
private final Clock clock;
PrivateMessageValidator(BdfReaderFactory bdfReaderFactory,
MetadataEncoder metadataEncoder, Clock clock) { MetadataEncoder metadataEncoder, Clock clock) {
this.bdfReaderFactory = bdfReaderFactory; super(clientHelper, metadataEncoder, clock);
this.metadataEncoder = metadataEncoder;
this.clock = clock;
} }
@Override @Override
public Metadata validateMessage(Message m, Group g) { protected BdfDictionary validateMessage(BdfList message, Group g,
// Reject the message if it's too far in the future long timestamp) throws FormatException {
long now = clock.currentTimeMillis(); // Parent ID, content type, private message body
if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) { checkSize(message, 3);
LOG.info("Timestamp is too far in the future"); // Parent ID is optional
return null; byte[] parentId = message.getOptionalRaw(0);
} checkLength(parentId, UniqueId.LENGTH);
try { // Content type
// Parse the message body String contentType = message.getString(1);
byte[] raw = m.getRaw(); checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
ByteArrayInputStream in = new ByteArrayInputStream(raw, // Private message body
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH); byte[] body = message.getRaw(2);
BdfReader r = bdfReaderFactory.createReader(in); checkLength(body, 0, MAX_PRIVATE_MESSAGE_BODY_LENGTH);
MessageId parent = null; // Return the metadata
r.readListStart(); BdfDictionary meta = new BdfDictionary();
// Read the parent ID, if any meta.put("timestamp", timestamp);
if (r.hasRaw()) { if (parentId != null) meta.put("parent", parentId);
byte[] id = r.readRaw(UniqueId.LENGTH); meta.put("contentType", contentType);
if (id.length < UniqueId.LENGTH) throw new FormatException(); meta.put("local", false);
parent = new MessageId(id); meta.put("read", false);
} else { return meta;
r.readNull();
}
// Read the content type
String contentType = r.readString(MAX_CONTENT_TYPE_LENGTH);
// Read the private message body
r.readRaw(MAX_PRIVATE_MESSAGE_BODY_LENGTH);
r.readListEnd();
if (!r.eof()) throw new FormatException();
// Return the metadata
BdfDictionary d = new BdfDictionary();
d.put("timestamp", m.getTimestamp());
if (parent != null) d.put("parent", parent.getBytes());
d.put("contentType", contentType);
d.put("local", false);
d.put("read", false);
return metadataEncoder.encode(d);
} catch (IOException e) {
LOG.info("Invalid private message");
return null;
}
} }
} }

View File

@@ -3,8 +3,8 @@ package org.briarproject.properties;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.properties.TransportPropertyManager; import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
@@ -21,10 +21,10 @@ public class PropertiesModule extends AbstractModule {
@Provides @Singleton @Provides @Singleton
TransportPropertyValidator getValidator(ValidationManager validationManager, TransportPropertyValidator getValidator(ValidationManager validationManager,
BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder, ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock) { Clock clock) {
TransportPropertyValidator validator = new TransportPropertyValidator( TransportPropertyValidator validator = new TransportPropertyValidator(
bdfReaderFactory, metadataEncoder, clock); clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, validator);
return validator; return validator;
} }

View File

@@ -2,82 +2,52 @@ package org.briarproject.properties;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.UniqueId; import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageValidator;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfMessageValidator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.logging.Logger;
import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH; import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT; import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
class TransportPropertyValidator implements MessageValidator { class TransportPropertyValidator extends BdfMessageValidator {
private static final Logger LOG = TransportPropertyValidator(ClientHelper clientHelper,
Logger.getLogger(TransportPropertyValidator.class.getName());
private final BdfReaderFactory bdfReaderFactory;
private final MetadataEncoder metadataEncoder;
private final Clock clock;
TransportPropertyValidator(BdfReaderFactory bdfReaderFactory,
MetadataEncoder metadataEncoder, Clock clock) { MetadataEncoder metadataEncoder, Clock clock) {
this.bdfReaderFactory = bdfReaderFactory; super(clientHelper, metadataEncoder, clock);
this.metadataEncoder = metadataEncoder;
this.clock = clock;
} }
@Override @Override
public Metadata validateMessage(Message m, Group g) { protected BdfDictionary validateMessage(BdfList message, Group g,
// Reject the message if it's too far in the future long timestamp) throws FormatException {
long now = clock.currentTimeMillis(); // Device ID, transport ID, version, properties
if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) { checkSize(message, 4);
LOG.info("Timestamp is too far in the future"); // Device ID
return null; byte[] deviceId = message.getRaw(0);
} checkLength(deviceId, UniqueId.LENGTH);
try { // Transport ID
// Parse the message body String transportId = message.getString(1);
byte[] raw = m.getRaw(); checkLength(transportId, 1, MAX_TRANSPORT_ID_LENGTH);
ByteArrayInputStream in = new ByteArrayInputStream(raw, // Version
MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH); long version = message.getLong(2);
BdfReader r = bdfReaderFactory.createReader(in); if (version < 0) throw new FormatException();
r.readListStart(); // Properties
byte[] deviceId = r.readRaw(UniqueId.LENGTH); BdfDictionary dictionary = message.getDictionary(3);
if (deviceId.length != UniqueId.LENGTH) throw new FormatException(); checkSize(dictionary, 0, MAX_PROPERTIES_PER_TRANSPORT);
String transportId = r.readString(MAX_TRANSPORT_ID_LENGTH); for (String key : dictionary.keySet()) {
if (transportId.length() == 0) throw new FormatException(); checkLength(key, 0, MAX_PROPERTY_LENGTH);
long version = r.readLong(); String value = dictionary.getString(key);
if (version < 0) throw new FormatException(); checkLength(value, 0, MAX_PROPERTY_LENGTH);
r.readDictionaryStart();
for (int i = 0; !r.hasDictionaryEnd(); i++) {
if (i == MAX_PROPERTIES_PER_TRANSPORT)
throw new FormatException();
r.readString(MAX_PROPERTY_LENGTH);
r.readString(MAX_PROPERTY_LENGTH);
}
r.readDictionaryEnd();
r.readListEnd();
if (!r.eof()) throw new FormatException();
// Return the metadata
BdfDictionary d = new BdfDictionary();
d.put("transportId", transportId);
d.put("version", version);
d.put("local", false);
return metadataEncoder.encode(d);
} catch (IOException e) {
LOG.info("Invalid transport update");
return null;
} }
// Return the metadata
BdfDictionary meta = new BdfDictionary();
meta.put("transportId", transportId);
meta.put("version", version);
meta.put("local", false);
return meta;
} }
} }