Address review comments

This commit is contained in:
Torsten Grote
2016-10-28 16:07:17 -02:00
parent c79ce61f6d
commit c0aa255bb6
22 changed files with 226 additions and 132 deletions

View File

@@ -72,6 +72,7 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
private LocalAuthor author0, author1; private LocalAuthor author0, author1;
private PrivateGroup privateGroup0; private PrivateGroup privateGroup0;
private GroupId groupId0; private GroupId groupId0;
private GroupMessage newMemberMsg0;
@Inject @Inject
Clock clock; Clock clock;
@@ -221,6 +222,20 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// assert that message did not arrive // assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size()); assertEquals(2, groupManager1.getHeaders(groupId0).size());
// create and add test message with previousMsgId of newMemberMsg
previousMsgId = newMemberMsg0.getMessage().getId();
msg = groupMessageFactory
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
author0, "test", previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
} }
@Test @Test
@@ -437,13 +452,13 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
private void addGroup() throws Exception { private void addGroup() throws Exception {
// author0 joins privateGroup0 // author0 joins privateGroup0
long joinTime = clock.currentTimeMillis(); long joinTime = clock.currentTimeMillis();
GroupMessage newMemberMsg = groupMessageFactory newMemberMsg0 = groupMessageFactory
.createNewMemberMessage(privateGroup0.getId(), joinTime, .createNewMemberMessage(privateGroup0.getId(), joinTime,
author0, author0); author0, author0);
GroupMessage joinMsg = groupMessageFactory GroupMessage joinMsg = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author0, .createJoinMessage(privateGroup0.getId(), joinTime, author0,
newMemberMsg.getMessage().getId()); newMemberMsg0.getMessage().getId());
groupManager0.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg); groupManager0.addPrivateGroup(privateGroup0, newMemberMsg0, joinMsg);
assertEquals(joinMsg.getMessage().getId(), assertEquals(joinMsg.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0)); groupManager0.getPreviousMsgId(groupId0));
@@ -457,13 +472,13 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// author1 joins privateGroup0 // author1 joins privateGroup0
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
newMemberMsg = groupMessageFactory GroupMessage newMemberMsg1 = groupMessageFactory
.createNewMemberMessage(privateGroup0.getId(), joinTime, .createNewMemberMessage(privateGroup0.getId(), joinTime,
author0, author1); author0, author1);
joinMsg = groupMessageFactory joinMsg = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1, .createJoinMessage(privateGroup0.getId(), joinTime, author1,
newMemberMsg.getMessage().getId()); newMemberMsg1.getMessage().getId());
groupManager1.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg); groupManager1.addPrivateGroup(privateGroup0, newMemberMsg1, joinMsg);
assertEquals(joinMsg.getMessage().getId(), assertEquals(joinMsg.getMessage().getId(),
groupManager1.getPreviousMsgId(groupId0)); groupManager1.getPreviousMsgId(groupId0));

View File

@@ -15,6 +15,7 @@ import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule; import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.privategroup.PrivateGroupModule; import org.briarproject.privategroup.PrivateGroupModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule; import org.briarproject.sharing.SharingModule;
@@ -37,6 +38,7 @@ import dagger.Component;
DataModule.class, DataModule.class,
DatabaseModule.class, DatabaseModule.class,
EventModule.class, EventModule.class,
MessagingModule.class,
PrivateGroupModule.class, PrivateGroupModule.class,
IdentityModule.class, IdentityModule.class,
LifecycleModule.class, LifecycleModule.class,

View File

@@ -2,7 +2,6 @@ package org.briarproject.android.privategroup.conversation;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.briarproject.R;
import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.android.controller.handler.ResultExceptionHandler;
import org.briarproject.android.threaded.ThreadListControllerImpl; import org.briarproject.android.threaded.ThreadListControllerImpl;
@@ -95,8 +94,8 @@ public class GroupControllerImpl extends
protected String loadMessageBody(GroupMessageHeader header) protected String loadMessageBody(GroupMessageHeader header)
throws DbException { throws DbException {
if (header instanceof JoinMessageHeader) { if (header instanceof JoinMessageHeader) {
return listener.getApplicationContext() // will be looked up later
.getString(R.string.groups_member_joined); return "";
} }
return privateGroupManager.getMessageBody(header.getId()); return privateGroupManager.getMessageBody(header.getId());
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.android.privategroup.conversation; package org.briarproject.android.privategroup.conversation;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -19,12 +20,11 @@ public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
super(listener, layoutManager); super(listener, layoutManager);
} }
@LayoutRes
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
GroupMessageItem item = getVisibleItem(position); GroupMessageItem item = getVisibleItem(position);
if (item instanceof JoinMessageItem) { if (item != null) return item.getLayout();
return R.layout.list_item_thread_notice;
}
return R.layout.list_item_thread; return R.layout.list_item_thread;
} }
@@ -34,7 +34,7 @@ public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
View v = LayoutInflater.from(parent.getContext()) View v = LayoutInflater.from(parent.getContext())
.inflate(type, parent, false); .inflate(type, parent, false);
if (type == R.layout.list_item_thread_notice) { if (type == R.layout.list_item_thread_notice) {
return new BaseThreadItemViewHolder<>(v); return new JoinMessageItemHolder(v);
} }
return new ThreadItemViewHolder<>(v); return new ThreadItemViewHolder<>(v);
} }

View File

@@ -1,11 +1,19 @@
package org.briarproject.android.privategroup.conversation; package org.briarproject.android.privategroup.conversation;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread;
import org.briarproject.R;
import org.briarproject.android.threaded.ThreadItem; import org.briarproject.android.threaded.ThreadItem;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status; import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.privategroup.GroupMessageHeader; import org.briarproject.api.privategroup.GroupMessageHeader;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import javax.annotation.concurrent.NotThreadSafe;
@UiThread
@NotThreadSafe
class GroupMessageItem extends ThreadItem { class GroupMessageItem extends ThreadItem {
private GroupMessageItem(MessageId messageId, MessageId parentId, private GroupMessageItem(MessageId messageId, MessageId parentId,
@@ -19,4 +27,9 @@ class GroupMessageItem extends ThreadItem {
h.getAuthorStatus(), h.isRead()); h.getAuthorStatus(), h.isRead());
} }
@LayoutRes
public int getLayout() {
return R.layout.list_item_thread;
}
} }

View File

@@ -1,7 +1,15 @@
package org.briarproject.android.privategroup.conversation; package org.briarproject.android.privategroup.conversation;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread;
import org.briarproject.R;
import org.briarproject.api.privategroup.GroupMessageHeader; import org.briarproject.api.privategroup.GroupMessageHeader;
import javax.annotation.concurrent.NotThreadSafe;
@UiThread
@NotThreadSafe
class JoinMessageItem extends GroupMessageItem { class JoinMessageItem extends GroupMessageItem {
JoinMessageItem(GroupMessageHeader h, JoinMessageItem(GroupMessageHeader h,
@@ -19,4 +27,9 @@ class JoinMessageItem extends GroupMessageItem {
return false; return false;
} }
@LayoutRes
public int getLayout() {
return R.layout.list_item_thread_notice;
}
} }

View File

@@ -0,0 +1,30 @@
package org.briarproject.android.privategroup.conversation;
import android.support.annotation.UiThread;
import android.view.View;
import org.briarproject.R;
import org.briarproject.android.threaded.BaseThreadItemViewHolder;
import org.briarproject.android.threaded.ThreadItemAdapter;
import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
import org.briarproject.api.nullsafety.NotNullByDefault;
@UiThread
@NotNullByDefault
public class JoinMessageItemHolder
extends BaseThreadItemViewHolder<GroupMessageItem> {
public JoinMessageItemHolder(View v) {
super(v);
}
@Override
public void bind(final ThreadItemAdapter<GroupMessageItem> adapter,
final ThreadItemListener<GroupMessageItem> listener,
final GroupMessageItem item, int pos) {
super.bind(adapter, listener, item, pos);
textView.setText(getContext().getString(R.string.groups_member_joined));
}
}

View File

@@ -21,13 +21,13 @@ import org.briarproject.util.StringUtils;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public class BaseThreadItemViewHolder<I extends ThreadItem> public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
extends RecyclerView.ViewHolder { extends RecyclerView.ViewHolder {
private final static int ANIMATION_DURATION = 5000; private final static int ANIMATION_DURATION = 5000;
protected final TextView textView;
private final ViewGroup layout; private final ViewGroup layout;
private final TextView textView;
private final AuthorView author; private final AuthorView author;
private final View topDivider; private final View topDivider;

View File

@@ -1,7 +1,5 @@
package org.briarproject.android.threaded; package org.briarproject.android.threaded;
import android.support.annotation.UiThread;
import org.briarproject.api.clients.MessageTree.MessageNode; import org.briarproject.api.clients.MessageTree.MessageNode;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status; import org.briarproject.api.identity.Author.Status;
@@ -11,7 +9,6 @@ import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.android.threaded.ThreadItemAdapter.UNDEFINED; import static org.briarproject.android.threaded.ThreadItemAdapter.UNDEFINED;
@UiThread
@NotThreadSafe @NotThreadSafe
public abstract class ThreadItem implements MessageNode { public abstract class ThreadItem implements MessageNode {
@@ -97,4 +94,5 @@ public abstract class ThreadItem implements MessageNode {
public void setDescendantCount(int descendantCount) { public void setDescendantCount(int descendantCount) {
this.descendantCount = descendantCount; this.descendantCount = descendantCount;
} }
} }

View File

@@ -316,7 +316,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
revision++; revision++;
} }
protected interface ThreadItemListener<I> { public interface ThreadItemListener<I> {
void onItemVisible(I item); void onItemVisible(I item);

View File

@@ -1,6 +1,5 @@
package org.briarproject.android.threaded; package org.briarproject.android.threaded;
import android.content.Context;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
@@ -40,8 +39,6 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
@UiThread @UiThread
void onGroupRemoved(); void onGroupRemoved();
Context getApplicationContext();
} }
} }

View File

@@ -42,7 +42,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
Logger.getLogger(ThreadListControllerImpl.class.getName()); Logger.getLogger(ThreadListControllerImpl.class.getName());
protected final IdentityManager identityManager; protected final IdentityManager identityManager;
@CryptoExecutor
protected final Executor cryptoExecutor; protected final Executor cryptoExecutor;
protected final AndroidNotificationManager notificationManager; protected final AndroidNotificationManager notificationManager;
protected final Clock clock; protected final Clock clock;

View File

@@ -84,6 +84,6 @@ public interface ClientHelper {
throws FormatException, GeneralSecurityException; throws FormatException, GeneralSecurityException;
void verifySignature(byte[] sig, byte[] publicKey, BdfList signed) void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
throws InvalidMessageException; throws FormatException, GeneralSecurityException;
} }

View File

@@ -12,16 +12,8 @@ public enum MessageType {
} }
public static MessageType valueOf(int value) { public static MessageType valueOf(int value) {
switch (value) { for (MessageType m : values()) if (m.value == value) return m;
case 0: throw new IllegalArgumentException();
return NEW_MEMBER;
case 1:
return JOIN;
case 2:
return POST;
default:
throw new IllegalArgumentException();
}
} }
public int getInt() { public int getInt() {

View File

@@ -18,8 +18,9 @@ public interface PrivateGroupManager extends MessageTracker {
* Adds a new private group and joins it. * Adds a new private group and joins it.
* *
* @param group The private group to add * @param group The private group to add
* @param newMemberMsg The creator's message announcing the first new member * @param newMemberMsg The creator's message announcing herself as
* @param joinMsg The first new member's join message * first new member
* @param joinMsg The creator's own join message
*/ */
void addPrivateGroup(PrivateGroup group, GroupMessage newMemberMsg, void addPrivateGroup(PrivateGroup group, GroupMessage newMemberMsg,
GroupMessage joinMsg) throws DbException; GroupMessage joinMsg) throws DbException;
@@ -27,10 +28,11 @@ public interface PrivateGroupManager extends MessageTracker {
/** Removes a dissolved private group. */ /** Removes a dissolved private group. */
void removePrivateGroup(GroupId g) throws DbException; void removePrivateGroup(GroupId g) throws DbException;
/** Gets the MessageId of the */ /** Gets the MessageId of your previous message sent to the group */
MessageId getPreviousMsgId(GroupId g) throws DbException; MessageId getPreviousMsgId(GroupId g) throws DbException;
/** Returns the timestamp of the message with the given ID */ /** Returns the timestamp of the message with the given ID */
// TODO change to getPreviousMessageHeader()
long getMessageTimestamp(MessageId id) throws DbException; long getMessageTimestamp(MessageId id) throws DbException;
/** Stores (and sends) a local group message. */ /** Stores (and sends) a local group message. */

View File

@@ -20,6 +20,7 @@ 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.security.GeneralSecurityException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -101,7 +102,11 @@ class BlogPostValidator extends BdfMessageValidator {
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), postBody); BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), postBody);
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
Author a = b.getAuthor(); Author a = b.getAuthor();
clientHelper.verifySignature(sig, a.getPublicKey(), signed); try {
clientHelper.verifySignature(sig, a.getPublicKey(), signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
// Return the metadata and dependencies // Return the metadata and dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
@@ -142,7 +147,11 @@ class BlogPostValidator extends BdfMessageValidator {
currentId); currentId);
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
Author a = b.getAuthor(); Author a = b.getAuthor();
clientHelper.verifySignature(sig, a.getPublicKey(), signed); try {
clientHelper.verifySignature(sig, a.getPublicKey(), signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
// Return the metadata and dependencies // Return the metadata and dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();

View File

@@ -20,7 +20,6 @@ import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Metadata; import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.InvalidMessageException;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageFactory; import org.briarproject.api.sync.MessageFactory;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -325,22 +324,16 @@ class ClientHelperImpl implements ClientHelper {
@Override @Override
public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed) public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
throws InvalidMessageException { throws FormatException, GeneralSecurityException {
try { // Parse the public key
// Parse the public key KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
KeyParser keyParser = cryptoComponent.getSignatureKeyParser(); PublicKey key = keyParser.parsePublicKey(publicKey);
PublicKey key = keyParser.parsePublicKey(publicKey); // Verify the signature
// Verify the signature Signature signature = cryptoComponent.getSignature();
Signature signature = cryptoComponent.getSignature(); signature.initVerify(key);
signature.initVerify(key); signature.update(toByteArray(signed));
signature.update(toByteArray(signed)); if (!signature.verify(sig)) {
if (!signature.verify(sig)) { throw new GeneralSecurityException("Invalid signature");
throw new InvalidMessageException("Invalid signature");
}
} catch (GeneralSecurityException e) {
throw new InvalidMessageException("Invalid public key");
} catch (FormatException e) {
throw new InvalidMessageException(e);
} }
} }

View File

@@ -16,6 +16,7 @@ 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.security.GeneralSecurityException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -73,10 +74,15 @@ class ForumPostValidator extends BdfMessageValidator {
} }
// Verify the signature, if any // Verify the signature, if any
if (author != null) { if (author != null) {
// Serialise the data to be signed // Serialise the data to be verified
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent, BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
authorList, contentType, forumPostBody); authorList, contentType, forumPostBody);
clientHelper.verifySignature(sig, author.getPublicKey(), signed); try {
clientHelper
.verifySignature(sig, author.getPublicKey(), signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
} }
// Return the metadata and dependencies // Return the metadata and dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();

View File

@@ -11,8 +11,8 @@ interface Constants {
String KEY_PARENT_MSG_ID = "parentMsgId"; String KEY_PARENT_MSG_ID = "parentMsgId";
String KEY_NEW_MEMBER_MSG_ID = "newMemberMsgId"; String KEY_NEW_MEMBER_MSG_ID = "newMemberMsgId";
String KEY_PREVIOUS_MSG_ID = "previousMsgId"; String KEY_PREVIOUS_MSG_ID = "previousMsgId";
String KEY_AUTHOR_ID = "authorId"; String KEY_MEMBER_ID = "memberId";
String KEY_AUTHOR_NAME = "authorName"; String KEY_MEMBER_NAME = "memberName";
String KEY_AUTHOR_PUBLIC_KEY = "authorPublicKey"; String KEY_MEMBER_PUBLIC_KEY = "memberPublicKey";
} }

View File

@@ -36,14 +36,15 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
LocalAuthor creator, Author member) { LocalAuthor creator, Author member) {
try { try {
// Generate the signature // Generate the signature
BdfList toSign = BdfList.of(groupId, timestamp, member.getName(), int type = NEW_MEMBER.getInt();
member.getPublicKey()); BdfList toSign = BdfList.of(groupId, timestamp, type,
member.getName(), member.getPublicKey());
byte[] signature = byte[] signature =
clientHelper.sign(toSign, creator.getPrivateKey()); clientHelper.sign(toSign, creator.getPrivateKey());
// Compose the message // Compose the message
BdfList body = BdfList body =
BdfList.of(NEW_MEMBER.getInt(), member.getName(), BdfList.of(type, member.getName(),
member.getPublicKey(), signature); member.getPublicKey(), signature);
Message m = clientHelper.createMessage(groupId, timestamp, body); Message m = clientHelper.createMessage(groupId, timestamp, body);
@@ -60,14 +61,15 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
LocalAuthor member, MessageId newMemberId) { LocalAuthor member, MessageId newMemberId) {
try { try {
// Generate the signature // Generate the signature
BdfList toSign = BdfList.of(groupId, timestamp, member.getName(), int type = JOIN.getInt();
member.getPublicKey(), newMemberId); BdfList toSign = BdfList.of(groupId, timestamp, type,
member.getName(), member.getPublicKey(), newMemberId);
byte[] signature = byte[] signature =
clientHelper.sign(toSign, member.getPrivateKey()); clientHelper.sign(toSign, member.getPrivateKey());
// Compose the message // Compose the message
BdfList body = BdfList body =
BdfList.of(JOIN.getInt(), member.getName(), BdfList.of(type, member.getName(),
member.getPublicKey(), newMemberId, signature); member.getPublicKey(), newMemberId, signature);
Message m = clientHelper.createMessage(groupId, timestamp, body); Message m = clientHelper.createMessage(groupId, timestamp, body);
@@ -85,14 +87,16 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
MessageId previousMsgId) { MessageId previousMsgId) {
try { try {
// Generate the signature // Generate the signature
BdfList toSign = BdfList.of(groupId, timestamp, author.getName(), int type = POST.getInt();
author.getPublicKey(), parentId, previousMsgId, content); BdfList toSign = BdfList.of(groupId, timestamp, type,
author.getName(), author.getPublicKey(), parentId,
previousMsgId, content);
byte[] signature = byte[] signature =
clientHelper.sign(toSign, author.getPrivateKey()); clientHelper.sign(toSign, author.getPrivateKey());
// Compose the message // Compose the message
BdfList body = BdfList body =
BdfList.of(POST.getInt(), author.getName(), BdfList.of(type, author.getName(),
author.getPublicKey(), parentId, previousMsgId, author.getPublicKey(), parentId, previousMsgId,
content, signature); content, signature);
Message m = clientHelper.createMessage(groupId, timestamp, body); Message m = clientHelper.createMessage(groupId, timestamp, body);

View File

@@ -18,6 +18,7 @@ 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.security.GeneralSecurityException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -25,10 +26,13 @@ import java.util.Collections;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; 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_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.api.privategroup.MessageType.JOIN;
import static org.briarproject.api.privategroup.MessageType.NEW_MEMBER;
import static org.briarproject.api.privategroup.MessageType.POST;
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.privategroup.Constants.KEY_AUTHOR_ID; import static org.briarproject.privategroup.Constants.KEY_MEMBER_ID;
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_NAME; import static org.briarproject.privategroup.Constants.KEY_MEMBER_NAME;
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_PUBLIC_KEY; import static org.briarproject.privategroup.Constants.KEY_MEMBER_PUBLIC_KEY;
import static org.briarproject.privategroup.Constants.KEY_NEW_MEMBER_MSG_ID; import static org.briarproject.privategroup.Constants.KEY_NEW_MEMBER_MSG_ID;
import static org.briarproject.privategroup.Constants.KEY_PARENT_MSG_ID; import static org.briarproject.privategroup.Constants.KEY_PARENT_MSG_ID;
import static org.briarproject.privategroup.Constants.KEY_PREVIOUS_MSG_ID; import static org.briarproject.privategroup.Constants.KEY_PREVIOUS_MSG_ID;
@@ -60,29 +64,29 @@ class GroupMessageValidator extends BdfMessageValidator {
body.removeElementAt(0); body.removeElementAt(0);
// member_name (string) // member_name (string)
String member_name = body.getString(0); String memberName = body.getString(0);
checkLength(member_name, 1, MAX_AUTHOR_NAME_LENGTH); checkLength(memberName, 1, MAX_AUTHOR_NAME_LENGTH);
// member_public_key (raw) // member_public_key (raw)
byte[] member_public_key = body.getRaw(1); byte[] memberPublicKey = body.getRaw(1);
checkLength(member_public_key, 1, MAX_PUBLIC_KEY_LENGTH); checkLength(memberPublicKey, 1, MAX_PUBLIC_KEY_LENGTH);
BdfMessageContext c; BdfMessageContext c;
switch (MessageType.valueOf(type)) { switch (MessageType.valueOf(type)) {
case NEW_MEMBER: case NEW_MEMBER:
c = validateNewMember(m, g, body, member_name, c = validateNewMember(m, g, body, memberName,
member_public_key); memberPublicKey);
addMessageMetadata(c, member_name, member_public_key, addMessageMetadata(c, memberName, memberPublicKey,
m.getTimestamp()); m.getTimestamp());
break; break;
case JOIN: case JOIN:
c = validateJoin(m, g, body, member_name, member_public_key); c = validateJoin(m, g, body, memberName, memberPublicKey);
addMessageMetadata(c, member_name, member_public_key, addMessageMetadata(c, memberName, memberPublicKey,
m.getTimestamp()); m.getTimestamp());
break; break;
case POST: case POST:
c = validatePost(m, g, body, member_name, member_public_key); c = validatePost(m, g, body, memberName, memberPublicKey);
addMessageMetadata(c, member_name, member_public_key, addMessageMetadata(c, memberName, memberPublicKey,
m.getTimestamp()); m.getTimestamp());
break; break;
default: default:
@@ -93,7 +97,7 @@ class GroupMessageValidator extends BdfMessageValidator {
} }
private BdfMessageContext validateNewMember(Message m, Group g, private BdfMessageContext validateNewMember(Message m, Group g,
BdfList body, String member_name, byte[] member_public_key) BdfList body, String memberName, byte[] memberPublicKey)
throws InvalidMessageException, FormatException { throws InvalidMessageException, FormatException {
// The content is a BDF list with three elements // The content is a BDF list with three elements
@@ -105,11 +109,16 @@ class GroupMessageValidator extends BdfMessageValidator {
checkLength(signature, 1, MAX_SIGNATURE_LENGTH); checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
// Verify Signature // Verify Signature
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name, BdfList signed =
member_public_key); BdfList.of(g.getId(), m.getTimestamp(), NEW_MEMBER.getInt(),
memberName, memberPublicKey);
PrivateGroup group = groupFactory.parsePrivateGroup(g); PrivateGroup group = groupFactory.parsePrivateGroup(g);
byte[] creatorPublicKey = group.getAuthor().getPublicKey(); byte[] creatorPublicKey = group.getAuthor().getPublicKey();
clientHelper.verifySignature(signature, creatorPublicKey, signed); try {
clientHelper.verifySignature(signature, creatorPublicKey, signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
// Return the metadata and no dependencies // Return the metadata and no dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
@@ -117,7 +126,7 @@ class GroupMessageValidator extends BdfMessageValidator {
} }
private BdfMessageContext validateJoin(Message m, Group g, BdfList body, private BdfMessageContext validateJoin(Message m, Group g, BdfList body,
String member_name, byte[] member_public_key) String memberName, byte[] memberPublicKey)
throws InvalidMessageException, FormatException { throws InvalidMessageException, FormatException {
// The content is a BDF list with four elements // The content is a BDF list with four elements
@@ -126,8 +135,8 @@ class GroupMessageValidator extends BdfMessageValidator {
// new_member_id (raw) // new_member_id (raw)
// the identifier of a new member message // the identifier of a new member message
// with the same member_name and member_public_key // with the same member_name and member_public_key
byte[] new_member_id = body.getRaw(2); byte[] newMemberId = body.getRaw(2);
checkLength(new_member_id, MessageId.LENGTH); checkLength(newMemberId, MessageId.LENGTH);
// signature (raw) // signature (raw)
// a signature with the member's private key over a list with 5 elements // a signature with the member's private key over a list with 5 elements
@@ -135,22 +144,26 @@ class GroupMessageValidator extends BdfMessageValidator {
checkLength(signature, 1, MAX_SIGNATURE_LENGTH); checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
// Verify Signature // Verify Signature
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name, BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), JOIN.getInt(),
member_public_key, new_member_id); memberName, memberPublicKey, newMemberId);
clientHelper.verifySignature(signature, member_public_key, signed); try {
clientHelper.verifySignature(signature, memberPublicKey, signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
// The new member message is a dependency // The new member message is a dependency
Collection<MessageId> dependencies = Collection<MessageId> dependencies =
Collections.singleton(new MessageId(new_member_id)); Collections.singleton(new MessageId(newMemberId));
// Return the metadata and dependencies // Return the metadata and dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(KEY_NEW_MEMBER_MSG_ID, new_member_id); meta.put(KEY_NEW_MEMBER_MSG_ID, newMemberId);
return new BdfMessageContext(meta, dependencies); return new BdfMessageContext(meta, dependencies);
} }
private BdfMessageContext validatePost(Message m, Group g, BdfList body, private BdfMessageContext validatePost(Message m, Group g, BdfList body,
String member_name, byte[] member_public_key) String memberName, byte[] memberPublicKey)
throws InvalidMessageException, FormatException { throws InvalidMessageException, FormatException {
// The content is a BDF list with six elements // The content is a BDF list with six elements
@@ -158,15 +171,13 @@ class GroupMessageValidator extends BdfMessageValidator {
// parent_id (raw or null) // parent_id (raw or null)
// the identifier of the post to which this is a reply, if any // the identifier of the post to which this is a reply, if any
byte[] parent_id = body.getOptionalRaw(2); byte[] parentId = body.getOptionalRaw(2);
if (parent_id != null) { checkLength(parentId, MessageId.LENGTH);
checkLength(parent_id, MessageId.LENGTH);
}
// previous_message_id (raw) // previous_message_id (raw)
// the identifier of the member's previous post or join message // the identifier of the member's previous post or join message
byte[] previous_message_id = body.getRaw(3); byte[] previousMessageId = body.getRaw(3);
checkLength(previous_message_id, MessageId.LENGTH); checkLength(previousMessageId, MessageId.LENGTH);
// content (string) // content (string)
String content = body.getString(4); String content = body.getString(4);
@@ -178,20 +189,25 @@ class GroupMessageValidator extends BdfMessageValidator {
checkLength(signature, 1, MAX_SIGNATURE_LENGTH); checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
// Verify Signature // Verify Signature
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name, BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), POST.getInt(),
member_public_key, parent_id, previous_message_id, content); memberName, memberPublicKey, parentId, previousMessageId,
clientHelper.verifySignature(signature, member_public_key, signed); content);
try {
clientHelper.verifySignature(signature, memberPublicKey, signed);
} catch (GeneralSecurityException e) {
throw new InvalidMessageException(e);
}
// The parent post, if any, // The parent post, if any,
// and the member's previous message are dependencies // and the member's previous message are dependencies
Collection<MessageId> dependencies = new ArrayList<MessageId>(); Collection<MessageId> dependencies = new ArrayList<MessageId>();
if (parent_id != null) dependencies.add(new MessageId(parent_id)); if (parentId != null) dependencies.add(new MessageId(parentId));
dependencies.add(new MessageId(previous_message_id)); dependencies.add(new MessageId(previousMessageId));
// Return the metadata and dependencies // Return the metadata and dependencies
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
if (parent_id != null) meta.put(KEY_PARENT_MSG_ID, parent_id); if (parentId != null) meta.put(KEY_PARENT_MSG_ID, parentId);
meta.put(KEY_PREVIOUS_MSG_ID, previous_message_id); meta.put(KEY_PREVIOUS_MSG_ID, previousMessageId);
return new BdfMessageContext(meta, dependencies); return new BdfMessageContext(meta, dependencies);
} }
@@ -200,9 +216,9 @@ class GroupMessageValidator extends BdfMessageValidator {
c.getDictionary().put(KEY_TIMESTAMP, time); c.getDictionary().put(KEY_TIMESTAMP, time);
c.getDictionary().put(KEY_READ, false); c.getDictionary().put(KEY_READ, false);
Author a = authorFactory.createAuthor(authorName, pubKey); Author a = authorFactory.createAuthor(authorName, pubKey);
c.getDictionary().put(KEY_AUTHOR_ID, a.getId()); c.getDictionary().put(KEY_MEMBER_ID, a.getId());
c.getDictionary().put(KEY_AUTHOR_NAME, authorName); c.getDictionary().put(KEY_MEMBER_NAME, authorName);
c.getDictionary().put(KEY_AUTHOR_PUBLIC_KEY, pubKey); c.getDictionary().put(KEY_MEMBER_PUBLIC_KEY, pubKey);
} }
} }

View File

@@ -44,9 +44,9 @@ import static org.briarproject.api.identity.Author.Status.OURSELVES;
import static org.briarproject.api.privategroup.MessageType.JOIN; import static org.briarproject.api.privategroup.MessageType.JOIN;
import static org.briarproject.api.privategroup.MessageType.NEW_MEMBER; import static org.briarproject.api.privategroup.MessageType.NEW_MEMBER;
import static org.briarproject.api.privategroup.MessageType.POST; import static org.briarproject.api.privategroup.MessageType.POST;
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_ID; import static org.briarproject.privategroup.Constants.KEY_MEMBER_ID;
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_NAME; import static org.briarproject.privategroup.Constants.KEY_MEMBER_NAME;
import static org.briarproject.privategroup.Constants.KEY_AUTHOR_PUBLIC_KEY; import static org.briarproject.privategroup.Constants.KEY_MEMBER_PUBLIC_KEY;
import static org.briarproject.privategroup.Constants.KEY_NEW_MEMBER_MSG_ID; import static org.briarproject.privategroup.Constants.KEY_NEW_MEMBER_MSG_ID;
import static org.briarproject.privategroup.Constants.KEY_PARENT_MSG_ID; import static org.briarproject.privategroup.Constants.KEY_PARENT_MSG_ID;
import static org.briarproject.privategroup.Constants.KEY_PREVIOUS_MSG_ID; import static org.briarproject.privategroup.Constants.KEY_PREVIOUS_MSG_ID;
@@ -141,8 +141,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
private MessageId getPreviousMsgId(Transaction txn, GroupId g) private MessageId getPreviousMsgId(Transaction txn, GroupId g)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g); BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
byte[] previousMsgIdBytes = d.getOptionalRaw(KEY_PREVIOUS_MSG_ID); byte[] previousMsgIdBytes = d.getRaw(KEY_PREVIOUS_MSG_ID);
if (previousMsgIdBytes == null) throw new DbException();
return new MessageId(previousMsgIdBytes); return new MessageId(previousMsgIdBytes);
} }
@@ -191,9 +190,9 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
boolean read) { boolean read) {
meta.put(KEY_TIMESTAMP, m.getMessage().getTimestamp()); meta.put(KEY_TIMESTAMP, m.getMessage().getTimestamp());
meta.put(KEY_READ, read); meta.put(KEY_READ, read);
meta.put(KEY_AUTHOR_ID, m.getMember().getId()); meta.put(KEY_MEMBER_ID, m.getMember().getId());
meta.put(KEY_AUTHOR_NAME, m.getMember().getName()); meta.put(KEY_MEMBER_NAME, m.getMember().getName());
meta.put(KEY_AUTHOR_PUBLIC_KEY, m.getMember().getPublicKey()); meta.put(KEY_MEMBER_PUBLIC_KEY, m.getMember().getPublicKey());
} }
@Override @Override
@@ -269,11 +268,10 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
clientHelper.getMessageMetadataAsDictionary(txn, g); clientHelper.getMessageMetadataAsDictionary(txn, g);
// get all authors we need to get the status for // get all authors we need to get the status for
Set<AuthorId> authors = new HashSet<AuthorId>(); Set<AuthorId> authors = new HashSet<AuthorId>();
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) { for (BdfDictionary meta : metadata.values()) {
BdfDictionary meta = entry.getValue();
if (meta.getLong(KEY_TYPE) == NEW_MEMBER.getInt()) if (meta.getLong(KEY_TYPE) == NEW_MEMBER.getInt())
continue; continue;
byte[] idBytes = meta.getRaw(KEY_AUTHOR_ID); byte[] idBytes = meta.getRaw(KEY_MEMBER_ID);
authors.add(new AuthorId(idBytes)); authors.add(new AuthorId(idBytes));
} }
// get statuses for all authors // get statuses for all authors
@@ -308,9 +306,9 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
} }
long timestamp = meta.getLong(KEY_TIMESTAMP); long timestamp = meta.getLong(KEY_TIMESTAMP);
AuthorId authorId = new AuthorId(meta.getRaw(KEY_AUTHOR_ID)); AuthorId authorId = new AuthorId(meta.getRaw(KEY_MEMBER_ID));
String name = meta.getString(KEY_AUTHOR_NAME); String name = meta.getString(KEY_MEMBER_NAME);
byte[] publicKey = meta.getRaw(KEY_AUTHOR_PUBLIC_KEY); byte[] publicKey = meta.getRaw(KEY_MEMBER_PUBLIC_KEY);
Author author = new Author(authorId, name, publicKey); Author author = new Author(authorId, name, publicKey);
Status status; Status status;
@@ -361,8 +359,8 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
return false; return false;
} }
// NEW_MEMBER must have same member_name and member_public_key // NEW_MEMBER must have same member_name and member_public_key
if (!Arrays.equals(meta.getRaw(KEY_AUTHOR_ID), if (!Arrays.equals(meta.getRaw(KEY_MEMBER_ID),
newMemberMeta.getRaw(KEY_AUTHOR_ID))) { newMemberMeta.getRaw(KEY_MEMBER_ID))) {
// FIXME throw new InvalidMessageException() (#643) // FIXME throw new InvalidMessageException() (#643)
db.deleteMessage(txn, m.getId()); db.deleteMessage(txn, m.getId());
return false; return false;
@@ -401,8 +399,16 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
return false; return false;
} }
// previous message must be from same member // previous message must be from same member
if (!Arrays.equals(meta.getRaw(KEY_AUTHOR_ID), if (!Arrays.equals(meta.getRaw(KEY_MEMBER_ID),
previousMeta.getRaw(KEY_AUTHOR_ID))) { previousMeta.getRaw(KEY_MEMBER_ID))) {
// FIXME throw new InvalidMessageException() (#643)
db.deleteMessage(txn, m.getId());
return false;
}
// previous message must be a POST or JOIN
MessageType previousType = MessageType
.valueOf(previousMeta.getLong(KEY_TYPE).intValue());
if (previousType != JOIN && previousType != POST) {
// FIXME throw new InvalidMessageException() (#643) // FIXME throw new InvalidMessageException() (#643)
db.deleteMessage(txn, m.getId()); db.deleteMessage(txn, m.getId());
return false; return false;