Added some more private group validation tests, found a bug.

This commit is contained in:
akwizgran
2016-11-29 17:46:46 +00:00
parent b37a7531ca
commit 050111a994
2 changed files with 358 additions and 247 deletions

View File

@@ -9,7 +9,6 @@ import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.privategroup.MessageType;
import org.briarproject.api.privategroup.PrivateGroup; import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.privategroup.PrivateGroupFactory; import org.briarproject.api.privategroup.PrivateGroupFactory;
import org.briarproject.api.privategroup.invitation.GroupInvitationFactory; import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
@@ -79,17 +78,14 @@ class GroupMessageValidator extends BdfMessageValidator {
Author member = authorFactory.createAuthor(memberName, memberPublicKey); Author member = authorFactory.createAuthor(memberName, memberPublicKey);
BdfMessageContext c; BdfMessageContext c;
switch (MessageType.valueOf(type)) { if (type == JOIN.getInt()) {
case JOIN: c = validateJoin(m, g, body, member);
c = validateJoin(m, g, body, member); addMessageMetadata(c, member, m.getTimestamp());
addMessageMetadata(c, member, m.getTimestamp()); } else if (type == POST.getInt()) {
break; c = validatePost(m, g, body, member);
case POST: addMessageMetadata(c, member, m.getTimestamp());
c = validatePost(m, g, body, member); } else {
addMessageMetadata(c, member, m.getTimestamp()); throw new InvalidMessageException("Unknown Message Type");
break;
default:
throw new InvalidMessageException("Unknown Message Type");
} }
c.getDictionary().put(KEY_TYPE, type); c.getDictionary().put(KEY_TYPE, type);
return c; return c;
@@ -133,8 +129,9 @@ class GroupMessageValidator extends BdfMessageValidator {
.createInviteToken(creator.getId(), member.getId(), .createInviteToken(creator.getId(), member.getId(),
pg.getId(), inviteTimestamp); pg.getId(), inviteTimestamp);
try { try {
clientHelper.verifySignature(SIGNING_LABEL_INVITE, creatorSignature, clientHelper
creator.getPublicKey(), token); .verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
creator.getPublicKey(), token);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }

View File

@@ -7,6 +7,7 @@ import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.privategroup.MessageType;
import org.briarproject.api.privategroup.PrivateGroup; import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.privategroup.PrivateGroupFactory; import org.briarproject.api.privategroup.PrivateGroupFactory;
import org.briarproject.api.privategroup.invitation.GroupInvitationFactory; import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
@@ -16,6 +17,9 @@ import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.briarproject.TestUtils.getRandomBytes; import static org.briarproject.TestUtils.getRandomBytes;
import static org.briarproject.TestUtils.getRandomId; import static org.briarproject.TestUtils.getRandomId;
@@ -28,6 +32,7 @@ import static org.briarproject.api.privategroup.GroupMessageFactory.SIGNING_LABE
import static org.briarproject.api.privategroup.MessageType.JOIN; import static org.briarproject.api.privategroup.MessageType.JOIN;
import static org.briarproject.api.privategroup.MessageType.POST; import static org.briarproject.api.privategroup.MessageType.POST;
import static org.briarproject.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH; import static org.briarproject.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH; import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE; import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG; import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG;
@@ -39,6 +44,7 @@ import static org.briarproject.privategroup.GroupConstants.KEY_PREVIOUS_MSG_ID;
import static org.briarproject.privategroup.GroupConstants.KEY_READ; import static org.briarproject.privategroup.GroupConstants.KEY_READ;
import static org.briarproject.privategroup.GroupConstants.KEY_TIMESTAMP; import static org.briarproject.privategroup.GroupConstants.KEY_TIMESTAMP;
import static org.briarproject.privategroup.GroupConstants.KEY_TYPE; import static org.briarproject.privategroup.GroupConstants.KEY_TYPE;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -50,9 +56,8 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
private final GroupInvitationFactory groupInvitationFactory = private final GroupInvitationFactory groupInvitationFactory =
context.mock(GroupInvitationFactory.class); context.mock(GroupInvitationFactory.class);
private final String creatorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final String creatorName = "Member Name"; private final String memberName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final String memberName = "Member Name";
private final byte[] creatorKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); private final byte[] creatorKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
private final byte[] memberKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); private final byte[] memberKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
private final byte[] creatorSignature = private final byte[] creatorSignature =
@@ -63,220 +68,272 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
private final Author creator = private final Author creator =
new Author(new AuthorId(getRandomId()), creatorName, creatorKey); new Author(new AuthorId(getRandomId()), creatorName, creatorKey);
private final long inviteTimestamp = 42L; private final long inviteTimestamp = 42L;
private final PrivateGroup privateGroup = private final PrivateGroup privateGroup = new PrivateGroup(group,
new PrivateGroup(group, "Private Group Name", creator, getRandomString(MAX_GROUP_NAME_LENGTH), creator,
getRandomBytes(GROUP_SALT_LENGTH)); getRandomBytes(GROUP_SALT_LENGTH));
private final BdfList token = BdfList.of("token"); private final BdfList token = BdfList.of("token");
private MessageId parentId = new MessageId(getRandomId()); private final MessageId parentId = new MessageId(getRandomId());
private MessageId previousMsgId = new MessageId(getRandomId()); private final MessageId previousMsgId = new MessageId(getRandomId());
private String postContent = "Post text"; private final String postContent =
getRandomString(MAX_GROUP_POST_BODY_LENGTH);
private GroupMessageValidator validator = private final GroupMessageValidator validator =
new GroupMessageValidator(privateGroupFactory, clientHelper, new GroupMessageValidator(privateGroupFactory, clientHelper,
metadataEncoder, clock, authorFactory, metadataEncoder, clock, authorFactory,
groupInvitationFactory); groupInvitationFactory);
@Test(expected = FormatException.class)
public void testRejectTooShortMemberName() throws Exception {
BdfList list = BdfList.of(JOIN.getInt(), "", memberKey, null,
signature);
validator.validateMessage(message, group, list);
}
@Test(expected = FormatException.class)
public void testRejectTooLongMemberName() throws Exception {
BdfList list = BdfList.of(JOIN.getInt(),
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1), memberKey, null,
signature);
validator.validateMessage(message, group, list);
}
@Test(expected = FormatException.class)
public void testRejectTooShortMemberKey() throws Exception {
BdfList list = BdfList.of(JOIN.getInt(), memberName, new byte[0], null,
signature);
validator.validateMessage(message, group, list);
}
@Test(expected = FormatException.class)
public void testRejectTooLongMemberKey() throws Exception {
BdfList list = BdfList.of(JOIN.getInt(), memberName,
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), null,
signature);
validator.validateMessage(message, group, list);
}
@Test(expected = FormatException.class)
public void testRejectNonRawMemberKey() throws Exception {
BdfList list =
BdfList.of(JOIN.getInt(), memberName, "non raw key", null,
signature);
validator.validateMessage(message, group, list);
}
// JOIN message // JOIN message
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortJoinMessage() throws Exception { public void testRejectsTooShortJoinMessage() throws Exception {
BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey, BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey, null);
null); validator.validateMessage(message, group, body);
validator.validateMessage(message, group, list);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongJoinMessage() throws Exception { public void testRejectsTooLongJoinMessage() throws Exception {
expectCreateAuthor(creator); expectCreateAuthor(creator);
BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey, BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey, null,
null, signature, ""); signature, "");
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsJoinMessageWithNonListInvitation() throws Exception { public void testRejectsJoinWithTooShortMemberName() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), "", memberKey, null,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinMessageWithTooLongMemberName() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(),
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1), memberKey, null,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinMessageWithNullMemberName() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), null, memberKey, null,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinMessageWithNonStringMemberName() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), getRandomBytes(5), memberKey,
null, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinWithTooShortMemberKey() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), memberName, new byte[0], null,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinWithTooLongMemberKey() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), memberName,
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), null, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinWithNoullMemberKey() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), memberName, null, null,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinWithNonRawMemberKey() throws Exception {
BdfList body = BdfList.of(JOIN.getInt(), memberName, "not raw", null,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsJoinWithNonListInvitation() throws Exception {
expectCreateAuthor(creator); expectCreateAuthor(creator);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey, BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
"not a list", signature); "not a list", signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test @Test
public void testAcceptCreatorJoinMessage() throws Exception { public void testAcceptsCreatorJoin() throws Exception {
final BdfList invite = null; expectJoinMessage(creator, null, true, true);
expectJoinMessage(creator, invite, true, true); BdfList body = BdfList.of(JOIN.getInt(), creatorName, creatorKey,
BdfList list = BdfList.of(JOIN.getInt(), creatorName, creatorKey, null, signature);
invite, signature);
BdfMessageContext messageContext = BdfMessageContext messageContext =
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
assertMessageContext(messageContext, creator); assertExpectedMessageContext(messageContext, JOIN, creator,
Collections.<MessageId>emptyList());
assertTrue(messageContext.getDictionary() assertTrue(messageContext.getDictionary()
.getBoolean(KEY_INITIAL_JOIN_MSG)); .getBoolean(KEY_INITIAL_JOIN_MSG));
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsMemberJoinMessageWithoutInvitation() public void testRejectsMemberJoinWithNullInvitation() throws Exception {
throws Exception {
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, null, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, null,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsJoinMessageWithTooShortInvitation() throws Exception { public void testRejectsMemberJoinWithTooShortInvitation() throws Exception {
BdfList invite = BdfList.of(inviteTimestamp); BdfList invite = BdfList.of(inviteTimestamp);
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsJoinMessageWithTooLongInvitation() throws Exception { public void testRejectsMemberJoinWithTooLongInvitation() throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, creatorSignature, ""); BdfList invite = BdfList.of(inviteTimestamp, creatorSignature, "");
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsJoinMessageWithEqualInvitationTime() public void testRejectsMemberJoinWithEqualInvitationTime()
throws Exception { throws Exception {
BdfList invite = BdfList.of(message.getTimestamp(), creatorSignature); BdfList invite = BdfList.of(message.getTimestamp(), creatorSignature);
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsJoinMessageWithLaterInvitationTime() public void testRejectsMemberJoinWithLaterInvitationTime()
throws Exception { throws Exception {
BdfList invite = BdfList invite = BdfList.of(message.getTimestamp() + 1,
BdfList.of(message.getTimestamp() + 1, creatorSignature); creatorSignature);
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsJoinMessageWithNonRawCreatorSignature() public void testRejectsMemberJoinWithNullInvitationTime()
throws Exception { throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, "non-raw signature"); BdfList invite = BdfList.of(null, creatorSignature);
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsJoinMessageWithTooShortCreatorSignature() public void testRejectsMemberJoinWithNonLongInvitationTime()
throws Exception {
BdfList invite = BdfList.of("not long", creatorSignature);
expectCreateAuthor(member);
expectParsePrivateGroup();
BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsMemberJoinWithTooShortCreatorSignature()
throws Exception { throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, new byte[0]); BdfList invite = BdfList.of(inviteTimestamp, new byte[0]);
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsJoinMessageWithTooLongCreatorSignature() public void testRejectsJoinWithTooLongCreatorSignature()
throws Exception { throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, BdfList invite = BdfList.of(inviteTimestamp,
getRandomBytes(MAX_SIGNATURE_LENGTH + 1)); getRandomBytes(MAX_SIGNATURE_LENGTH + 1));
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsMemberJoinWithNullCreatorSignature()
throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, null);
expectCreateAuthor(member);
expectParsePrivateGroup();
BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsMemberJoinWithNonRawCreatorSignature()
throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, "not raw");
expectCreateAuthor(member);
expectParsePrivateGroup();
BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature);
validator.validateMessage(message, group, body);
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsJoinMessageWithInvalidCreatorSignature() public void testRejectsMemberJoinWithInvalidCreatorSignature()
throws Exception { throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, creatorSignature); BdfList invite = BdfList.of(inviteTimestamp, creatorSignature);
expectJoinMessage(member, invite, false, true); expectJoinMessage(member, invite, false, true);
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsJoinMessageWithInvalidMemberSignature() public void testRejectsMemberJoinWithInvalidMemberSignature()
throws Exception { throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, creatorSignature); BdfList invite = BdfList.of(inviteTimestamp, creatorSignature);
expectJoinMessage(member, invite, true, false); expectJoinMessage(member, invite, true, false);
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test @Test
public void testAcceptMemberJoinMessage() throws Exception { public void testAcceptsMemberJoin() throws Exception {
BdfList invite = BdfList.of(inviteTimestamp, creatorSignature); BdfList invite = BdfList.of(inviteTimestamp, creatorSignature);
expectJoinMessage(member, invite, true, true); expectJoinMessage(member, invite, true, true);
BdfList list = BdfList.of(JOIN.getInt(), memberName, memberKey, invite, BdfList body = BdfList.of(JOIN.getInt(), memberName, memberKey, invite,
signature); signature);
BdfMessageContext messageContext = BdfMessageContext messageContext =
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
assertMessageContext(messageContext, member); assertExpectedMessageContext(messageContext, JOIN, member,
Collections.<MessageId>emptyList());
assertFalse(messageContext.getDictionary() assertFalse(messageContext.getDictionary()
.getBoolean(KEY_INITIAL_JOIN_MSG)); .getBoolean(KEY_INITIAL_JOIN_MSG));
} }
private void expectCreateAuthor(final Author member) { private void expectCreateAuthor(final Author member) {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(authorFactory) oneOf(authorFactory).createAuthor(member.getName(),
.createAuthor(member.getName(), member.getPublicKey()); member.getPublicKey());
will(returnValue(member)); will(returnValue(member));
}}); }});
} }
@@ -291,27 +348,23 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
private void expectJoinMessage(final Author member, final BdfList invite, private void expectJoinMessage(final Author member, final BdfList invite,
final boolean creatorSigValid, final boolean memberSigValid) final boolean creatorSigValid, final boolean memberSigValid)
throws Exception { throws Exception {
final BdfList signed = final BdfList signed = BdfList.of(group.getId(), message.getTimestamp(),
BdfList.of(group.getId(), message.getTimestamp(), JOIN.getInt(), JOIN.getInt(), member.getName(), member.getPublicKey(), invite);
member.getName(), member.getPublicKey(), invite);
expectCreateAuthor(member); expectCreateAuthor(member);
expectParsePrivateGroup(); expectParsePrivateGroup();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
if (invite != null) { if (invite != null) {
oneOf(groupInvitationFactory) oneOf(groupInvitationFactory).createInviteToken(creator.getId(),
.createInviteToken(creator.getId(), member.getId(), member.getId(), privateGroup.getId(), inviteTimestamp);
privateGroup.getId(), inviteTimestamp);
will(returnValue(token)); will(returnValue(token));
oneOf(clientHelper) oneOf(clientHelper).verifySignature(SIGNING_LABEL_INVITE,
.verifySignature(SIGNING_LABEL_INVITE, creatorSignature, creatorSignature, creatorKey, token);
creatorKey, token);
if (!memberSigValid) if (!memberSigValid)
will(throwException(new GeneralSecurityException())); will(throwException(new GeneralSecurityException()));
} }
if (memberSigValid) { if (memberSigValid) {
oneOf(clientHelper) oneOf(clientHelper).verifySignature(SIGNING_LABEL_JOIN,
.verifySignature(SIGNING_LABEL_JOIN, signature, signature, member.getPublicKey(), signed);
member.getPublicKey(), signed);
if (!creatorSigValid) if (!creatorSigValid)
will(throwException(new GeneralSecurityException())); will(throwException(new GeneralSecurityException()));
} }
@@ -322,213 +375,274 @@ public class GroupMessageValidatorTest extends ValidatorTestCase {
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortPost() throws Exception { public void testRejectsTooShortPost() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent);
previousMsgId, postContent);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongPost() throws Exception { public void testRejectsTooLongPost() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent, signature, "");
previousMsgId, postContent, signature, ""); validator.validateMessage(message, group, body);
validator.validateMessage(message, group, list);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithNonRawParentId() throws Exception { public void testRejectsPostWithTooShortMemberName() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), "", memberKey, parentId,
BdfList.of(POST.getInt(), memberName, memberKey, "non-raw", previousMsgId, postContent, signature);
previousMsgId, postContent, signature); validator.validateMessage(message, group, body);
expectCreateAuthor(member); }
validator.validateMessage(message, group, list);
@Test(expected = FormatException.class)
public void testRejectsPostWithTooLongMemberName() throws Exception {
BdfList body = BdfList.of(POST.getInt(),
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1), memberKey,
parentId, previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNullMemberName() throws Exception {
BdfList body = BdfList.of(POST.getInt(), null, memberKey,
parentId, previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNonStringMemberName() throws Exception {
BdfList body = BdfList.of(POST.getInt(), getRandomBytes(5), memberKey,
parentId, previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithTooShortMemberKey() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, new byte[0],
parentId, previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithTooLongMemberKey() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName,
getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1), parentId,
previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNullMemberKey() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, null,
parentId, previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNonRawMemberKey() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, "not raw",
parentId, previousMsgId, postContent, signature);
validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithTooShortParentId() throws Exception { public void testRejectsPostWithTooShortParentId() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, getRandomBytes(MessageId.LENGTH - 1), previousMsgId,
getRandomBytes(MessageId.LENGTH - 1), previousMsgId, postContent, signature);
postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithTooLongParentId() throws Exception { public void testRejectsPostWithTooLongParentId() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, getRandomBytes(MessageId.LENGTH + 1), previousMsgId,
getRandomBytes(MessageId.LENGTH + 1), previousMsgId, postContent, signature);
postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithNonRawPreviousMsgId() throws Exception { public void testRejectsPostWithNonRawParentId() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, "not raw", previousMsgId, postContent, signature);
"non-raw", postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithTooShortPreviousMsgId() throws Exception { public void testRejectsPostWithTooShortPreviousMsgId() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, getRandomBytes(MessageId.LENGTH - 1), postContent,
getRandomBytes(MessageId.LENGTH - 1), signature);
postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithTooLongPreviousMsgId() throws Exception { public void testRejectsPostWithTooLongPreviousMsgId() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, getRandomBytes(MessageId.LENGTH + 1), postContent,
getRandomBytes(MessageId.LENGTH + 1), signature);
postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithEmptyContent() throws Exception { public void testRejectsPostWithNullPreviousMsgId() throws Exception {
postContent = ""; BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList list = parentId, null, postContent, signature);
BdfList.of(POST.getInt(), memberName, memberKey, parentId,
previousMsgId, postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNonRawPreviousMsgId() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
parentId, "not raw", postContent, signature);
expectCreateAuthor(member);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithTooShortContent() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
parentId, previousMsgId, "", signature);
expectCreateAuthor(member);
validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithTooLongContent() throws Exception { public void testRejectsPostWithTooLongContent() throws Exception {
postContent = getRandomString(MAX_GROUP_POST_BODY_LENGTH + 1); BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList list = parentId, previousMsgId,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, getRandomString(MAX_GROUP_POST_BODY_LENGTH + 1), signature);
previousMsgId, postContent, signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNullContent() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
parentId, previousMsgId, null, signature);
expectCreateAuthor(member);
validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithNonStringContent() throws Exception { public void testRejectsPostWithNonStringContent() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, getRandomBytes(5), signature);
previousMsgId, getRandomBytes(5), signature);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithEmptySignature() throws Exception { public void testRejectsPostWithTooShortSignature() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent, new byte[0]);
previousMsgId, postContent, new byte[0]);
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithTooLongSignature() throws Exception { public void testRejectsPostWithTooLongSignature() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent,
previousMsgId, postContent, getRandomBytes(MAX_SIGNATURE_LENGTH + 1));
getRandomBytes(MAX_SIGNATURE_LENGTH + 1));
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsPostWithNullSignature() throws Exception {
BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
parentId, previousMsgId, postContent,null);
expectCreateAuthor(member);
validator.validateMessage(message, group, body);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsPostWithNonRawSignature() throws Exception { public void testRejectsPostWithNonRawSignature() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent, "not raw");
previousMsgId, postContent, "non-raw");
expectCreateAuthor(member); expectCreateAuthor(member);
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsPostWithInvalidSignature() throws Exception { public void testRejectsPostWithInvalidSignature() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent, signature);
previousMsgId, postContent, signature); expectPostMessage(member, parentId, false);
expectPostMessage(member, false); validator.validateMessage(message, group, body);
validator.validateMessage(message, group, list);
} }
@Test @Test
public void testAcceptPost() throws Exception { public void testAcceptsPost() throws Exception {
BdfList list = BdfList body = BdfList.of(POST.getInt(), memberName, memberKey,
BdfList.of(POST.getInt(), memberName, memberKey, parentId, parentId, previousMsgId, postContent, signature);
previousMsgId, postContent, signature); expectPostMessage(member, parentId, true);
expectPostMessage(member, true);
BdfMessageContext messageContext = BdfMessageContext messageContext =
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
assertMessageContext(messageContext, member); assertExpectedMessageContext(messageContext, POST, member,
assertEquals(previousMsgId.getBytes(), Arrays.asList(parentId, previousMsgId));
assertArrayEquals(previousMsgId.getBytes(),
messageContext.getDictionary().getRaw(KEY_PREVIOUS_MSG_ID)); messageContext.getDictionary().getRaw(KEY_PREVIOUS_MSG_ID));
assertEquals(parentId.getBytes(), assertArrayEquals(parentId.getBytes(),
messageContext.getDictionary().getRaw(KEY_PARENT_MSG_ID)); messageContext.getDictionary().getRaw(KEY_PARENT_MSG_ID));
} }
@Test @Test
public void testAcceptTopLevelPost() throws Exception { public void testAcceptsTopLevelPost() throws Exception {
parentId = null; BdfList body = BdfList.of(POST.getInt(), memberName, memberKey, null,
BdfList list = previousMsgId, postContent, signature);
BdfList.of(POST.getInt(), memberName, memberKey, parentId, expectPostMessage(member, null, true);
previousMsgId, postContent, signature);
expectPostMessage(member, true);
BdfMessageContext messageContext = BdfMessageContext messageContext =
validator.validateMessage(message, group, list); validator.validateMessage(message, group, body);
assertMessageContext(messageContext, member); assertExpectedMessageContext(messageContext, POST, member,
assertEquals(previousMsgId.getBytes(), Collections.singletonList(previousMsgId));
assertArrayEquals(previousMsgId.getBytes(),
messageContext.getDictionary().getRaw(KEY_PREVIOUS_MSG_ID)); messageContext.getDictionary().getRaw(KEY_PREVIOUS_MSG_ID));
assertFalse( assertFalse(
messageContext.getDictionary().containsKey(KEY_PARENT_MSG_ID)); messageContext.getDictionary().containsKey(KEY_PARENT_MSG_ID));
} }
private void expectPostMessage(final Author member, final boolean sigValid) private void expectPostMessage(final Author member,
throws Exception { final MessageId parentId, final boolean sigValid) throws Exception {
final BdfList signed = final BdfList signed = BdfList.of(group.getId(), message.getTimestamp(),
BdfList.of(group.getId(), message.getTimestamp(), POST.getInt(), POST.getInt(), member.getName(), member.getPublicKey(),
member.getName(), member.getPublicKey(), parentId == null ? null : parentId.getBytes(),
parentId == null ? null : parentId.getBytes(), previousMsgId.getBytes(), postContent);
previousMsgId == null ? null : previousMsgId.getBytes(),
postContent);
expectCreateAuthor(member); expectCreateAuthor(member);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper) oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
.verifySignature(SIGNING_LABEL_POST, signature, member.getPublicKey(), signed);
member.getPublicKey(), signed);
if (!sigValid) will(throwException(new GeneralSecurityException())); if (!sigValid) will(throwException(new GeneralSecurityException()));
}}); }});
} }
private void assertMessageContext(BdfMessageContext c, Author member) private void assertExpectedMessageContext(BdfMessageContext c,
throws FormatException { MessageType type, Author member,
Collection<MessageId> dependencies) throws FormatException {
BdfDictionary d = c.getDictionary(); BdfDictionary d = c.getDictionary();
assertTrue(message.getTimestamp() == d.getLong(KEY_TIMESTAMP)); assertEquals(type.getInt(), d.getLong(KEY_TYPE).intValue());
assertEquals(message.getTimestamp(),
d.getLong(KEY_TIMESTAMP).longValue());
assertFalse(d.getBoolean(KEY_READ)); assertFalse(d.getBoolean(KEY_READ));
assertEquals(member.getId().getBytes(), d.getRaw(KEY_MEMBER_ID)); assertEquals(member.getId().getBytes(), d.getRaw(KEY_MEMBER_ID));
assertEquals(member.getName(), d.getString(KEY_MEMBER_NAME)); assertEquals(member.getName(), d.getString(KEY_MEMBER_NAME));
assertEquals(member.getPublicKey(), d.getRaw(KEY_MEMBER_PUBLIC_KEY)); assertEquals(member.getPublicKey(), d.getRaw(KEY_MEMBER_PUBLIC_KEY));
assertEquals(dependencies, c.getDependencies());
// assert message dependencies
if (d.getLong(KEY_TYPE) == POST.getInt()) {
assertTrue(c.getDependencies().contains(previousMsgId));
if (parentId != null) {
assertTrue(c.getDependencies().contains(parentId));
} else {
assertFalse(c.getDependencies().contains(parentId));
}
} else {
assertEquals(JOIN.getInt(), d.getLong(KEY_TYPE).intValue());
}
} }
@Test(expected = InvalidMessageException.class)
public void testRejectsMessageWithUnknownType() throws Exception {
BdfList body = BdfList.of(POST.getInt() + 1, memberName, memberKey,
parentId, previousMsgId, postContent, signature);
expectCreateAuthor(member);
validator.validateMessage(message, group, body);
}
} }