Require a label for signing

This adds a sign() and a verify() method to the CryptoComponent
that take a mandatory label argument to ensure that signatures can't be
repurposed.
This commit is contained in:
Torsten Grote
2016-11-17 16:36:51 -02:00
parent 9b09b64ad3
commit c86d971166
20 changed files with 158 additions and 107 deletions

View File

@@ -5,6 +5,7 @@ import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.ContactGroupFactory; import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageTracker.GroupCount; import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
@@ -29,7 +30,7 @@ import org.briarproject.api.privategroup.JoinMessageHeader;
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.PrivateGroupManager; import org.briarproject.api.privategroup.PrivateGroupManager;
import org.briarproject.api.sync.Group; import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.SyncSession; import org.briarproject.api.sync.SyncSession;
@@ -65,7 +66,7 @@ import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT; import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US; import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US;
import static org.briarproject.api.privategroup.Visibility.VISIBLE; import static org.briarproject.api.privategroup.Visibility.VISIBLE;
import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID; import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
import static org.briarproject.api.sync.Group.Visibility.SHARED; import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID; import static org.briarproject.api.sync.ValidationManager.State.INVALID;
@@ -103,6 +104,8 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
PrivateGroupFactory privateGroupFactory; PrivateGroupFactory privateGroupFactory;
@Inject @Inject
GroupMessageFactory groupMessageFactory; GroupMessageFactory groupMessageFactory;
@Inject
GroupInvitationFactory groupInvitationFactory;
// objects accessed from background threads need to be volatile // objects accessed from background threads need to be volatile
private volatile Waiter validationWaiter; private volatile Waiter validationWaiter;
@@ -358,13 +361,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// author1 joins privateGroup0 with wrong timestamp // author1 joins privateGroup0 with wrong timestamp
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
long inviteTime = joinTime; long inviteTime = joinTime;
Group invitationGroup = contactGroupFactory Contact c1 = contactManager0.getContact(contactId1);
.createContactGroup(CLIENT_ID, author0.getId(), byte[] creatorSignature = groupInvitationFactory
author1.getId()); .signInvitation(c1, privateGroup0.getId(), inviteTime,
BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(), author0.getPrivateKey());
privateGroup0.getId());
byte[] creatorSignature =
clientHelper.sign(toSign, author0.getPrivateKey());
GroupMessage joinMsg1 = groupMessageFactory GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1, .createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature); inviteTime, creatorSignature);
@@ -402,13 +402,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// author0 joins privateGroup0 with wrong member's join message // author0 joins privateGroup0 with wrong member's join message
long joinTime = clock.currentTimeMillis(); long joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1; long inviteTime = joinTime - 1;
Group invitationGroup = contactGroupFactory BdfList toSign = groupInvitationFactory
.createContactGroup(CLIENT_ID, author0.getId(), .createInviteToken(author0.getId(), author0.getId(),
author0.getId()); privateGroup0.getId(), inviteTime);
BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(), byte[] creatorSignature = clientHelper
privateGroup0.getId()); .sign(SIGNING_LABEL_INVITE, toSign, author0.getPrivateKey());
byte[] creatorSignature =
clientHelper.sign(toSign, author0.getPrivateKey());
// join message should not include invite time and creator's signature // join message should not include invite time and creator's signature
GroupMessage joinMsg0 = groupMessageFactory GroupMessage joinMsg0 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author0, .createJoinMessage(privateGroup0.getId(), joinTime, author0,
@@ -426,13 +424,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// author1 joins privateGroup0 with wrong signature in join message // author1 joins privateGroup0 with wrong signature in join message
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
inviteTime = joinTime - 1; inviteTime = joinTime - 1;
invitationGroup = contactGroupFactory
.createContactGroup(CLIENT_ID, author0.getId(),
author1.getId());
toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
privateGroup0.getId());
// signature uses joiner's key, not creator's key // signature uses joiner's key, not creator's key
creatorSignature = clientHelper.sign(toSign, author1.getPrivateKey()); Contact c1 = contactManager0.getContact(contactId1);
creatorSignature = groupInvitationFactory
.signInvitation(c1, privateGroup0.getId(), inviteTime,
author1.getPrivateKey());
GroupMessage joinMsg1 = groupMessageFactory GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1, .createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature); inviteTime, creatorSignature);
@@ -529,13 +525,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// author2 joins privateGroup0 // author2 joins privateGroup0
long joinTime = clock.currentTimeMillis(); long joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1; long inviteTime = joinTime - 1;
Group invitationGroup = contactGroupFactory Contact c2 = contactManager0.getContact(contactId2);
.createContactGroup(CLIENT_ID, author0.getId(), byte[] creatorSignature = groupInvitationFactory
author2.getId()); .signInvitation(c2, privateGroup0.getId(), inviteTime,
BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(), author0.getPrivateKey());
privateGroup0.getId());
byte[] creatorSignature =
clientHelper.sign(toSign, author0.getPrivateKey());
GroupMessage joinMsg2 = groupMessageFactory GroupMessage joinMsg2 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author2, .createJoinMessage(privateGroup0.getId(), joinTime, author2,
inviteTime, creatorSignature); inviteTime, creatorSignature);
@@ -753,13 +746,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
// author1 joins privateGroup0 // author1 joins privateGroup0
joinTime = clock.currentTimeMillis(); joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1; long inviteTime = joinTime - 1;
Group invitationGroup = contactGroupFactory Contact c1 = contactManager0.getContact(contactId1);
.createContactGroup(CLIENT_ID, author0.getId(), byte[] creatorSignature = groupInvitationFactory
author1.getId()); .signInvitation(c1, privateGroup0.getId(), inviteTime,
BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(), author0.getPrivateKey());
privateGroup0.getId());
byte[] creatorSignature =
clientHelper.sign(toSign, author0.getPrivateKey());
GroupMessage joinMsg1 = groupMessageFactory GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1, .createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature); inviteTime, creatorSignature);

View File

@@ -11,8 +11,13 @@ import org.jetbrains.annotations.Nullable;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import static org.briarproject.api.blogs.BlogManager.CLIENT_ID;
public interface BlogPostFactory { public interface BlogPostFactory {
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
String SIGNING_LABEL_COMMENT = CLIENT_ID + "/COMMENT";
BlogPost createBlogPost(@NotNull GroupId groupId, long timestamp, BlogPost createBlogPost(@NotNull GroupId groupId, long timestamp,
@Nullable MessageId parent, @NotNull LocalAuthor author, @Nullable MessageId parent, @NotNull LocalAuthor author,
@NotNull String body) @NotNull String body)

View File

@@ -92,10 +92,10 @@ public interface ClientHelper {
BdfList toList(Message m) throws FormatException; BdfList toList(Message m) throws FormatException;
byte[] sign(BdfList toSign, byte[] privateKey) byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException; throws FormatException, GeneralSecurityException;
void verifySignature(byte[] sig, byte[] publicKey, BdfList signed) void verifySignature(String label, byte[] sig, byte[] publicKey,
throws FormatException, GeneralSecurityException; BdfList signed) throws FormatException, GeneralSecurityException;
} }

View File

@@ -143,6 +143,26 @@ public interface CryptoComponent {
/** Encodes the pseudo-random tag that is used to recognise a stream. */ /** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber); void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
/**
* Signs the given byte[] with the given PrivateKey.
*
* @param label A label specific to this signature
* to ensure that the signature cannot be repurposed
*/
byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
throws GeneralSecurityException;
/**
* Verifies that the given signature is valid for the signedData
* and the given publicKey.
*
* @param label A label that was specific to this signature
* to ensure that the signature cannot be repurposed
* @return true if the signature was valid, false otherwise.
*/
boolean verify(String label, byte[] signedData, PublicKey publicKey,
byte[] signature) throws GeneralSecurityException;
/** /**
* Returns the hash of the given inputs. The inputs are unambiguously * Returns the hash of the given inputs. The inputs are unambiguously
* combined by prefixing each input with its length. * combined by prefixing each input with its length.

View File

@@ -8,8 +8,12 @@ import org.briarproject.api.sync.MessageId;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import static org.briarproject.api.forum.ForumManager.CLIENT_ID;
public interface ForumPostFactory { public interface ForumPostFactory {
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
@CryptoExecutor @CryptoExecutor
ForumPost createPost(GroupId groupId, long timestamp, MessageId parent, ForumPost createPost(GroupId groupId, long timestamp, MessageId parent,
LocalAuthor author, String body) LocalAuthor author, String body)

View File

@@ -1,16 +1,20 @@
package org.briarproject.api.privategroup; package org.briarproject.api.privategroup;
import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import static org.briarproject.api.privategroup.PrivateGroupManager.CLIENT_ID;
@NotNullByDefault @NotNullByDefault
public interface GroupMessageFactory { public interface GroupMessageFactory {
String SIGNING_LABEL_JOIN = CLIENT_ID + "/JOIN";
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
/** /**
* Creates a join announcement message for the creator of a group. * Creates a join announcement message for the creator of a group.
* *

View File

@@ -6,8 +6,12 @@ import org.briarproject.api.data.BdfList;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
public interface GroupInvitationFactory { public interface GroupInvitationFactory {
String SIGNING_LABEL_INVITE = CLIENT_ID + "/INVITE";
/** /**
* Returns a signature to include when inviting a member to join a private * Returns a signature to include when inviting a member to join a private
* group. If the member accepts the invitation, the signature will be * group. If the member accepts the invitation, the signature will be
@@ -24,4 +28,5 @@ public interface GroupInvitationFactory {
*/ */
BdfList createInviteToken(AuthorId creatorId, AuthorId memberId, BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
GroupId privateGroupId, long timestamp); GroupId privateGroupId, long timestamp);
} }

View File

@@ -52,7 +52,8 @@ class BlogPostFactoryImpl implements BlogPostFactory {
BdfList signed = BdfList.of(groupId, timestamp, body); BdfList signed = BdfList.of(groupId, timestamp, body);
// Generate the signature // Generate the signature
byte[] sig = clientHelper.sign(signed, author.getPrivateKey()); byte[] sig = clientHelper
.sign(SIGNING_LABEL_POST, signed, author.getPrivateKey());
// Serialise the signed message // Serialise the signed message
BdfList message = BdfList.of(POST.getInt(), body, sig); BdfList message = BdfList.of(POST.getInt(), body, sig);
@@ -77,7 +78,8 @@ class BlogPostFactoryImpl implements BlogPostFactory {
// Generate the signature // Generate the signature
BdfList signed = BdfList signed =
BdfList.of(groupId, timestamp, comment, pOriginalId, parentId); BdfList.of(groupId, timestamp, comment, pOriginalId, parentId);
byte[] sig = clientHelper.sign(signed, author.getPrivateKey()); byte[] sig = clientHelper
.sign(SIGNING_LABEL_COMMENT, signed, author.getPrivateKey());
// Serialise the signed message // Serialise the signed message
BdfList message = BdfList message =

View File

@@ -39,6 +39,8 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE; import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_COMMENT_LENGTH; import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_COMMENT_LENGTH;
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH; import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_COMMENT;
import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_POST;
import static org.briarproject.api.blogs.MessageType.COMMENT; import static org.briarproject.api.blogs.MessageType.COMMENT;
import static org.briarproject.api.blogs.MessageType.POST; import static org.briarproject.api.blogs.MessageType.POST;
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -105,7 +107,9 @@ class BlogPostValidator extends BdfMessageValidator {
Blog b = blogFactory.parseBlog(g); Blog b = blogFactory.parseBlog(g);
Author a = b.getAuthor(); Author a = b.getAuthor();
try { try {
clientHelper.verifySignature(sig, a.getPublicKey(), signed); clientHelper
.verifySignature(SIGNING_LABEL_POST, sig, a.getPublicKey(),
signed);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
@@ -150,7 +154,8 @@ class BlogPostValidator extends BdfMessageValidator {
Blog b = blogFactory.parseBlog(g); Blog b = blogFactory.parseBlog(g);
Author a = b.getAuthor(); Author a = b.getAuthor();
try { try {
clientHelper.verifySignature(sig, a.getPublicKey(), signed); clientHelper.verifySignature(SIGNING_LABEL_COMMENT, sig,
a.getPublicKey(), signed);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }

View File

@@ -6,7 +6,6 @@ import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser; import org.briarproject.api.crypto.KeyParser;
import org.briarproject.api.crypto.PrivateKey; import org.briarproject.api.crypto.PrivateKey;
import org.briarproject.api.crypto.PublicKey; import org.briarproject.api.crypto.PublicKey;
import org.briarproject.api.crypto.Signature;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfReader;
@@ -346,27 +345,21 @@ class ClientHelperImpl implements ClientHelper {
} }
@Override @Override
public byte[] sign(BdfList toSign, byte[] privateKey) public byte[] sign(String label, BdfList toSign, byte[] privateKey)
throws FormatException, GeneralSecurityException { throws FormatException, GeneralSecurityException {
Signature signature = crypto.getSignature();
KeyParser keyParser = crypto.getSignatureKeyParser(); KeyParser keyParser = crypto.getSignatureKeyParser();
PrivateKey key = keyParser.parsePrivateKey(privateKey); PrivateKey key = keyParser.parsePrivateKey(privateKey);
signature.initSign(key); return crypto.sign(label, toByteArray(toSign), key);
signature.update(toByteArray(toSign));
return signature.sign();
} }
@Override @Override
public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed) public void verifySignature(String label, byte[] sig, byte[] publicKey,
throws FormatException, GeneralSecurityException { BdfList signed) throws FormatException, GeneralSecurityException {
// Parse the public key // Parse the public key
KeyParser keyParser = crypto.getSignatureKeyParser(); KeyParser keyParser = crypto.getSignatureKeyParser();
PublicKey key = keyParser.parsePublicKey(publicKey); PublicKey key = keyParser.parsePublicKey(publicKey);
// Verify the signature // Verify the signature
Signature signature = crypto.getSignature(); if (!crypto.verify(label, toByteArray(signed), key, sig)) {
signature.initVerify(key);
signature.update(toByteArray(signed));
if (!signature.verify(sig)) {
throw new GeneralSecurityException("Invalid signature"); throw new GeneralSecurityException("Invalid signature");
} }
} }

View File

@@ -1,5 +1,7 @@
package org.briarproject.crypto; package org.briarproject.crypto;
import com.google.common.primitives.Bytes;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair; import org.briarproject.api.crypto.KeyPair;
@@ -399,6 +401,26 @@ class CryptoComponentImpl implements CryptoComponent {
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
} }
@Override
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
throws GeneralSecurityException {
Signature signature = getSignature();
signature.initSign(privateKey);
toSign = Bytes.concat(StringUtils.toUtf8(label), toSign);
signature.update(toSign);
return signature.sign();
}
@Override
public boolean verify(String label, byte[] signedData, PublicKey publicKey,
byte[] signature) throws GeneralSecurityException {
Signature sig = getSignature();
sig.initVerify(publicKey);
signedData = Bytes.concat(StringUtils.toUtf8(label), signedData);
sig.update(signedData);
return sig.verify(signature);
}
@Override @Override
public byte[] hash(byte[]... inputs) { public byte[] hash(byte[]... inputs) {
MessageDigest digest = getMessageDigest(); MessageDigest digest = getMessageDigest();

View File

@@ -39,7 +39,8 @@ class ForumPostFactoryImpl implements ForumPostFactory {
BdfList signed = BdfList.of(groupId, timestamp, parent, authorList, BdfList signed = BdfList.of(groupId, timestamp, parent, authorList,
body); body);
// Sign the data // Sign the data
byte[] sig = clientHelper.sign(signed, author.getPrivateKey()); byte[] sig = clientHelper
.sign(SIGNING_LABEL_POST, signed, author.getPrivateKey());
// Serialise the signed message // Serialise the signed message
BdfList message = BdfList.of(parent, authorList, body, sig); BdfList message = BdfList.of(parent, authorList, body, sig);
Message m = clientHelper.createMessage(groupId, timestamp, message); Message m = clientHelper.createMessage(groupId, timestamp, message);

View File

@@ -22,6 +22,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
import static org.briarproject.api.forum.ForumPostFactory.SIGNING_LABEL_POST;
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;
@@ -68,7 +69,8 @@ class ForumPostValidator extends BdfMessageValidator {
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent, BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
authorList, forumPostBody); authorList, forumPostBody);
try { try {
clientHelper.verifySignature(sig, author.getPublicKey(), signed); clientHelper.verifySignature(SIGNING_LABEL_POST, sig,
author.getPublicKey(), signed);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }

View File

@@ -51,8 +51,8 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
int type = JOIN.getInt(); int type = JOIN.getInt();
BdfList toSign = BdfList.of(groupId, timestamp, type, BdfList toSign = BdfList.of(groupId, timestamp, type,
member.getName(), member.getPublicKey(), invite); member.getName(), member.getPublicKey(), invite);
byte[] memberSignature = byte[] memberSignature = clientHelper
clientHelper.sign(toSign, member.getPrivateKey()); .sign(SIGNING_LABEL_JOIN, toSign, member.getPrivateKey());
// Compose the message // Compose the message
BdfList body = BdfList body =
@@ -78,8 +78,8 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
BdfList toSign = BdfList.of(groupId, timestamp, type, BdfList toSign = BdfList.of(groupId, timestamp, type,
author.getName(), author.getPublicKey(), parentId, author.getName(), author.getPublicKey(), parentId,
previousMsgId, content); previousMsgId, content);
byte[] signature = byte[] signature = clientHelper
clientHelper.sign(toSign, author.getPrivateKey()); .sign(SIGNING_LABEL_POST, toSign, author.getPrivateKey());
// Compose the message // Compose the message
BdfList body = BdfList body =

View File

@@ -27,9 +27,12 @@ import java.util.Collection;
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.GroupMessageFactory.SIGNING_LABEL_JOIN;
import static org.briarproject.api.privategroup.GroupMessageFactory.SIGNING_LABEL_POST;
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.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.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG; import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG;
import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_ID; import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_ID;
import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_NAME; import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_NAME;
@@ -130,7 +133,7 @@ class GroupMessageValidator extends BdfMessageValidator {
.createInviteToken(creator.getId(), member.getId(), .createInviteToken(creator.getId(), member.getId(),
pg.getId(), inviteTimestamp); pg.getId(), inviteTimestamp);
try { try {
clientHelper.verifySignature(creatorSignature, clientHelper.verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
creator.getPublicKey(), token); creator.getPublicKey(), token);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
@@ -146,8 +149,8 @@ class GroupMessageValidator extends BdfMessageValidator {
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), JOIN.getInt(), BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), JOIN.getInt(),
member.getName(), member.getPublicKey(), invite); member.getName(), member.getPublicKey(), invite);
try { try {
clientHelper.verifySignature(memberSignature, member.getPublicKey(), clientHelper.verifySignature(SIGNING_LABEL_JOIN, memberSignature,
signed); member.getPublicKey(), signed);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }
@@ -189,8 +192,8 @@ class GroupMessageValidator extends BdfMessageValidator {
member.getName(), member.getPublicKey(), parentId, member.getName(), member.getPublicKey(), parentId,
previousMessageId, content); previousMessageId, content);
try { try {
clientHelper clientHelper.verifySignature(SIGNING_LABEL_POST, signature,
.verifySignature(signature, member.getPublicKey(), signed); member.getPublicKey(), signed);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new InvalidMessageException(e); throw new InvalidMessageException(e);
} }

View File

@@ -36,7 +36,7 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
BdfList token = createInviteToken(creatorId, memberId, privateGroupId, BdfList token = createInviteToken(creatorId, memberId, privateGroupId,
timestamp); timestamp);
try { try {
return clientHelper.sign(token, privateKey); return clientHelper.sign(SIGNING_LABEL_INVITE, token, privateKey);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} catch (FormatException e) { } catch (FormatException e) {
@@ -50,7 +50,6 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID, Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID,
creatorId, memberId); creatorId, memberId);
return BdfList.of( return BdfList.of(
0, // TODO: Replace with a namespaced string
timestamp, timestamp,
contactGroup.getId(), contactGroup.getId(),
privateGroupId privateGroupId

View File

@@ -31,6 +31,7 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH
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_INVITATION_MSG_LENGTH; import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH; import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
import static org.briarproject.privategroup.invitation.MessageType.ABORT; import static org.briarproject.privategroup.invitation.MessageType.ABORT;
import static org.briarproject.privategroup.invitation.MessageType.INVITE; import static org.briarproject.privategroup.invitation.MessageType.INVITE;
import static org.briarproject.privategroup.invitation.MessageType.JOIN; import static org.briarproject.privategroup.invitation.MessageType.JOIN;
@@ -96,13 +97,13 @@ class GroupInvitationValidator extends BdfMessageValidator {
groupName, creator, salt); groupName, creator, salt);
// Verify the signature // Verify the signature
BdfList signed = BdfList.of( BdfList signed = BdfList.of(
INVITE.getValue(),
m.getTimestamp(), m.getTimestamp(),
m.getGroupId(), m.getGroupId(),
privateGroup.getId() privateGroup.getId()
); );
try { try {
clientHelper.verifySignature(signature, creatorPublicKey, signed); clientHelper.verifySignature(SIGNING_LABEL_INVITE, signature,
creatorPublicKey, signed);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new FormatException(); throw new FormatException();
} }

View File

@@ -38,6 +38,8 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_PARENT_MSG_I
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_MSG_ID; import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_MSG_ID;
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY; import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.KEY_READ; import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_COMMENT;
import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_POST;
import static org.briarproject.api.blogs.MessageType.COMMENT; import static org.briarproject.api.blogs.MessageType.COMMENT;
import static org.briarproject.api.blogs.MessageType.POST; import static org.briarproject.api.blogs.MessageType.POST;
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT; import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
@@ -101,7 +103,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
BdfList signed = BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), body); BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(signed, sigBytes); expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
final BdfDictionary result = final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary(); validator.validateMessage(message, group, m).getDictionary();
@@ -143,7 +145,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
BdfList signed = BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), comment, BdfList.of(blog.getId(), message.getTimestamp(), comment,
pOriginalId, currentId); pOriginalId, currentId);
expectCrypto(signed, sigBytes); expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
final BdfDictionary result = final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary(); validator.validateMessage(message, group, m).getDictionary();
@@ -170,7 +172,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
BdfList signed = BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), null, BdfList.of(blog.getId(), message.getTimestamp(), null,
originalId, currentId); originalId, currentId);
expectCrypto(signed, sigBytes); expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
final BdfDictionary result = final BdfDictionary result =
validator.validateMessage(message, group, m).getDictionary(); validator.validateMessage(message, group, m).getDictionary();
@@ -189,7 +191,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
BdfList signed = BdfList signed =
BdfList.of(blog.getId(), message.getTimestamp(), body); BdfList.of(blog.getId(), message.getTimestamp(), body);
expectCrypto(signed, sigBytes); expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
final BdfList originalList = BdfList.of(POST.getInt(), body, sigBytes); final BdfList originalList = BdfList.of(POST.getInt(), body, sigBytes);
final byte[] originalBody = TestUtils.getRandomBytes(42); final byte[] originalBody = TestUtils.getRandomBytes(42);
@@ -228,7 +230,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(), BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
comment, originalId, oldId); comment, originalId, oldId);
expectCrypto(signed, sigBytes); expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
final BdfList originalList = BdfList.of(COMMENT.getInt(), comment, final BdfList originalList = BdfList.of(COMMENT.getInt(), comment,
originalId, oldId, sigBytes); originalId, oldId, sigBytes);
@@ -256,13 +258,13 @@ public class BlogPostValidatorTest extends BriarTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
private void expectCrypto(final BdfList signed, final byte[] sig) private void expectCrypto(final String label, final BdfList signed,
throws IOException, GeneralSecurityException { final byte[] sig) throws IOException, GeneralSecurityException {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(blogFactory).parseBlog(group); oneOf(blogFactory).parseBlog(group);
will(returnValue(blog)); will(returnValue(blog));
oneOf(clientHelper) oneOf(clientHelper)
.verifySignature(sig, author.getPublicKey(), signed); .verifySignature(label, sig, author.getPublicKey(), signed);
}}); }});
} }

View File

@@ -1,6 +1,7 @@
package org.briarproject.clients; package org.briarproject.clients;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
@@ -70,6 +71,7 @@ public class ClientHelperImplTest extends BriarTestCase {
new Message(messageId, groupId, timestamp, rawMessage); new Message(messageId, groupId, timestamp, rawMessage);
private final Metadata metadata = new Metadata(); private final Metadata metadata = new Metadata();
private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42)); private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
private final String label = TestUtils.getRandomString(5);
public ClientHelperImplTest() { public ClientHelperImplTest() {
clientHelper = clientHelper =
@@ -290,19 +292,16 @@ public class ClientHelperImplTest extends BriarTestCase {
final byte[] bytes = expectToByteArray(list); final byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).getSignature();
will(returnValue(signature));
oneOf(cryptoComponent).getSignatureKeyParser(); oneOf(cryptoComponent).getSignatureKeyParser();
will(returnValue(keyParser)); will(returnValue(keyParser));
oneOf(keyParser).parsePrivateKey(privateKeyBytes); oneOf(keyParser).parsePrivateKey(privateKeyBytes);
will(returnValue(privateKey)); will(returnValue(privateKey));
oneOf(signature).initSign(privateKey); oneOf(cryptoComponent).sign(label, bytes, privateKey);
oneOf(signature).update(bytes);
oneOf(signature).sign();
will(returnValue(signed)); will(returnValue(signed));
}}); }});
assertArrayEquals(signed, clientHelper.sign(list, privateKeyBytes)); assertArrayEquals(signed,
clientHelper.sign(label, list, privateKeyBytes));
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -317,15 +316,11 @@ public class ClientHelperImplTest extends BriarTestCase {
will(returnValue(keyParser)); will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(publicKeyBytes); oneOf(keyParser).parsePublicKey(publicKeyBytes);
will(returnValue(publicKey)); will(returnValue(publicKey));
oneOf(cryptoComponent).getSignature(); oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
will(returnValue(signature));
oneOf(signature).initVerify(publicKey);
oneOf(signature).update(bytes);
oneOf(signature).verify(rawMessage);
will(returnValue(true)); will(returnValue(true));
}}); }});
clientHelper.verifySignature(rawMessage, publicKeyBytes, list); clientHelper.verifySignature(label, rawMessage, publicKeyBytes, list);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -340,16 +335,13 @@ public class ClientHelperImplTest extends BriarTestCase {
will(returnValue(keyParser)); will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(publicKeyBytes); oneOf(keyParser).parsePublicKey(publicKeyBytes);
will(returnValue(publicKey)); will(returnValue(publicKey));
oneOf(cryptoComponent).getSignature(); oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
will(returnValue(signature));
oneOf(signature).initVerify(publicKey);
oneOf(signature).update(bytes);
oneOf(signature).verify(rawMessage);
will(returnValue(false)); will(returnValue(false));
}}); }});
try { try {
clientHelper.verifySignature(rawMessage, publicKeyBytes, list); clientHelper
.verifySignature(label, rawMessage, publicKeyBytes, list);
fail(); fail();
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
// expected // expected

View File

@@ -19,6 +19,7 @@ import java.security.GeneralSecurityException;
import java.util.Collection; import java.util.Collection;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
import static org.briarproject.api.forum.ForumPostFactory.SIGNING_LABEL_POST;
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;
@@ -70,8 +71,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(authorFactory).createAuthor(authorName, authorPublicKey); oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
will(returnValue(author)); will(returnValue(author));
oneOf(clientHelper).verifySignature(signature, authorPublicKey, oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
signedWithoutParent); authorPublicKey, signedWithoutParent);
}}); }});
ForumPostValidator v = new ForumPostValidator(authorFactory, ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -179,8 +180,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(authorFactory).createAuthor(shortAuthorName, authorPublicKey); oneOf(authorFactory).createAuthor(shortAuthorName, authorPublicKey);
will(returnValue(shortNameAuthor)); will(returnValue(shortNameAuthor));
oneOf(clientHelper).verifySignature(signature, authorPublicKey, oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
signedWithShortNameAuthor); authorPublicKey, signedWithShortNameAuthor);
}}); }});
ForumPostValidator v = new ForumPostValidator(authorFactory, ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -267,8 +268,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(authorFactory).createAuthor(authorName, authorPublicKey); oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
will(returnValue(author)); will(returnValue(author));
oneOf(clientHelper).verifySignature(signature, authorPublicKey, oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
signedWithShortContent); authorPublicKey, signedWithShortContent);
}}); }});
ForumPostValidator v = new ForumPostValidator(authorFactory, ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -342,8 +343,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(authorFactory).createAuthor(authorName, authorPublicKey); oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
will(returnValue(author)); will(returnValue(author));
oneOf(clientHelper).verifySignature(signature, authorPublicKey, oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
signedWithParent); authorPublicKey, signedWithParent);
will(throwException(new FormatException())); will(throwException(new FormatException()));
}}); }});
@@ -359,8 +360,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(authorFactory).createAuthor(authorName, authorPublicKey); oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
will(returnValue(author)); will(returnValue(author));
oneOf(clientHelper).verifySignature(signature, authorPublicKey, oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
signedWithParent); authorPublicKey, signedWithParent);
will(throwException(new GeneralSecurityException())); will(throwException(new GeneralSecurityException()));
}}); }});