mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Merge branch '591-add-new-message-types-to-blogvalidator' into 'master'
Validate New Messages for Reblogging and Comments of Blog Posts Closes #591 See merge request !279
This commit is contained in:
@@ -29,16 +29,19 @@ public interface BlogConstants {
|
||||
String BLOG_PUBLIC_KEY = "blogPublicKey";
|
||||
|
||||
// Metadata keys
|
||||
String KEY_TYPE = "type";
|
||||
String KEY_DESCRIPTION = "description";
|
||||
String KEY_TITLE = "title";
|
||||
String KEY_TIMESTAMP = "timestamp";
|
||||
String KEY_TIME_RECEIVED = "timeReceived";
|
||||
String KEY_PARENT = "parent";
|
||||
String KEY_AUTHOR_ID = "id";
|
||||
String KEY_AUTHOR_NAME = "name";
|
||||
String KEY_PUBLIC_KEY = "publicKey";
|
||||
String KEY_AUTHOR = "author";
|
||||
String KEY_CONTENT_TYPE = "contentType";
|
||||
String KEY_READ = "read";
|
||||
String KEY_COMMENT = "comment";
|
||||
String KEY_ORIGINAL_MSG_ID = "originalMessageId";
|
||||
String KEY_CURRENT_MSG_ID = "currentMessageId";
|
||||
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ public class BlogPostHeader extends PostHeader {
|
||||
private final long timeReceived;
|
||||
|
||||
public BlogPostHeader(@Nullable String title, @NotNull MessageId id,
|
||||
@Nullable MessageId parentId, long timestamp, long timeReceived,
|
||||
@NotNull Author author, @NotNull Status authorStatus,
|
||||
@NotNull String contentType, boolean read) {
|
||||
super(id, parentId, timestamp, author, authorStatus, contentType, read);
|
||||
long timestamp, long timeReceived, @NotNull Author author,
|
||||
@NotNull Status authorStatus, @NotNull String contentType,
|
||||
boolean read) {
|
||||
super(id, null, timestamp, author, authorStatus, contentType, read);
|
||||
|
||||
this.title = title;
|
||||
this.timeReceived = timeReceived;
|
||||
|
||||
33
briar-api/src/org/briarproject/api/blogs/MessageType.java
Normal file
33
briar-api/src/org/briarproject/api/blogs/MessageType.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package org.briarproject.api.blogs;
|
||||
|
||||
public enum MessageType {
|
||||
POST(0),
|
||||
COMMENT(1),
|
||||
WRAPPED_POST(2),
|
||||
WRAPPED_COMMENT(3);
|
||||
|
||||
int value;
|
||||
|
||||
MessageType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static MessageType valueOf(int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
return POST;
|
||||
case 1:
|
||||
return COMMENT;
|
||||
case 2:
|
||||
return WRAPPED_POST;
|
||||
case 3:
|
||||
return WRAPPED_COMMENT;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,6 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
|
||||
@@ -240,7 +239,6 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
meta = new BdfDictionary();
|
||||
if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
|
||||
meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
|
||||
if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
|
||||
|
||||
Author a = p.getAuthor();
|
||||
BdfDictionary authorMeta = new BdfDictionary();
|
||||
@@ -409,9 +407,6 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
String title = meta.getOptionalString(KEY_TITLE);
|
||||
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
||||
long timeReceived = meta.getLong(KEY_TIME_RECEIVED, timestamp);
|
||||
MessageId parentId = null;
|
||||
if (meta.containsKey(KEY_PARENT))
|
||||
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
||||
|
||||
BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
|
||||
AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
|
||||
@@ -427,7 +422,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
|
||||
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
||||
boolean read = meta.getBoolean(KEY_READ);
|
||||
return new BlogPostHeader(title, id, parentId, timestamp, timeReceived,
|
||||
author, authorStatus, contentType, read);
|
||||
return new BlogPostHeader(title, id, timestamp, timeReceived, author,
|
||||
authorStatus, contentType, read);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.UniqueId;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogFactory;
|
||||
import org.briarproject.api.blogs.MessageType;
|
||||
import org.briarproject.api.clients.BdfMessageContext;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
@@ -16,8 +17,10 @@ import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.InvalidMessageException;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageFactory;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.clients.BdfMessageValidator;
|
||||
@@ -29,13 +32,16 @@ import java.util.Collections;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CURRENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
@@ -44,14 +50,19 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH
|
||||
class BlogPostValidator extends BdfMessageValidator {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final GroupFactory groupFactory;
|
||||
private final MessageFactory messageFactory;
|
||||
private final BlogFactory blogFactory;
|
||||
|
||||
BlogPostValidator(CryptoComponent crypto, BlogFactory blogFactory,
|
||||
BlogPostValidator(CryptoComponent crypto, GroupFactory groupFactory,
|
||||
MessageFactory messageFactory, BlogFactory blogFactory,
|
||||
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||
Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
|
||||
this.crypto = crypto;
|
||||
this.groupFactory = groupFactory;
|
||||
this.messageFactory = messageFactory;
|
||||
this.blogFactory = blogFactory;
|
||||
}
|
||||
|
||||
@@ -59,14 +70,51 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
|
||||
BdfMessageContext c;
|
||||
|
||||
// TODO Remove! For Temporary Backwards Compatibility only!
|
||||
if (body.get(0) instanceof BdfList) {
|
||||
c = validatePost(m, g, body);
|
||||
addMessageMetadata(c, m.getTimestamp());
|
||||
return c;
|
||||
}
|
||||
|
||||
int type = body.getLong(0).intValue();
|
||||
body.removeElementAt(0);
|
||||
switch (MessageType.valueOf(type)) {
|
||||
case POST:
|
||||
c = validatePost(m, g, body);
|
||||
addMessageMetadata(c, m.getTimestamp());
|
||||
break;
|
||||
case COMMENT:
|
||||
c = validateComment(m, g, body);
|
||||
addMessageMetadata(c, m.getTimestamp());
|
||||
break;
|
||||
case WRAPPED_POST:
|
||||
c = validateWrappedPost(m, g, body);
|
||||
break;
|
||||
case WRAPPED_COMMENT:
|
||||
c = validateWrappedComment(m, g, body);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidMessageException("Unknown Message Type");
|
||||
}
|
||||
c.getDictionary().put(KEY_TYPE, type);
|
||||
return c;
|
||||
}
|
||||
|
||||
private BdfMessageContext validatePost(Message m, Group g, BdfList body)
|
||||
throws InvalidMessageException, FormatException {
|
||||
|
||||
// Content, Signature
|
||||
checkSize(body, 2);
|
||||
BdfList content = body.getList(0);
|
||||
|
||||
// Content: Parent ID, content type, title (optional), post body,
|
||||
// Content: content type, title (optional), post body,
|
||||
// attachments (optional)
|
||||
checkSize(content, 5);
|
||||
// Parent ID is optional
|
||||
// TODO remove when breaking backwards compatibility
|
||||
byte[] parent = content.getOptionalRaw(0);
|
||||
checkLength(parent, UniqueId.LENGTH);
|
||||
// Content type
|
||||
@@ -81,23 +129,166 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
byte[] postBody = content.getRaw(3);
|
||||
checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
|
||||
// Attachments
|
||||
BdfDictionary attachments = content.getOptionalDictionary(4);
|
||||
// TODO handle attachments somehow
|
||||
content.getOptionalDictionary(4);
|
||||
|
||||
// Verify Signature
|
||||
byte[] sig = body.getRaw(1);
|
||||
checkLength(sig, 1, MAX_SIGNATURE_LENGTH);
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), content);
|
||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||
Author a = b.getAuthor();
|
||||
verifySignature(sig, a.getPublicKey(), signed);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
if (title != null) meta.put(KEY_TITLE, title);
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
|
||||
meta.put(KEY_CONTENT_TYPE, contentType);
|
||||
return new BdfMessageContext(meta, null);
|
||||
}
|
||||
|
||||
private BdfMessageContext validateComment(Message m, Group g, BdfList body)
|
||||
throws InvalidMessageException, FormatException {
|
||||
|
||||
// comment, parent_original_id, signature, parent_current_id
|
||||
checkSize(body, 4);
|
||||
|
||||
// Comment
|
||||
String comment = body.getOptionalString(0);
|
||||
checkLength(comment, 0, MAX_BLOG_POST_BODY_LENGTH);
|
||||
|
||||
// parent_original_id
|
||||
// The ID of a post or comment in this group or another group
|
||||
byte[] originalIdBytes = body.getRaw(1);
|
||||
checkLength(originalIdBytes, MessageId.LENGTH);
|
||||
MessageId originalId = new MessageId(originalIdBytes);
|
||||
|
||||
// Signature
|
||||
byte[] sig = body.getRaw(1);
|
||||
byte[] sig = body.getRaw(2);
|
||||
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
||||
// Verify the signature
|
||||
Author a;
|
||||
BdfList signed =
|
||||
BdfList.of(g.getId(), m.getTimestamp(), comment, originalId);
|
||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||
Author a = b.getAuthor();
|
||||
verifySignature(sig, a.getPublicKey(), signed);
|
||||
|
||||
// parent_current_id
|
||||
// The ID of a post, comment, wrapped post or wrapped comment in this
|
||||
// group, which had the ID parent_original_id in the group
|
||||
// where it was originally posted
|
||||
byte[] currentIdBytes = body.getRaw(3);
|
||||
checkLength(currentIdBytes, MessageId.LENGTH);
|
||||
MessageId currentId = new MessageId(currentIdBytes);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
if (comment != null) meta.put(KEY_COMMENT, comment);
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, originalId);
|
||||
meta.put(KEY_CURRENT_MSG_ID, currentId);
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
|
||||
Collection<MessageId> dependencies = Collections.singleton(currentId);
|
||||
return new BdfMessageContext(meta, dependencies);
|
||||
}
|
||||
|
||||
private BdfMessageContext validateWrappedPost(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
|
||||
// group descriptor, timestamp, content, signature
|
||||
checkSize(body, 4);
|
||||
|
||||
// Group Descriptor
|
||||
byte[] descriptor = body.getRaw(0);
|
||||
|
||||
// Timestamp of Wrapped Post
|
||||
long wTimestamp = body.getLong(1);
|
||||
|
||||
// Content of Wrapped Post
|
||||
BdfList content = body.getList(2);
|
||||
|
||||
// Signature of Wrapped Post
|
||||
byte[] signature = body.getRaw(3);
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// Get and Validate the Wrapped Message
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
BdfList wBodyList = BdfList.of(content, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
Message wMessage =
|
||||
messageFactory.createMessage(wGroup.getId(), wTimestamp, wBody);
|
||||
BdfMessageContext c = validatePost(wMessage, wGroup, wBodyList);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(KEY_TIMESTAMP, wTimestamp);
|
||||
meta.put(KEY_AUTHOR, c.getDictionary().getDictionary(KEY_AUTHOR));
|
||||
meta.put(KEY_CONTENT_TYPE,
|
||||
c.getDictionary().getString(KEY_CONTENT_TYPE));
|
||||
return new BdfMessageContext(meta, null);
|
||||
}
|
||||
|
||||
private BdfMessageContext validateWrappedComment(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
|
||||
// group descriptor, timestamp, comment, parent_original_id, signature,
|
||||
// parent_current_id
|
||||
checkSize(body, 6);
|
||||
|
||||
// Group Descriptor
|
||||
byte[] descriptor = body.getRaw(0);
|
||||
|
||||
// Timestamp of Wrapped Comment
|
||||
long wTimestamp = body.getLong(1);
|
||||
|
||||
// Body of Wrapped Comment
|
||||
String comment = body.getOptionalString(2);
|
||||
|
||||
// parent_original_id
|
||||
// Taken from the original comment
|
||||
byte[] originalIdBytes = body.getRaw(3);
|
||||
checkLength(originalIdBytes, MessageId.LENGTH);
|
||||
MessageId originalId = new MessageId(originalIdBytes);
|
||||
|
||||
// signature
|
||||
// Taken from the original comment
|
||||
byte[] signature = body.getRaw(4);
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// parent_current_id
|
||||
// The ID of a post, comment, wrapped post or wrapped comment in this
|
||||
// group, which had the ID parent_original_id in the group
|
||||
// where it was originally posted
|
||||
byte[] currentIdBytes = body.getRaw(5);
|
||||
checkLength(currentIdBytes, MessageId.LENGTH);
|
||||
MessageId currentId = new MessageId(currentIdBytes);
|
||||
|
||||
// Get and Validate the Wrapped Comment
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
BdfList wBodyList = BdfList.of(comment, originalId, signature,
|
||||
currentId);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
Message wMessage =
|
||||
messageFactory.createMessage(wGroup.getId(), wTimestamp, wBody);
|
||||
BdfMessageContext c = validateComment(wMessage, wGroup, wBodyList);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
Collection<MessageId> dependencies = Collections.singleton(currentId);
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, wMessage.getId());
|
||||
meta.put(KEY_CURRENT_MSG_ID, currentId);
|
||||
meta.put(KEY_TIMESTAMP, wTimestamp);
|
||||
if (comment != null) meta.put(KEY_COMMENT, comment);
|
||||
meta.put(KEY_AUTHOR, c.getDictionary().getDictionary(KEY_AUTHOR));
|
||||
return new BdfMessageContext(meta, dependencies);
|
||||
}
|
||||
|
||||
private void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
|
||||
throws InvalidMessageException {
|
||||
try {
|
||||
// Get the blog author
|
||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||
a = b.getAuthor();
|
||||
// Parse the public key
|
||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
||||
PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
|
||||
// Serialise the data to be signed
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), content);
|
||||
PublicKey key = keyParser.parsePublicKey(publicKey);
|
||||
// Verify the signature
|
||||
Signature signature = crypto.getSignature();
|
||||
signature.initVerify(key);
|
||||
@@ -107,26 +298,23 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new InvalidMessageException("Invalid public key");
|
||||
} catch (FormatException e) {
|
||||
throw new InvalidMessageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
Collection<MessageId> dependencies = null;
|
||||
if (title != null) meta.put(KEY_TITLE, title);
|
||||
BdfDictionary author = BdfDictionary.of(
|
||||
static BdfDictionary authorToBdfDictionary(Author a) {
|
||||
return BdfDictionary.of(
|
||||
new BdfEntry(KEY_AUTHOR_ID, a.getId()),
|
||||
new BdfEntry(KEY_AUTHOR_NAME, a.getName()),
|
||||
new BdfEntry(KEY_PUBLIC_KEY, a.getPublicKey())
|
||||
);
|
||||
meta.put(KEY_AUTHOR, author);
|
||||
meta.put(KEY_TIMESTAMP, m.getTimestamp());
|
||||
meta.put(KEY_TIME_RECEIVED, clock.currentTimeMillis());
|
||||
if (parent != null) {
|
||||
meta.put(KEY_PARENT, parent);
|
||||
dependencies = Collections.singletonList(new MessageId(parent));
|
||||
}
|
||||
meta.put(KEY_CONTENT_TYPE, contentType);
|
||||
meta.put(KEY_READ, false);
|
||||
return new BdfMessageContext(meta, dependencies);
|
||||
}
|
||||
|
||||
private void addMessageMetadata(BdfMessageContext c, long time) {
|
||||
c.getDictionary().put(KEY_TIMESTAMP, time);
|
||||
c.getDictionary().put(KEY_TIME_RECEIVED, clock.currentTimeMillis());
|
||||
c.getDictionary().put(KEY_READ, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.MessageFactory;
|
||||
import org.briarproject.api.sync.ValidationManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
|
||||
@@ -64,11 +65,13 @@ public class BlogsModule {
|
||||
@Singleton
|
||||
BlogPostValidator provideBlogPostValidator(
|
||||
ValidationManager validationManager, CryptoComponent crypto,
|
||||
GroupFactory groupFactory, MessageFactory messageFactory,
|
||||
BlogFactory blogFactory, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
|
||||
BlogPostValidator validator = new BlogPostValidator(crypto,
|
||||
blogFactory, clientHelper, metadataEncoder, clock);
|
||||
groupFactory, messageFactory, blogFactory, clientHelper,
|
||||
metadataEncoder, clock);
|
||||
validationManager.registerMessageValidator(CLIENT_ID, validator);
|
||||
|
||||
return validator;
|
||||
|
||||
@@ -18,9 +18,11 @@ import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.InvalidMessageException;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageFactory;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.system.SystemClock;
|
||||
@@ -34,10 +36,17 @@ import java.security.GeneralSecurityException;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CURRENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||
import static org.briarproject.api.blogs.MessageType.COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.POST;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -47,11 +56,17 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
private final Mockery context = new Mockery();
|
||||
private final Blog blog;
|
||||
private final Author author;
|
||||
private final BdfDictionary authorDict;
|
||||
private final ClientId clientId;
|
||||
private final byte[] descriptor;
|
||||
private final Group group;
|
||||
private final Message message;
|
||||
private final BlogPostValidator validator;
|
||||
private final CryptoComponent cryptoComponent =
|
||||
context.mock(CryptoComponent.class);
|
||||
private final GroupFactory groupFactory = context.mock(GroupFactory.class);
|
||||
private final MessageFactory messageFactory =
|
||||
context.mock(MessageFactory.class);
|
||||
private final BlogFactory blogFactory = context.mock(BlogFactory.class);
|
||||
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
|
||||
private final Clock clock = new SystemClock();
|
||||
@@ -61,12 +76,18 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
|
||||
public BlogPostValidatorTest() {
|
||||
GroupId groupId = new GroupId(TestUtils.getRandomId());
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
byte[] descriptor = TestUtils.getRandomBytes(12);
|
||||
clientId = BlogManagerImpl.CLIENT_ID;
|
||||
descriptor = TestUtils.getRandomBytes(42);
|
||||
group = new Group(groupId, clientId, descriptor);
|
||||
AuthorId authorId = new AuthorId(TestUtils.getRandomBytes(AuthorId.LENGTH));
|
||||
AuthorId authorId =
|
||||
new AuthorId(TestUtils.getRandomBytes(AuthorId.LENGTH));
|
||||
byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
|
||||
author = new Author(authorId, "Author", publicKey);
|
||||
authorDict = BdfDictionary.of(
|
||||
new BdfEntry(KEY_AUTHOR_ID, author.getId()),
|
||||
new BdfEntry(KEY_AUTHOR_NAME, author.getName()),
|
||||
new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey())
|
||||
);
|
||||
blog = new Blog(group, "Test Blog", "", author);
|
||||
|
||||
MessageId messageId = new MessageId(TestUtils.getRandomId());
|
||||
@@ -75,29 +96,27 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
message = new Message(messageId, group.getId(), timestamp, raw);
|
||||
|
||||
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
|
||||
validator = new BlogPostValidator(cryptoComponent, blogFactory,
|
||||
clientHelper, metadataEncoder, clock);
|
||||
validator = new BlogPostValidator(cryptoComponent, groupFactory,
|
||||
messageFactory, blogFactory, clientHelper, metadataEncoder,
|
||||
clock);
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateProperBlogPost()
|
||||
throws IOException, GeneralSecurityException {
|
||||
// Parent ID, content type, title (optional), post body, attachments
|
||||
// content type, title (optional), post body, attachments
|
||||
BdfList content = BdfList.of(null, contentType, null, body, null);
|
||||
final byte[] sigBytes = TestUtils.getRandomBytes(42);
|
||||
BdfList m = BdfList.of(content, sigBytes);
|
||||
BdfList m = BdfList.of(POST.getInt(), content, sigBytes);
|
||||
|
||||
expectCrypto(m, true);
|
||||
BdfList signed =
|
||||
BdfList.of(blog.getId(), message.getTimestamp(), content);
|
||||
expectCrypto(signed, sigBytes, true);
|
||||
final BdfDictionary result =
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
|
||||
assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
|
||||
BdfDictionary authorDict = BdfDictionary.of(
|
||||
new BdfEntry(KEY_AUTHOR_ID, author.getId()),
|
||||
new BdfEntry(KEY_AUTHOR_NAME, author.getName()),
|
||||
new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey())
|
||||
);
|
||||
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
|
||||
assertFalse(result.getBoolean(KEY_READ));
|
||||
context.assertIsSatisfied();
|
||||
@@ -107,7 +126,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
public void testValidateBlogPostWithoutAttachments()
|
||||
throws IOException, GeneralSecurityException {
|
||||
BdfList content = BdfList.of(null, contentType, null, body);
|
||||
BdfList m = BdfList.of(content, null);
|
||||
BdfList m = BdfList.of(POST.getInt(), content, null);
|
||||
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
}
|
||||
@@ -116,7 +135,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
public void testValidateBlogPostWithoutSignature()
|
||||
throws IOException, GeneralSecurityException {
|
||||
BdfList content = BdfList.of(null, contentType, null, body, null);
|
||||
BdfList m = BdfList.of(content, null);
|
||||
BdfList m = BdfList.of(POST.getInt(), content, null);
|
||||
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
}
|
||||
@@ -124,27 +143,149 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
@Test(expected = InvalidMessageException.class)
|
||||
public void testValidateBlogPostWithBadSignature()
|
||||
throws IOException, GeneralSecurityException {
|
||||
// Parent ID, content type, title (optional), post body, attachments
|
||||
// content type, title (optional), post body, attachments
|
||||
BdfList content = BdfList.of(null, contentType, null, body, null);
|
||||
final byte[] sigBytes = TestUtils.getRandomBytes(42);
|
||||
BdfList m = BdfList.of(content, sigBytes);
|
||||
BdfList m = BdfList.of(POST.getInt(), content, sigBytes);
|
||||
|
||||
expectCrypto(m, false);
|
||||
BdfList signed =
|
||||
BdfList.of(blog.getId(), message.getTimestamp(), content);
|
||||
expectCrypto(signed, sigBytes, false);
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
}
|
||||
|
||||
private void expectCrypto(BdfList m, final boolean pass)
|
||||
@Test
|
||||
public void testValidateProperBlogComment()
|
||||
throws IOException, GeneralSecurityException {
|
||||
// comment, parent_original_id, signature, parent_current_id
|
||||
String comment = "This is a blog comment";
|
||||
MessageId originalId = new MessageId(TestUtils.getRandomId());
|
||||
byte[] currentId = TestUtils.getRandomId();
|
||||
final byte[] sigBytes = TestUtils.getRandomBytes(42);
|
||||
BdfList m = BdfList.of(COMMENT.getInt(), comment, originalId,
|
||||
sigBytes, currentId);
|
||||
|
||||
BdfList signed =
|
||||
BdfList.of(blog.getId(), message.getTimestamp(), comment,
|
||||
originalId);
|
||||
expectCrypto(signed, sigBytes, true);
|
||||
final BdfDictionary result =
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
|
||||
assertEquals(comment, result.getString(KEY_COMMENT));
|
||||
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
|
||||
assertEquals(originalId.getBytes(), result.getRaw(KEY_ORIGINAL_MSG_ID));
|
||||
assertEquals(currentId, result.getRaw(KEY_CURRENT_MSG_ID));
|
||||
assertFalse(result.getBoolean(KEY_READ));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateProperEmptyBlogComment()
|
||||
throws IOException, GeneralSecurityException {
|
||||
// comment, parent_original_id, signature, parent_current_id
|
||||
MessageId originalId = new MessageId(TestUtils.getRandomId());
|
||||
byte[] currentId = TestUtils.getRandomId();
|
||||
final byte[] sigBytes = TestUtils.getRandomBytes(42);
|
||||
BdfList m = BdfList.of(COMMENT.getInt(), null, originalId, sigBytes,
|
||||
currentId);
|
||||
|
||||
BdfList signed =
|
||||
BdfList.of(blog.getId(), message.getTimestamp(), null,
|
||||
originalId);
|
||||
expectCrypto(signed, sigBytes, true);
|
||||
final BdfDictionary result =
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
|
||||
assertFalse(result.containsKey(KEY_COMMENT));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateProperWrappedPost()
|
||||
throws IOException, GeneralSecurityException {
|
||||
// group descriptor, timestamp, content, signature
|
||||
BdfList content = BdfList.of(null, contentType, null, body, null);
|
||||
final byte[] sigBytes = TestUtils.getRandomBytes(42);
|
||||
BdfList m =
|
||||
BdfList.of(WRAPPED_POST.getInt(), descriptor,
|
||||
message.getTimestamp(), content, sigBytes);
|
||||
|
||||
BdfList signed =
|
||||
BdfList.of(blog.getId(), message.getTimestamp(), content);
|
||||
expectCrypto(signed, sigBytes, true);
|
||||
|
||||
final BdfList originalList = BdfList.of(content, sigBytes);
|
||||
final byte[] originalBody = TestUtils.getRandomBytes(42);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(groupFactory).createGroup(clientId, descriptor);
|
||||
will(returnValue(blog.getGroup()));
|
||||
oneOf(clientHelper).toByteArray(originalList);
|
||||
will(returnValue(originalBody));
|
||||
oneOf(messageFactory)
|
||||
.createMessage(group.getId(), message.getTimestamp(),
|
||||
originalBody);
|
||||
will(returnValue(message));
|
||||
}});
|
||||
|
||||
final BdfDictionary result =
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
|
||||
assertEquals(contentType, result.getString(KEY_CONTENT_TYPE));
|
||||
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateProperWrappedComment()
|
||||
throws IOException, GeneralSecurityException {
|
||||
// group descriptor, timestamp, comment, parent_original_id, signature,
|
||||
// parent_current_id
|
||||
String comment = "This is another comment";
|
||||
MessageId originalId = new MessageId(TestUtils.getRandomId());
|
||||
final byte[] sigBytes = TestUtils.getRandomBytes(42);
|
||||
MessageId currentId = new MessageId(TestUtils.getRandomId());
|
||||
BdfList m = BdfList.of(WRAPPED_COMMENT.getInt(), descriptor,
|
||||
message.getTimestamp(), comment, originalId, sigBytes,
|
||||
currentId);
|
||||
|
||||
BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
|
||||
comment, originalId);
|
||||
expectCrypto(signed, sigBytes, true);
|
||||
|
||||
final BdfList originalList = BdfList.of(comment, originalId, sigBytes,
|
||||
currentId);
|
||||
final byte[] originalBody = TestUtils.getRandomBytes(42);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(groupFactory).createGroup(clientId, descriptor);
|
||||
will(returnValue(blog.getGroup()));
|
||||
oneOf(clientHelper).toByteArray(originalList);
|
||||
will(returnValue(originalBody));
|
||||
oneOf(messageFactory)
|
||||
.createMessage(group.getId(), message.getTimestamp(),
|
||||
originalBody);
|
||||
will(returnValue(message));
|
||||
}});
|
||||
|
||||
final BdfDictionary result =
|
||||
validator.validateMessage(message, group, m).getDictionary();
|
||||
|
||||
assertEquals(comment, result.getString(KEY_COMMENT));
|
||||
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
|
||||
assertEquals(
|
||||
message.getId().getBytes(), result.getRaw(KEY_ORIGINAL_MSG_ID));
|
||||
assertEquals(currentId.getBytes(), result.getRaw(KEY_CURRENT_MSG_ID));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private void expectCrypto(final BdfList signed, final byte[] sig,
|
||||
final boolean pass) throws IOException, GeneralSecurityException {
|
||||
final Signature signature = context.mock(Signature.class);
|
||||
final KeyParser keyParser = context.mock(KeyParser.class);
|
||||
final PublicKey publicKey = context.mock(PublicKey.class);
|
||||
|
||||
final BdfList content = m.getList(0);
|
||||
final byte[] sigBytes = m.getRaw(1);
|
||||
|
||||
final BdfList signed =
|
||||
BdfList.of(blog.getId(), message.getTimestamp(), content);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(blogFactory).parseBlog(group, "");
|
||||
will(returnValue(blog));
|
||||
@@ -156,9 +297,9 @@ public class BlogPostValidatorTest extends BriarTestCase {
|
||||
will(returnValue(signature));
|
||||
oneOf(signature).initVerify(publicKey);
|
||||
oneOf(clientHelper).toByteArray(signed);
|
||||
will(returnValue(sigBytes));
|
||||
oneOf(signature).update(sigBytes);
|
||||
oneOf(signature).verify(sigBytes);
|
||||
will(returnValue(sig));
|
||||
oneOf(signature).update(sig);
|
||||
oneOf(signature).verify(sig);
|
||||
will(returnValue(pass));
|
||||
}});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user