mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 14:49:53 +01:00
Superclass for validating messages that are BDF lists.
This commit is contained in:
114
briar-core/src/org/briarproject/clients/BdfMessageValidator.java
Normal file
114
briar-core/src/org/briarproject/clients/BdfMessageValidator.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user