Files
briar/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
Torsten Grote c86d971166 Require a label for signing
This adds a sign() and a verify() method to the CryptoComponent
that take a mandatory label argument to ensure that signatures can't be
repurposed.
2016-11-18 11:05:19 -02:00

272 lines
10 KiB
Java

package org.briarproject.blogs;
import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
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.Message;
import org.briarproject.api.sync.MessageFactory;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock;
import org.briarproject.system.SystemClock;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.io.IOException;
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_ORIGINAL_MSG_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_PARENT_MSG_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_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.BlogPostFactory.SIGNING_LABEL_COMMENT;
import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_POST;
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;
public class BlogPostValidatorTest extends BriarTestCase {
private final Mockery context = new Mockery();
private final Blog blog;
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 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 Author author;
private final String body = TestUtils.getRandomString(42);
public BlogPostValidatorTest() {
GroupId groupId = new GroupId(TestUtils.getRandomId());
clientId = BlogManagerImpl.CLIENT_ID;
descriptor = TestUtils.getRandomBytes(42);
group = new Group(groupId, clientId, descriptor);
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, author);
MessageId messageId = new MessageId(TestUtils.getRandomId());
long timestamp = System.currentTimeMillis();
byte[] raw = TestUtils.getRandomBytes(123);
message = new Message(messageId, group.getId(), timestamp, raw);
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
Clock clock = new SystemClock();
validator =
new BlogPostValidator(groupFactory, messageFactory, blogFactory,
clientHelper, metadataEncoder, clock);
context.assertIsSatisfied();
}
@Test
public void testValidateProperBlogPost()
throws IOException, GeneralSecurityException {
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m = BdfList.of(POST.getInt(), body, sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
assertFalse(result.getBoolean(KEY_READ));
context.assertIsSatisfied();
}
@Test(expected = FormatException.class)
public void testValidateBlogPostWithoutAttachments()
throws IOException, GeneralSecurityException {
BdfList content = BdfList.of(null, null, body);
BdfList m = BdfList.of(POST.getInt(), content, null);
validator.validateMessage(message, group, m).getDictionary();
}
@Test(expected = FormatException.class)
public void testValidateBlogPostWithoutSignature()
throws IOException, GeneralSecurityException {
BdfList content = BdfList.of(null, null, body, null);
BdfList m = BdfList.of(POST.getInt(), content, null);
validator.validateMessage(message, group, m).getDictionary();
}
@Test
public void testValidateProperBlogComment()
throws IOException, GeneralSecurityException {
// comment, parent_original_id, parent_id, signature
String comment = "This is a blog comment";
MessageId pOriginalId = new MessageId(TestUtils.getRandomId());
MessageId currentId = new MessageId(TestUtils.getRandomId());
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m =
BdfList.of(COMMENT.getInt(), comment, pOriginalId, currentId,
sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), comment,
pOriginalId, currentId);
expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary();
assertEquals(comment, result.getString(KEY_COMMENT));
assertEquals(authorDict, result.getDictionary(KEY_AUTHOR));
assertEquals(pOriginalId.getBytes(),
result.getRaw(KEY_ORIGINAL_PARENT_MSG_ID));
assertEquals(currentId.getBytes(), result.getRaw(KEY_PARENT_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());
MessageId currentId = new MessageId(TestUtils.getRandomId());
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m =
BdfList.of(COMMENT.getInt(), null, originalId, currentId,
sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), null,
originalId, currentId);
expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
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
final byte[] sigBytes = TestUtils.getRandomBytes(42);
BdfList m =
BdfList.of(WRAPPED_POST.getInt(), descriptor,
message.getTimestamp(), body, sigBytes);
BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
final BdfList originalList = BdfList.of(POST.getInt(), body, 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(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());
MessageId oldId = 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, oldId, sigBytes,
currentId);
BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
comment, originalId, oldId);
expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
final BdfList originalList = BdfList.of(COMMENT.getInt(), comment,
originalId, oldId, 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(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_PARENT_MSG_ID));
context.assertIsSatisfied();
}
private void expectCrypto(final String label, final BdfList signed,
final byte[] sig) throws IOException, GeneralSecurityException {
context.checking(new Expectations() {{
oneOf(blogFactory).parseBlog(group);
will(returnValue(blog));
oneOf(clientHelper)
.verifySignature(label, sig, author.getPublicKey(), signed);
}});
}
}