diff --git a/briar-api/src/org/briarproject/api/Bytes.java b/briar-api/src/org/briarproject/api/Bytes.java
index 91660eb7b37431c55232f622707e3fe29d650954..7fce90ad706471abf2e90132dd5bb1337dbb64b2 100644
--- a/briar-api/src/org/briarproject/api/Bytes.java
+++ b/briar-api/src/org/briarproject/api/Bytes.java
@@ -1,9 +1,14 @@
 package org.briarproject.api;
 
 import java.util.Arrays;
+import java.util.Comparator;
 
-/** A wrapper around a byte array, to allow it to be stored in maps etc. */
-public class Bytes {
+/**
+ * A wrapper around a byte array, to allow it to be stored in maps etc.
+ */
+public class Bytes implements Comparable<Bytes> {
+
+	public static final BytesComparator COMPARATOR = new BytesComparator();
 
 	private final byte[] bytes;
 
@@ -27,8 +32,26 @@ public class Bytes {
 
 	@Override
 	public boolean equals(Object o) {
-		if (o instanceof Bytes)
-			return Arrays.equals(bytes, ((Bytes) o).bytes);
-		return false;
+		return o instanceof Bytes && Arrays.equals(bytes, ((Bytes) o).bytes);
+	}
+
+	@Override
+	public int compareTo(Bytes other) {
+		byte[] aBytes = bytes, bBytes = other.bytes;
+		int length = Math.min(aBytes.length, bBytes.length);
+		for (int i = 0; i < length; i++) {
+			int aUnsigned = aBytes[i] & 0xFF, bUnsigned = bBytes[i] & 0xFF;
+			if (aUnsigned < bUnsigned) return -1;
+			if (aUnsigned > bUnsigned) return 1;
+		}
+		return aBytes.length - bBytes.length;
+	}
+
+	public static class BytesComparator implements Comparator<Bytes> {
+
+		@Override
+		public int compare(Bytes a, Bytes b) {
+			return a.compareTo(b);
+		}
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/DeviceId.java b/briar-api/src/org/briarproject/api/DeviceId.java
index 2b72d681dea60d9c234e954609537f7516494ad4..e6501f41c5cd5b1a6dc3243621e83726fc3d926d 100644
--- a/briar-api/src/org/briarproject/api/DeviceId.java
+++ b/briar-api/src/org/briarproject/api/DeviceId.java
@@ -1,8 +1,8 @@
 package org.briarproject.api;
 
-import java.util.Arrays;
-
-/** Type-safe wrapper for a byte array that uniquely identifies a device. */
+/**
+ * Type-safe wrapper for a byte array that uniquely identifies a device.
+ */
 public class DeviceId extends UniqueId {
 
 	public DeviceId(byte[] id) {
@@ -11,6 +11,6 @@ public class DeviceId extends UniqueId {
 
 	@Override
 	public boolean equals(Object o) {
-		return o instanceof DeviceId && Arrays.equals(id, ((DeviceId) o).id);
+		return o instanceof DeviceId && super.equals(o);
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/UniqueId.java b/briar-api/src/org/briarproject/api/UniqueId.java
index 9602a5fa4d98a65379e3f3c9d776ee69484e6a06..72c78d103272a42784aef24f8404cccbac9626d3 100644
--- a/briar-api/src/org/briarproject/api/UniqueId.java
+++ b/briar-api/src/org/briarproject/api/UniqueId.java
@@ -1,47 +1,12 @@
 package org.briarproject.api;
 
-import java.util.Arrays;
-import java.util.Comparator;
-
-public abstract class UniqueId {
+public abstract class UniqueId extends Bytes {
 
 	/** The length of a unique identifier in bytes. */
 	public static final int LENGTH = 32;
 
-	protected final byte[] id;
-
-	private int hashCode = -1;
-
 	protected UniqueId(byte[] id) {
+		super(id);
 		if (id.length != LENGTH) throw new IllegalArgumentException();
-		this.id = id;
-	}
-
-	public byte[] getBytes() {
-		return id;
-	}
-
-	@Override
-	public int hashCode() {
-		// Thread-safe because if two or more threads check and update the
-		// value, they'll calculate the same value
-		if (hashCode == -1) hashCode = Arrays.hashCode(id);
-		return hashCode;
-	}
-
-	public static class IdComparator implements Comparator<UniqueId> {
-
-		public static final IdComparator INSTANCE = new IdComparator();
-
-		@Override
-		public int compare(UniqueId a, UniqueId b) {
-			byte[] aBytes = a.getBytes(), bBytes = b.getBytes();
-			for (int i = 0; i < UniqueId.LENGTH; i++) {
-				int aUnsigned = aBytes[i] & 0xFF, bUnsigned = bBytes[i] & 0xFF;
-				if (aUnsigned < bUnsigned) return -1;
-				if (aUnsigned > bUnsigned) return 1;
-			}
-			return 0;
-		}
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/identity/AuthorId.java b/briar-api/src/org/briarproject/api/identity/AuthorId.java
index 4e346239c7e09325c70af864cf09773f76752107..00f35db0e9acd2192281cc488f9bf00bec516e23 100644
--- a/briar-api/src/org/briarproject/api/identity/AuthorId.java
+++ b/briar-api/src/org/briarproject/api/identity/AuthorId.java
@@ -3,7 +3,6 @@ package org.briarproject.api.identity;
 import org.briarproject.api.UniqueId;
 
 import java.nio.charset.Charset;
-import java.util.Arrays;
 
 /**
  * Type-safe wrapper for a byte array that uniquely identifies an
@@ -11,7 +10,9 @@ import java.util.Arrays;
  */
 public class AuthorId extends UniqueId {
 
-	/** Label for hashing authors to calculate their identities. */
+	/**
+	 * Label for hashing authors to calculate their identities.
+	 */
 	public static final byte[] LABEL =
 			"AUTHOR_ID".getBytes(Charset.forName("US-ASCII"));
 
@@ -21,6 +22,6 @@ public class AuthorId extends UniqueId {
 
 	@Override
 	public boolean equals(Object o) {
-		return o instanceof AuthorId && Arrays.equals(id, ((AuthorId) o).id);
+		return o instanceof AuthorId && super.equals(o);
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/sync/ClientId.java b/briar-api/src/org/briarproject/api/sync/ClientId.java
index d99f5c18dffa79668ac1a24c8dae48d1494b35c6..7fd5456c1c075dc6f56b336b435dfe4447542d90 100644
--- a/briar-api/src/org/briarproject/api/sync/ClientId.java
+++ b/briar-api/src/org/briarproject/api/sync/ClientId.java
@@ -2,8 +2,6 @@ package org.briarproject.api.sync;
 
 import org.briarproject.api.UniqueId;
 
-import java.util.Arrays;
-
 /**
  * Type-safe wrapper for a byte array that uniquely identifies a sync client.
  */
@@ -15,6 +13,6 @@ public class ClientId extends UniqueId {
 
 	@Override
 	public boolean equals(Object o) {
-		return o instanceof ClientId && Arrays.equals(id, ((ClientId) o).id);
+		return o instanceof ClientId && super.equals(o);
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/sync/GroupId.java b/briar-api/src/org/briarproject/api/sync/GroupId.java
index 6a86445578f0c80f740114b71ba30161a3766205..8a5ed3fe713b1c7455a2da14c4f25372dc6fee1a 100644
--- a/briar-api/src/org/briarproject/api/sync/GroupId.java
+++ b/briar-api/src/org/briarproject/api/sync/GroupId.java
@@ -3,14 +3,15 @@ package org.briarproject.api.sync;
 import org.briarproject.api.UniqueId;
 
 import java.nio.charset.Charset;
-import java.util.Arrays;
 
 /**
  * Type-safe wrapper for a byte array that uniquely identifies a {@link Group}.
  */
 public class GroupId extends UniqueId {
 
-	/** Label for hashing groups to calculate their identifiers. */
+	/**
+	 * Label for hashing groups to calculate their identifiers.
+	 */
 	public static final byte[] LABEL =
 			"GROUP_ID".getBytes(Charset.forName("US-ASCII"));
 
@@ -20,6 +21,6 @@ public class GroupId extends UniqueId {
 
 	@Override
 	public boolean equals(Object o) {
-		return o instanceof GroupId && Arrays.equals(id, ((GroupId) o).id);
+		return o instanceof GroupId && super.equals(o);
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/sync/MessageId.java b/briar-api/src/org/briarproject/api/sync/MessageId.java
index f437c511b4b09d56874e9a9ead439da00dda431d..3543460f645644b31f65f5bf218332fb04a31b18 100644
--- a/briar-api/src/org/briarproject/api/sync/MessageId.java
+++ b/briar-api/src/org/briarproject/api/sync/MessageId.java
@@ -3,7 +3,6 @@ package org.briarproject.api.sync;
 import org.briarproject.api.UniqueId;
 
 import java.nio.charset.Charset;
-import java.util.Arrays;
 
 /**
  * Type-safe wrapper for a byte array that uniquely identifies a
@@ -11,7 +10,9 @@ import java.util.Arrays;
  */
 public class MessageId extends UniqueId {
 
-	/** Label for hashing messages to calculate their identifiers. */
+	/**
+	 * Label for hashing messages to calculate their identifiers.
+	 */
 	public static final byte[] LABEL =
 			"MESSAGE_ID".getBytes(Charset.forName("US-ASCII"));
 
@@ -21,6 +22,6 @@ public class MessageId extends UniqueId {
 
 	@Override
 	public boolean equals(Object o) {
-		return o instanceof MessageId && Arrays.equals(id, ((MessageId) o).id);
+		return o instanceof MessageId && super.equals(o);
 	}
 }
diff --git a/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java b/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
index 679ff3427292b1732098704241732538cf9e3d83..424f8b328160f3bc0348370350cb960306eb68a2 100644
--- a/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
+++ b/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
@@ -2,7 +2,7 @@ package org.briarproject.sync;
 
 import com.google.inject.Inject;
 
-import org.briarproject.api.UniqueId;
+import org.briarproject.api.Bytes;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.data.BdfWriter;
 import org.briarproject.api.data.BdfWriterFactory;
@@ -40,7 +40,7 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
 		BdfWriter w = bdfWriterFactory.createWriter(out);
 		try {
 			w.writeListStart();
-			if (UniqueId.IdComparator.INSTANCE.compare(local, remote) < 0) {
+			if (Bytes.COMPARATOR.compare(local, remote) < 0) {
 				w.writeRaw(local.getBytes());
 				w.writeRaw(remote.getBytes());
 			} else {