From c86d971166215eb31f7e0a13cc9d7620b8f19123 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Thu, 17 Nov 2016 16:36:51 -0200
Subject: [PATCH] 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.
---
 .../briarproject/PrivateGroupManagerTest.java | 62 ++++++++-----------
 .../api/blogs/BlogPostFactory.java            |  5 ++
 .../api/clients/ClientHelper.java             |  6 +-
 .../api/crypto/CryptoComponent.java           | 20 ++++++
 .../api/forum/ForumPostFactory.java           |  4 ++
 .../api/privategroup/GroupMessageFactory.java |  6 +-
 .../invitation/GroupInvitationFactory.java    |  5 ++
 .../blogs/BlogPostFactoryImpl.java            |  6 +-
 .../briarproject/blogs/BlogPostValidator.java |  9 ++-
 .../clients/ClientHelperImpl.java             | 17 ++---
 .../crypto/CryptoComponentImpl.java           | 22 +++++++
 .../forum/ForumPostFactoryImpl.java           |  3 +-
 .../forum/ForumPostValidator.java             |  4 +-
 .../privategroup/GroupMessageFactoryImpl.java |  8 +--
 .../privategroup/GroupMessageValidator.java   | 13 ++--
 .../GroupInvitationFactoryImpl.java           |  3 +-
 .../invitation/GroupInvitationValidator.java  |  5 +-
 .../blogs/BlogPostValidatorTest.java          | 18 +++---
 .../clients/ClientHelperImplTest.java         | 28 +++------
 .../forum/ForumPostValidatorTest.java         | 21 ++++---
 20 files changed, 158 insertions(+), 107 deletions(-)

diff --git a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
index d7c9c051dc..ab8fa52cc1 100644
--- a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
+++ b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
@@ -5,6 +5,7 @@ import net.jodah.concurrentunit.Waiter;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.clients.ContactGroupFactory;
 import org.briarproject.api.clients.MessageTracker.GroupCount;
+import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
 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.PrivateGroupFactory;
 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.MessageId;
 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_US;
 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.ValidationManager.State.DELIVERED;
 import static org.briarproject.api.sync.ValidationManager.State.INVALID;
@@ -103,6 +104,8 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 	PrivateGroupFactory privateGroupFactory;
 	@Inject
 	GroupMessageFactory groupMessageFactory;
+	@Inject
+	GroupInvitationFactory groupInvitationFactory;
 
 	// objects accessed from background threads need to be volatile
 	private volatile Waiter validationWaiter;
@@ -358,13 +361,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author1 joins privateGroup0 with wrong timestamp
 		joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author1.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		Contact c1 = contactManager0.getContact(contactId1);
+		byte[] creatorSignature = groupInvitationFactory
+				.signInvitation(c1, privateGroup0.getId(), inviteTime,
+						author0.getPrivateKey());
 		GroupMessage joinMsg1 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
 						inviteTime, creatorSignature);
@@ -402,13 +402,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author0 joins privateGroup0 with wrong member's join message
 		long joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime - 1;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author0.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		BdfList toSign = groupInvitationFactory
+				.createInviteToken(author0.getId(), author0.getId(),
+						privateGroup0.getId(), inviteTime);
+		byte[] creatorSignature = clientHelper
+				.sign(SIGNING_LABEL_INVITE, toSign, author0.getPrivateKey());
 		// join message should not include invite time and creator's signature
 		GroupMessage joinMsg0 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author0,
@@ -426,13 +424,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author1 joins privateGroup0 with wrong signature in join message
 		joinTime = clock.currentTimeMillis();
 		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
-		creatorSignature = clientHelper.sign(toSign, author1.getPrivateKey());
+		Contact c1 = contactManager0.getContact(contactId1);
+		creatorSignature = groupInvitationFactory
+				.signInvitation(c1, privateGroup0.getId(), inviteTime,
+						author1.getPrivateKey());
 		GroupMessage joinMsg1 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
 						inviteTime, creatorSignature);
@@ -529,13 +525,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author2 joins privateGroup0
 		long joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime - 1;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author2.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		Contact c2 = contactManager0.getContact(contactId2);
+		byte[] creatorSignature = groupInvitationFactory
+				.signInvitation(c2, privateGroup0.getId(), inviteTime,
+						author0.getPrivateKey());
 		GroupMessage joinMsg2 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author2,
 						inviteTime, creatorSignature);
@@ -753,13 +746,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author1 joins privateGroup0
 		joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime - 1;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author1.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		Contact c1 = contactManager0.getContact(contactId1);
+		byte[] creatorSignature = groupInvitationFactory
+				.signInvitation(c1, privateGroup0.getId(), inviteTime,
+						author0.getPrivateKey());
 		GroupMessage joinMsg1 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
 						inviteTime, creatorSignature);
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
index 7b05c0078e..4f8292720a 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
@@ -11,8 +11,13 @@ import org.jetbrains.annotations.Nullable;
 
 import java.security.GeneralSecurityException;
 
+import static org.briarproject.api.blogs.BlogManager.CLIENT_ID;
+
 public interface BlogPostFactory {
 
+	String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
+	String SIGNING_LABEL_COMMENT = CLIENT_ID + "/COMMENT";
+
 	BlogPost createBlogPost(@NotNull GroupId groupId, long timestamp,
 			@Nullable MessageId parent, @NotNull LocalAuthor author,
 			@NotNull String body)
diff --git a/briar-api/src/org/briarproject/api/clients/ClientHelper.java b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
index 7743f3ba5e..32af81f627 100644
--- a/briar-api/src/org/briarproject/api/clients/ClientHelper.java
+++ b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
@@ -92,10 +92,10 @@ public interface ClientHelper {
 
 	BdfList toList(Message m) throws FormatException;
 
-	byte[] sign(BdfList toSign, byte[] privateKey)
+	byte[] sign(String label, BdfList toSign, byte[] privateKey)
 			throws FormatException, GeneralSecurityException;
 
-	void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
-			throws FormatException, GeneralSecurityException;
+	void verifySignature(String label, byte[] sig, byte[] publicKey,
+			BdfList signed) throws FormatException, GeneralSecurityException;
 
 }
diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
index 50a9c94d56..90830ac1d5 100644
--- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
+++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
@@ -143,6 +143,26 @@ public interface CryptoComponent {
 	/** Encodes the pseudo-random tag that is used to recognise a stream. */
 	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
 	 * combined by prefixing each input with its length.
diff --git a/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java b/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java
index 820a11acc2..d5b8d9b356 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java
@@ -8,8 +8,12 @@ import org.briarproject.api.sync.MessageId;
 
 import java.security.GeneralSecurityException;
 
+import static org.briarproject.api.forum.ForumManager.CLIENT_ID;
+
 public interface ForumPostFactory {
 
+	String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
+
 	@CryptoExecutor
 	ForumPost createPost(GroupId groupId, long timestamp, MessageId parent,
 			LocalAuthor author, String body)
diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
index ff1b72e84f..220e958fc0 100644
--- a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
+++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
@@ -1,16 +1,20 @@
 package org.briarproject.api.privategroup;
 
 import org.briarproject.api.crypto.CryptoExecutor;
-import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.Nullable;
 
+import static org.briarproject.api.privategroup.PrivateGroupManager.CLIENT_ID;
+
 @NotNullByDefault
 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.
 	 *
diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java
index e5cf41adf7..fdde77abe3 100644
--- a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java
+++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java
@@ -6,8 +6,12 @@ import org.briarproject.api.data.BdfList;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.sync.GroupId;
 
+import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
+
 public interface GroupInvitationFactory {
 
+	String SIGNING_LABEL_INVITE = CLIENT_ID + "/INVITE";
+
 	/**
 	 * Returns a signature to include when inviting a member to join a private
 	 * group. If the member accepts the invitation, the signature will be
@@ -24,4 +28,5 @@ public interface GroupInvitationFactory {
 	 */
 	BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
 			GroupId privateGroupId, long timestamp);
+
 }
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
index fdab87cac6..52cbc72a76 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
@@ -52,7 +52,8 @@ class BlogPostFactoryImpl implements BlogPostFactory {
 		BdfList signed = BdfList.of(groupId, timestamp, body);
 
 		// Generate the signature
-		byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
+		byte[] sig = clientHelper
+				.sign(SIGNING_LABEL_POST, signed, author.getPrivateKey());
 
 		// Serialise the signed message
 		BdfList message = BdfList.of(POST.getInt(), body, sig);
@@ -77,7 +78,8 @@ class BlogPostFactoryImpl implements BlogPostFactory {
 		// Generate the signature
 		BdfList signed =
 				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
 		BdfList message =
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
index 088354fc16..4b7dd148e3 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
@@ -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.MAX_BLOG_COMMENT_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.POST;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -105,7 +107,9 @@ class BlogPostValidator extends BdfMessageValidator {
 		Blog b = blogFactory.parseBlog(g);
 		Author a = b.getAuthor();
 		try {
-			clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+			clientHelper
+					.verifySignature(SIGNING_LABEL_POST, sig, a.getPublicKey(),
+							signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
@@ -150,7 +154,8 @@ class BlogPostValidator extends BdfMessageValidator {
 		Blog b = blogFactory.parseBlog(g);
 		Author a = b.getAuthor();
 		try {
-			clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+			clientHelper.verifySignature(SIGNING_LABEL_COMMENT, sig,
+					a.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
diff --git a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
index 7356ca1d1c..77a3caad1f 100644
--- a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
+++ b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
@@ -6,7 +6,6 @@ import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.KeyParser;
 import org.briarproject.api.crypto.PrivateKey;
 import org.briarproject.api.crypto.PublicKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.BdfReader;
@@ -346,27 +345,21 @@ class ClientHelperImpl implements ClientHelper {
 	}
 
 	@Override
-	public byte[] sign(BdfList toSign, byte[] privateKey)
+	public byte[] sign(String label, BdfList toSign, byte[] privateKey)
 			throws FormatException, GeneralSecurityException {
-		Signature signature = crypto.getSignature();
 		KeyParser keyParser = crypto.getSignatureKeyParser();
 		PrivateKey key = keyParser.parsePrivateKey(privateKey);
-		signature.initSign(key);
-		signature.update(toByteArray(toSign));
-		return signature.sign();
+		return crypto.sign(label, toByteArray(toSign), key);
 	}
 
 	@Override
-	public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
-			throws FormatException, GeneralSecurityException {
+	public void verifySignature(String label, byte[] sig, byte[] publicKey,
+			BdfList signed) throws FormatException, GeneralSecurityException {
 		// Parse the public key
 		KeyParser keyParser = crypto.getSignatureKeyParser();
 		PublicKey key = keyParser.parsePublicKey(publicKey);
 		// Verify the signature
-		Signature signature = crypto.getSignature();
-		signature.initVerify(key);
-		signature.update(toByteArray(signed));
-		if (!signature.verify(sig)) {
+		if (!crypto.verify(label, toByteArray(signed), key, sig)) {
 			throw new GeneralSecurityException("Invalid signature");
 		}
 	}
diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
index 22698a22ff..3ce6e0e61a 100644
--- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
+++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
@@ -1,5 +1,7 @@
 package org.briarproject.crypto;
 
+import com.google.common.primitives.Bytes;
+
 import org.briarproject.api.TransportId;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.KeyPair;
@@ -399,6 +401,26 @@ class CryptoComponentImpl implements CryptoComponent {
 		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
 	public byte[] hash(byte[]... inputs) {
 		MessageDigest digest = getMessageDigest();
diff --git a/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java b/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
index f4ddfad0f0..70a545e1f9 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
@@ -39,7 +39,8 @@ class ForumPostFactoryImpl implements ForumPostFactory {
 		BdfList signed = BdfList.of(groupId, timestamp, parent, authorList,
 				body);
 		// Sign the data
-		byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
+		byte[] sig = clientHelper
+				.sign(SIGNING_LABEL_POST, signed, author.getPrivateKey());
 		// Serialise the signed message
 		BdfList message = BdfList.of(parent, authorList, body, sig);
 		Message m = clientHelper.createMessage(groupId, timestamp, message);
diff --git a/briar-core/src/org/briarproject/forum/ForumPostValidator.java b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
index 19e62584c8..b7d2f70660 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostValidator.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
@@ -22,6 +22,7 @@ import java.util.Collection;
 import java.util.Collections;
 
 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_PUBLIC_KEY_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,
 				authorList, forumPostBody);
 		try {
-			clientHelper.verifySignature(sig, author.getPublicKey(), signed);
+			clientHelper.verifySignature(SIGNING_LABEL_POST, sig,
+					author.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
index aeb478001b..71d6ebbeef 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
@@ -51,8 +51,8 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			int type = JOIN.getInt();
 			BdfList toSign = BdfList.of(groupId, timestamp, type,
 					member.getName(), member.getPublicKey(), invite);
-			byte[] memberSignature =
-					clientHelper.sign(toSign, member.getPrivateKey());
+			byte[] memberSignature = clientHelper
+					.sign(SIGNING_LABEL_JOIN, toSign, member.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
@@ -78,8 +78,8 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			BdfList toSign = BdfList.of(groupId, timestamp, type,
 					author.getName(), author.getPublicKey(), parentId,
 					previousMsgId, content);
-			byte[] signature =
-					clientHelper.sign(toSign, author.getPrivateKey());
+			byte[] signature = clientHelper
+					.sign(SIGNING_LABEL_POST, toSign, author.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
index a80f1ce4b5..7e27d74685 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
@@ -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_PUBLIC_KEY_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.POST;
 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_MEMBER_ID;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_NAME;
@@ -130,7 +133,7 @@ class GroupMessageValidator extends BdfMessageValidator {
 					.createInviteToken(creator.getId(), member.getId(),
 							pg.getId(), inviteTimestamp);
 			try {
-				clientHelper.verifySignature(creatorSignature,
+				clientHelper.verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
 						creator.getPublicKey(), token);
 			} catch (GeneralSecurityException e) {
 				throw new InvalidMessageException(e);
@@ -146,8 +149,8 @@ class GroupMessageValidator extends BdfMessageValidator {
 		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), JOIN.getInt(),
 				member.getName(), member.getPublicKey(), invite);
 		try {
-			clientHelper.verifySignature(memberSignature, member.getPublicKey(),
-					signed);
+			clientHelper.verifySignature(SIGNING_LABEL_JOIN, memberSignature,
+					member.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
@@ -189,8 +192,8 @@ class GroupMessageValidator extends BdfMessageValidator {
 				member.getName(), member.getPublicKey(), parentId,
 				previousMessageId, content);
 		try {
-			clientHelper
-					.verifySignature(signature, member.getPublicKey(), signed);
+			clientHelper.verifySignature(SIGNING_LABEL_POST, signature,
+					member.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
diff --git a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java
index 3e9b8c88c6..502d4f0761 100644
--- a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java
@@ -36,7 +36,7 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
 		BdfList token = createInviteToken(creatorId, memberId, privateGroupId,
 				timestamp);
 		try {
-			return clientHelper.sign(token, privateKey);
+			return clientHelper.sign(SIGNING_LABEL_INVITE, token, privateKey);
 		} catch (GeneralSecurityException e) {
 			throw new IllegalArgumentException(e);
 		} catch (FormatException e) {
@@ -50,7 +50,6 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
 		Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID,
 				creatorId, memberId);
 		return BdfList.of(
-				0, // TODO: Replace with a namespaced string
 				timestamp,
 				contactGroup.getId(),
 				privateGroupId
diff --git a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java
index 15de431499..175a593c49 100644
--- a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java
@@ -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.MAX_GROUP_INVITATION_MSG_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.INVITE;
 import static org.briarproject.privategroup.invitation.MessageType.JOIN;
@@ -96,13 +97,13 @@ class GroupInvitationValidator extends BdfMessageValidator {
 				groupName, creator, salt);
 		// Verify the signature
 		BdfList signed = BdfList.of(
-				INVITE.getValue(),
 				m.getTimestamp(),
 				m.getGroupId(),
 				privateGroup.getId()
 		);
 		try {
-			clientHelper.verifySignature(signature, creatorPublicKey, signed);
+			clientHelper.verifySignature(SIGNING_LABEL_INVITE, signature,
+					creatorPublicKey, signed);
 		} catch (GeneralSecurityException e) {
 			throw new FormatException();
 		}
diff --git a/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java b/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
index 08943fc9f1..306b082a6b 100644
--- a/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
+++ b/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
@@ -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_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
+import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_COMMENT;
+import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_POST;
 import static org.briarproject.api.blogs.MessageType.COMMENT;
 import static org.briarproject.api.blogs.MessageType.POST;
 import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
@@ -101,7 +103,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), body);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
@@ -143,7 +145,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), comment,
 						pOriginalId, currentId);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
@@ -170,7 +172,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), null,
 						originalId, currentId);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
@@ -189,7 +191,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 		BdfList signed =
 				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 byte[] originalBody = TestUtils.getRandomBytes(42);
@@ -228,7 +230,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 		BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
 				comment, originalId, oldId);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
 
 		final BdfList originalList = BdfList.of(COMMENT.getInt(), comment,
 				originalId, oldId, sigBytes);
@@ -256,13 +258,13 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		context.assertIsSatisfied();
 	}
 
-	private void expectCrypto(final BdfList signed, final byte[] sig)
-			throws IOException, GeneralSecurityException {
+	private void expectCrypto(final String label, final BdfList signed,
+			final byte[] sig) throws IOException, GeneralSecurityException {
 		context.checking(new Expectations() {{
 			oneOf(blogFactory).parseBlog(group);
 			will(returnValue(blog));
 			oneOf(clientHelper)
-					.verifySignature(sig, author.getPublicKey(), signed);
+					.verifySignature(label, sig, author.getPublicKey(), signed);
 		}});
 	}
 
diff --git a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
index 73e17616a1..7c79464517 100644
--- a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
+++ b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
@@ -1,6 +1,7 @@
 package org.briarproject.clients;
 
 import org.briarproject.BriarTestCase;
+import org.briarproject.TestUtils;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.crypto.CryptoComponent;
@@ -70,6 +71,7 @@ public class ClientHelperImplTest extends BriarTestCase {
 			new Message(messageId, groupId, timestamp, rawMessage);
 	private final Metadata metadata = new Metadata();
 	private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
+	private final String label = TestUtils.getRandomString(5);
 
 	public ClientHelperImplTest() {
 		clientHelper =
@@ -290,19 +292,16 @@ public class ClientHelperImplTest extends BriarTestCase {
 
 		final byte[] bytes = expectToByteArray(list);
 		context.checking(new Expectations() {{
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
 			oneOf(cryptoComponent).getSignatureKeyParser();
 			will(returnValue(keyParser));
 			oneOf(keyParser).parsePrivateKey(privateKeyBytes);
 			will(returnValue(privateKey));
-			oneOf(signature).initSign(privateKey);
-			oneOf(signature).update(bytes);
-			oneOf(signature).sign();
+			oneOf(cryptoComponent).sign(label, bytes, privateKey);
 			will(returnValue(signed));
 		}});
 
-		assertArrayEquals(signed, clientHelper.sign(list, privateKeyBytes));
+		assertArrayEquals(signed,
+				clientHelper.sign(label, list, privateKeyBytes));
 		context.assertIsSatisfied();
 	}
 
@@ -317,15 +316,11 @@ public class ClientHelperImplTest extends BriarTestCase {
 			will(returnValue(keyParser));
 			oneOf(keyParser).parsePublicKey(publicKeyBytes);
 			will(returnValue(publicKey));
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(signature).initVerify(publicKey);
-			oneOf(signature).update(bytes);
-			oneOf(signature).verify(rawMessage);
+			oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
 			will(returnValue(true));
 		}});
 
-		clientHelper.verifySignature(rawMessage, publicKeyBytes, list);
+		clientHelper.verifySignature(label, rawMessage, publicKeyBytes, list);
 		context.assertIsSatisfied();
 	}
 
@@ -340,16 +335,13 @@ public class ClientHelperImplTest extends BriarTestCase {
 			will(returnValue(keyParser));
 			oneOf(keyParser).parsePublicKey(publicKeyBytes);
 			will(returnValue(publicKey));
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(signature).initVerify(publicKey);
-			oneOf(signature).update(bytes);
-			oneOf(signature).verify(rawMessage);
+			oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
 			will(returnValue(false));
 		}});
 
 		try {
-			clientHelper.verifySignature(rawMessage, publicKeyBytes, list);
+			clientHelper
+					.verifySignature(label, rawMessage, publicKeyBytes, list);
 			fail();
 		} catch (GeneralSecurityException e) {
 			// expected
diff --git a/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java b/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java
index 17e5c1266d..af048f6f1a 100644
--- a/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java
+++ b/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java
@@ -19,6 +19,7 @@ import java.security.GeneralSecurityException;
 import java.util.Collection;
 
 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_PUBLIC_KEY_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -70,8 +71,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithoutParent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithoutParent);
 		}});
 
 		ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -179,8 +180,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(shortAuthorName, authorPublicKey);
 			will(returnValue(shortNameAuthor));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithShortNameAuthor);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithShortNameAuthor);
 		}});
 
 		ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -267,8 +268,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithShortContent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithShortContent);
 		}});
 
 		ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -342,8 +343,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithParent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithParent);
 			will(throwException(new FormatException()));
 		}});
 
@@ -359,8 +360,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithParent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithParent);
 			will(throwException(new GeneralSecurityException()));
 		}});
 
-- 
GitLab