mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
Implement first prototype of GroupMessageValidator
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
package org.briarproject.privategroup;
|
||||
|
||||
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
|
||||
|
||||
interface Constants {
|
||||
|
||||
// Database keys
|
||||
String KEY_READ = "read";
|
||||
String KEY_TYPE = "type";
|
||||
String KEY_TIMESTAMP = "timestamp";
|
||||
String KEY_READ = MSG_KEY_READ;
|
||||
String KEY_AUTHOR_NAME = "authorName";
|
||||
String KEY_AUTHOR_PUBLIC_KEY = "authorPublicKey";
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package org.briarproject.privategroup;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.BdfMessageContext;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.privategroup.MessageType;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupFactory;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.InvalidMessageException;
|
||||
import org.briarproject.api.sync.Message;
|
||||
@@ -15,29 +16,178 @@ import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.clients.BdfMessageValidator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
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.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
|
||||
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_NAME;
|
||||
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_PUBLIC_KEY;
|
||||
import static org.briarproject.privategroup.Constants.KEY_READ;
|
||||
import static org.briarproject.privategroup.Constants.KEY_TIMESTAMP;
|
||||
import static org.briarproject.privategroup.Constants.KEY_TYPE;
|
||||
|
||||
class GroupMessageValidator extends BdfMessageValidator {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final AuthorFactory authorFactory;
|
||||
private final PrivateGroupFactory groupFactory;
|
||||
|
||||
GroupMessageValidator(CryptoComponent crypto, AuthorFactory authorFactory,
|
||||
GroupMessageValidator(PrivateGroupFactory groupFactory,
|
||||
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||
Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
this.crypto = crypto;
|
||||
this.authorFactory = authorFactory;
|
||||
this.groupFactory = groupFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
|
||||
checkSize(body, 4, 7);
|
||||
|
||||
// message type (int)
|
||||
int type = body.getLong(0).intValue();
|
||||
body.removeElementAt(0);
|
||||
|
||||
// member_name (string)
|
||||
String member_name = body.getString(0);
|
||||
checkLength(member_name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||
|
||||
// member_public_key (raw)
|
||||
byte[] member_public_key = body.getRaw(1);
|
||||
checkLength(member_public_key, 1, MAX_PUBLIC_KEY_LENGTH);
|
||||
|
||||
BdfMessageContext c;
|
||||
switch (MessageType.valueOf(type)) {
|
||||
case NEW_MEMBER:
|
||||
c = validateNewMember(m, g, body, member_name,
|
||||
member_public_key);
|
||||
addMessageMetadata(c, member_name, member_public_key,
|
||||
m.getTimestamp());
|
||||
break;
|
||||
case JOIN:
|
||||
c = validateJoin(m, g, body, member_name, member_public_key);
|
||||
addMessageMetadata(c, member_name, member_public_key,
|
||||
m.getTimestamp());
|
||||
break;
|
||||
case POST:
|
||||
c = validatePost(m, g, body, member_name, member_public_key);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidMessageException("Unknown Message Type");
|
||||
}
|
||||
c.getDictionary().put(KEY_TYPE, type);
|
||||
return c;
|
||||
}
|
||||
|
||||
private BdfMessageContext validateNewMember(Message m, Group g,
|
||||
BdfList body, String member_name, byte[] member_public_key)
|
||||
throws InvalidMessageException, FormatException {
|
||||
|
||||
// The content is a BDF list with three elements
|
||||
checkSize(body, 3);
|
||||
|
||||
// signature (raw)
|
||||
// signature with the creator's private key over a list with 4 elements
|
||||
byte[] signature = body.getRaw(2);
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// Verify Signature
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
|
||||
member_public_key);
|
||||
PrivateGroup group = groupFactory.parsePrivateGroup(g);
|
||||
byte[] creatorPublicKey = group.getAuthor().getPublicKey();
|
||||
clientHelper.verifySignature(signature, creatorPublicKey, signed);
|
||||
|
||||
// Return the metadata and no dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
return new BdfMessageContext(meta);
|
||||
}
|
||||
|
||||
private BdfMessageContext validateJoin(Message m, Group g, BdfList body,
|
||||
String member_name, byte[] member_public_key)
|
||||
throws InvalidMessageException, FormatException {
|
||||
|
||||
// The content is a BDF list with four elements
|
||||
checkSize(body, 4);
|
||||
|
||||
// new_member_id (raw)
|
||||
// the identifier of a new member message
|
||||
// with the same member_name and member_public_key
|
||||
byte[] new_member_id = body.getRaw(2);
|
||||
checkLength(new_member_id, MessageId.LENGTH);
|
||||
|
||||
// signature (raw)
|
||||
// a signature with the member's private key over a list with 5 elements
|
||||
byte[] signature = body.getRaw(3);
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// Verify Signature
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
|
||||
member_public_key, new_member_id);
|
||||
clientHelper.verifySignature(signature, member_public_key, signed);
|
||||
|
||||
// The new member message is a dependency
|
||||
Collection<MessageId> dependencies =
|
||||
Collections.singleton(new MessageId(new_member_id));
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
Collection<MessageId> dependencies = Collections.emptyList();
|
||||
return new BdfMessageContext(meta, dependencies);
|
||||
}
|
||||
|
||||
private BdfMessageContext validatePost(Message m, Group g, BdfList body,
|
||||
String member_name, byte[] member_public_key)
|
||||
throws InvalidMessageException, FormatException {
|
||||
|
||||
// The content is a BDF list with six elements
|
||||
checkSize(body, 6);
|
||||
|
||||
// parent_id (raw or null)
|
||||
// the identifier of the post to which this is a reply, if any
|
||||
byte[] parent_id = body.getOptionalRaw(2);
|
||||
if (parent_id != null) {
|
||||
checkLength(parent_id, MessageId.LENGTH);
|
||||
}
|
||||
|
||||
// previous_message_id (raw)
|
||||
// the identifier of the member's previous post or join message
|
||||
byte[] previous_message_id = body.getRaw(3);
|
||||
checkLength(previous_message_id, MessageId.LENGTH);
|
||||
|
||||
// content (string)
|
||||
String content = body.getString(4);
|
||||
checkLength(content, 0, MAX_GROUP_POST_BODY_LENGTH);
|
||||
|
||||
// signature (raw)
|
||||
// a signature with the member's private key over a list with 7 elements
|
||||
byte[] signature = body.getRaw(5);
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// Verify Signature
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
|
||||
member_public_key, parent_id, previous_message_id, content);
|
||||
clientHelper.verifySignature(signature, member_public_key, signed);
|
||||
|
||||
// The parent post, if any,
|
||||
// and the member's previous message are dependencies
|
||||
Collection<MessageId> dependencies = new ArrayList<MessageId>();
|
||||
if (parent_id != null) dependencies.add(new MessageId(parent_id));
|
||||
dependencies.add(new MessageId(previous_message_id));
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
return new BdfMessageContext(meta, dependencies);
|
||||
}
|
||||
|
||||
private void addMessageMetadata(BdfMessageContext c, String authorName,
|
||||
byte[] pubKey, long time) {
|
||||
c.getDictionary().put(KEY_TIMESTAMP, time);
|
||||
c.getDictionary().put(KEY_READ, false);
|
||||
c.getDictionary().put(KEY_AUTHOR_NAME, authorName);
|
||||
c.getDictionary().put(KEY_AUTHOR_PUBLIC_KEY, pubKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -198,6 +198,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
|
||||
|
||||
trackIncomingMessage(txn, m);
|
||||
|
||||
|
||||
// TODO POST timestamp must be greater than the timestamps of the parent post, if any, and the member's previous message
|
||||
|
||||
// TODO JOIN timestamp must be equal to the timestamp of the new member message.
|
||||
// TODO JOIN new_member_id must be the identifier of a NEW_MEMBER message with the same member_name and member_public_key
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@ package org.briarproject.privategroup;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.messaging.ConversationManager;
|
||||
import org.briarproject.api.privategroup.GroupMessageFactory;
|
||||
@@ -59,11 +57,11 @@ public class PrivateGroupModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
GroupMessageValidator provideGroupMessageValidator(
|
||||
ValidationManager validationManager, CryptoComponent crypto,
|
||||
AuthorFactory authorFactory, ClientHelper clientHelper,
|
||||
PrivateGroupFactory groupFactory,
|
||||
ValidationManager validationManager, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
GroupMessageValidator validator = new GroupMessageValidator(crypto,
|
||||
authorFactory, clientHelper, metadataEncoder, clock);
|
||||
GroupMessageValidator validator = new GroupMessageValidator(
|
||||
groupFactory, clientHelper, metadataEncoder, clock);
|
||||
validationManager.registerMessageValidator(
|
||||
PrivateGroupManagerImpl.CLIENT_ID, validator);
|
||||
return validator;
|
||||
|
||||
Reference in New Issue
Block a user