diff --git a/briar-api/src/org/briarproject/api/clients/BaseMessage.java b/briar-api/src/org/briarproject/api/clients/BaseMessage.java index fbfb56337789d1e2cb3dbee512fc5e170b04b84e..ee3c0319f484e0d58889681ed1acd20f30b24d4c 100644 --- a/briar-api/src/org/briarproject/api/clients/BaseMessage.java +++ b/briar-api/src/org/briarproject/api/clients/BaseMessage.java @@ -3,7 +3,6 @@ package org.briarproject.api.clients; import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.sync.Message; import org.briarproject.api.sync.MessageId; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.annotation.concurrent.Immutable; diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java index 06f277460e7828d4231bbefc58b4a127e9b7be2c..4de6bbbb0a0430fba36ef09ddf1c2dd0ac610958 100644 --- a/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java +++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessage.java @@ -5,7 +5,6 @@ import org.briarproject.api.identity.Author; import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.sync.Message; import org.briarproject.api.sync.MessageId; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.annotation.concurrent.Immutable; @@ -14,16 +13,16 @@ import javax.annotation.concurrent.Immutable; @NotNullByDefault public class GroupMessage extends BaseMessage { - private final Author author; + private final Author member; public GroupMessage(Message message, @Nullable MessageId parent, - Author author) { + Author member) { super(message, parent); - this.author = author; + this.member = member; } - public Author getAuthor() { - return author; + public Author getMember() { + return member; } } diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java index 76f46314e96b752f3aba60f3eb69d3f1ee0d3281..26e7ae9c7b73f533b5136fc05a709d28347e171d 100644 --- a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java +++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java @@ -1,20 +1,58 @@ package org.briarproject.api.privategroup; -import org.briarproject.api.FormatException; -import org.briarproject.api.crypto.PrivateKey; +import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; -import org.jetbrains.annotations.NotNull; - -import java.security.GeneralSecurityException; +import org.jetbrains.annotations.Nullable; public interface GroupMessageFactory { - @NotNull + /** + * Creates a new member announcement that contains the joiner's identity + * and is signed by the creator. + * <p> + * When a new member accepts an invitation to the group, + * the creator sends this new member announcement to the group. + * + * @param groupId The ID of the group the new member joined + * @param timestamp The current timestamp + * @param creator The creator of the group with {@param groupId} + * @param member The new member that has just accepted an invitation + */ + @CryptoExecutor + GroupMessage createNewMemberMessage(GroupId groupId, long timestamp, + LocalAuthor creator, Author member); + + /** + * Creates a join announcement message + * that depends on a previous new member announcement. + * + * @param groupId The ID of the Group that is being joined + * @param timestamp Must be equal to the timestamp of the new member message + * @param member Our own LocalAuthor + * @param newMemberId The MessageId of the new member message + */ + @CryptoExecutor + GroupMessage createJoinMessage(GroupId groupId, long timestamp, + LocalAuthor member, MessageId newMemberId); + + /** + * Creates a group message + * + * @param groupId The ID of the Group that is posted in + * @param timestamp Must be greater than the timestamps of the parentId + * post, if any, and the member's previous message + * @param parentId The ID of the message that is replied to + * @param author The author of the group message + * @param body The content of the group message + * @param previousMsgId The ID of the author's previous message + * in this group + */ + @CryptoExecutor GroupMessage createGroupMessage(GroupId groupId, long timestamp, - MessageId parent, LocalAuthor author, String body) - throws FormatException, GeneralSecurityException; + @Nullable MessageId parentId, LocalAuthor author, String body, + MessageId previousMsgId); } diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java index 271f7712c02c3b24373bddbb715a77e36f490400..4569bc50d5d052f886e609b13f49c7d141ff61a1 100644 --- a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java +++ b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java @@ -3,18 +3,25 @@ package org.briarproject.privategroup; import org.briarproject.api.FormatException; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.data.BdfList; +import org.briarproject.api.identity.Author; import org.briarproject.api.identity.LocalAuthor; +import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.privategroup.GroupMessage; import org.briarproject.api.privategroup.GroupMessageFactory; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.Message; import org.briarproject.api.sync.MessageId; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.security.GeneralSecurityException; import javax.inject.Inject; +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; + +@NotNullByDefault class GroupMessageFactoryImpl implements GroupMessageFactory { private final ClientHelper clientHelper; @@ -24,20 +31,78 @@ class GroupMessageFactoryImpl implements GroupMessageFactory { this.clientHelper = clientHelper; } - @NotNull @Override - public GroupMessage createGroupMessage(GroupId groupId, long timestamp, - MessageId parent, LocalAuthor author, String body) - throws FormatException, GeneralSecurityException { + public GroupMessage createNewMemberMessage(GroupId groupId, long timestamp, + LocalAuthor creator, Author member) { + try { + // Generate the signature + BdfList toSign = BdfList.of(groupId, timestamp, member.getName(), + member.getPublicKey()); + byte[] signature = + clientHelper.sign(toSign, creator.getPrivateKey()); + + // Compose the message + BdfList body = + BdfList.of(NEW_MEMBER.getInt(), member.getName(), + member.getPublicKey(), signature); + Message m = clientHelper.createMessage(groupId, timestamp, body); + + return new GroupMessage(m, null, member); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } catch (FormatException e) { + throw new RuntimeException(e); + } + } + + @Override + public GroupMessage createJoinMessage(GroupId groupId, long timestamp, + LocalAuthor member, MessageId newMemberId) { + try { + // Generate the signature + BdfList toSign = BdfList.of(groupId, timestamp, member.getName(), + member.getPublicKey(), newMemberId); + byte[] signature = + clientHelper.sign(toSign, member.getPrivateKey()); + + // Compose the message + BdfList body = + BdfList.of(JOIN.getInt(), member.getName(), + member.getPublicKey(), newMemberId, signature); + Message m = clientHelper.createMessage(groupId, timestamp, body); + + return new GroupMessage(m, null, member); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } catch (FormatException e) { + throw new RuntimeException(e); + } + } - // Generate the signature - byte[] sig = clientHelper.sign(new BdfList(), author.getPrivateKey()); + @Override + public GroupMessage createGroupMessage(GroupId groupId, long timestamp, + @Nullable MessageId parentId, LocalAuthor author, String content, + MessageId previousMsgId) { + try { + // Generate the signature + BdfList toSign = BdfList.of(groupId, timestamp, author.getName(), + author.getPublicKey(), parentId, previousMsgId, content); + byte[] signature = + clientHelper.sign(toSign, author.getPrivateKey()); - // Compose the message - Message m = - clientHelper.createMessage(groupId, timestamp, new BdfList()); + // Compose the message + BdfList body = + BdfList.of(POST.getInt(), author.getName(), + author.getPublicKey(), parentId, previousMsgId, + content, signature); + Message m = clientHelper.createMessage(groupId, timestamp, body); - return new GroupMessage(m, parent, author); + return new GroupMessage(m, parentId, author); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } catch (FormatException e) { + throw new RuntimeException(e); + } } }