mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 06:39:54 +01:00
Implement first prototype of GroupMessageValidator
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
package org.briarproject.api.privategroup;
|
||||||
|
|
||||||
|
public enum MessageType {
|
||||||
|
NEW_MEMBER(0),
|
||||||
|
JOIN(1),
|
||||||
|
POST(2);
|
||||||
|
|
||||||
|
int value;
|
||||||
|
|
||||||
|
MessageType(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MessageType valueOf(int value) {
|
||||||
|
switch (value) {
|
||||||
|
case 0:
|
||||||
|
return NEW_MEMBER;
|
||||||
|
case 1:
|
||||||
|
return JOIN;
|
||||||
|
case 2:
|
||||||
|
return POST;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
package org.briarproject.privategroup;
|
package org.briarproject.privategroup;
|
||||||
|
|
||||||
|
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
|
||||||
|
|
||||||
interface Constants {
|
interface Constants {
|
||||||
|
|
||||||
// Database keys
|
// 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.FormatException;
|
||||||
import org.briarproject.api.clients.BdfMessageContext;
|
import org.briarproject.api.clients.BdfMessageContext;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
import org.briarproject.api.data.MetadataEncoder;
|
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.Group;
|
||||||
import org.briarproject.api.sync.InvalidMessageException;
|
import org.briarproject.api.sync.InvalidMessageException;
|
||||||
import org.briarproject.api.sync.Message;
|
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.api.system.Clock;
|
||||||
import org.briarproject.clients.BdfMessageValidator;
|
import org.briarproject.clients.BdfMessageValidator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
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 {
|
class GroupMessageValidator extends BdfMessageValidator {
|
||||||
|
|
||||||
private final CryptoComponent crypto;
|
private final PrivateGroupFactory groupFactory;
|
||||||
private final AuthorFactory authorFactory;
|
|
||||||
|
|
||||||
GroupMessageValidator(CryptoComponent crypto, AuthorFactory authorFactory,
|
GroupMessageValidator(PrivateGroupFactory groupFactory,
|
||||||
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||||
Clock clock) {
|
Clock clock) {
|
||||||
super(clientHelper, metadataEncoder, clock);
|
super(clientHelper, metadataEncoder, clock);
|
||||||
this.crypto = crypto;
|
this.groupFactory = groupFactory;
|
||||||
this.authorFactory = authorFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||||
BdfList body) throws InvalidMessageException, FormatException {
|
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();
|
BdfDictionary meta = new BdfDictionary();
|
||||||
Collection<MessageId> dependencies = Collections.emptyList();
|
|
||||||
return new BdfMessageContext(meta, dependencies);
|
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);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package org.briarproject.privategroup;
|
|||||||
|
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
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.data.MetadataEncoder;
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
import org.briarproject.api.identity.AuthorFactory;
|
|
||||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.api.messaging.ConversationManager;
|
import org.briarproject.api.messaging.ConversationManager;
|
||||||
import org.briarproject.api.privategroup.GroupMessageFactory;
|
import org.briarproject.api.privategroup.GroupMessageFactory;
|
||||||
@@ -59,11 +57,11 @@ public class PrivateGroupModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
GroupMessageValidator provideGroupMessageValidator(
|
GroupMessageValidator provideGroupMessageValidator(
|
||||||
ValidationManager validationManager, CryptoComponent crypto,
|
PrivateGroupFactory groupFactory,
|
||||||
AuthorFactory authorFactory, ClientHelper clientHelper,
|
ValidationManager validationManager, ClientHelper clientHelper,
|
||||||
MetadataEncoder metadataEncoder, Clock clock) {
|
MetadataEncoder metadataEncoder, Clock clock) {
|
||||||
GroupMessageValidator validator = new GroupMessageValidator(crypto,
|
GroupMessageValidator validator = new GroupMessageValidator(
|
||||||
authorFactory, clientHelper, metadataEncoder, clock);
|
groupFactory, clientHelper, metadataEncoder, clock);
|
||||||
validationManager.registerMessageValidator(
|
validationManager.registerMessageValidator(
|
||||||
PrivateGroupManagerImpl.CLIENT_ID, validator);
|
PrivateGroupManagerImpl.CLIENT_ID, validator);
|
||||||
return validator;
|
return validator;
|
||||||
|
|||||||
Reference in New Issue
Block a user