diff --git a/test/net/sf/briar/crypto/CounterModeTest.java b/test/net/sf/briar/crypto/CounterModeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..176296af9dafd14ae995861cdf0ddd5ffce1e1ae
--- /dev/null
+++ b/test/net/sf/briar/crypto/CounterModeTest.java
@@ -0,0 +1,163 @@
+package net.sf.briar.crypto;
+
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import junit.framework.TestCase;
+import net.sf.briar.api.serial.Bytes;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Test;
+
+public class CounterModeTest extends TestCase {
+
+	private static final String CIPHER_ALGO = "AES";
+	private static final String CIPHER_MODE = "AES/CTR/NoPadding";
+	private static final String PROVIDER = "BC";
+	private static final int KEY_SIZE_BYTES = 32; // AES-256
+	private static final int BLOCK_SIZE_BYTES = 16;
+
+	private final SecureRandom random;
+	private final byte[] keyBytes;
+	private final SecretKeySpec key;
+
+	public CounterModeTest() {
+		super();
+		Security.addProvider(new BouncyCastleProvider());
+		random = new SecureRandom();
+		keyBytes = new byte[KEY_SIZE_BYTES];
+		random.nextBytes(keyBytes);
+		key = new SecretKeySpec(keyBytes, CIPHER_ALGO);
+	}
+
+	@Test
+	public void testEveryBitOfIvIsSignificant()
+	throws GeneralSecurityException {
+		// Set each bit of the IV in turn, encrypt the same plaintext and check
+		// that all the resulting ciphertexts are distinct
+		byte[] plaintext = new byte[BLOCK_SIZE_BYTES];
+		random.nextBytes(plaintext);
+		Set<Bytes> ciphertexts = new HashSet<Bytes>();
+		for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) {
+			// Set the i^th bit of the IV
+			byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
+			ivBytes[i / 8] |= (byte) (128 >> i % 8);
+			IvParameterSpec iv = new IvParameterSpec(ivBytes);
+			// Encrypt the plaintext
+			Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
+			cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+			byte[] ciphertext =
+				new byte[cipher.getOutputSize(plaintext.length)];
+			cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
+			ciphertexts.add(new Bytes(ciphertext));
+		}
+		// All the ciphertexts should be distinct using Arrays.equals()
+		assertEquals(BLOCK_SIZE_BYTES * 8, ciphertexts.size());
+	}
+
+	@Test
+	public void testRepeatedIvsProduceRepeatedCiphertexts()
+	throws GeneralSecurityException {
+		// This is the inverse of the previous test, to check that the
+		// distinct ciphertexts were due to using distinct IVs
+		byte[] plaintext = new byte[BLOCK_SIZE_BYTES];
+		random.nextBytes(plaintext);
+		byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
+		random.nextBytes(ivBytes);
+		IvParameterSpec iv = new IvParameterSpec(ivBytes);
+		Set<Bytes> ciphertexts = new HashSet<Bytes>();
+		for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) {
+			Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
+			cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+			byte[] ciphertext =
+				new byte[cipher.getOutputSize(plaintext.length)];
+			cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
+			ciphertexts.add(new Bytes(ciphertext));
+		}
+		assertEquals(1, ciphertexts.size());
+	}
+
+	@Test
+	public void testLeastSignificantBitsUsedAsCounter()
+	throws GeneralSecurityException {
+		// Initialise the least significant 32 bits of the IV to zero and
+		// encrypt ten blocks of zeroes
+		byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
+		byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
+		random.nextBytes(ivBytes);
+		for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
+			ivBytes[i] = 0;
+		}
+		IvParameterSpec iv = new IvParameterSpec(ivBytes);
+		Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
+		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+		byte[] ciphertext =
+			new byte[cipher.getOutputSize(plaintext.length)];
+		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
+		// Initialise the least significant 32 bits of the IV to one and
+		// encrypt another ten blocks of zeroes
+		for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
+			assertEquals(0, ivBytes[i]);
+		}
+		ivBytes[BLOCK_SIZE_BYTES - 1] = 1;
+		iv = new IvParameterSpec(ivBytes);
+		cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
+		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+		byte[] ciphertext1 =
+			new byte[cipher.getOutputSize(plaintext.length)];
+		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1, 0);
+		// The last nine blocks of the first ciphertext should be identical to
+		// the first nine blocks of the second ciphertext
+		for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) {
+			assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]);
+		}
+	}
+
+	@Test
+	public void testCounterUsesMoreThan32Bits()
+	throws GeneralSecurityException {
+		// Initialise the least significant bits of the IV to 2^32-1 and
+		// encrypt ten blocks of zeroes
+		byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
+		byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
+		random.nextBytes(ivBytes);
+		ivBytes[BLOCK_SIZE_BYTES - 5] = 0;
+		for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
+			ivBytes[i] = (byte) 255;
+		}
+		IvParameterSpec iv = new IvParameterSpec(ivBytes);
+		Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
+		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+		byte[] ciphertext =
+			new byte[cipher.getOutputSize(plaintext.length)];
+		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
+		// Initialise the least significant bits of the IV to 2^32 and
+		// encrypt another ten blocks of zeroes
+		assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 5]);
+		for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
+			assertEquals((byte) 255, ivBytes[i]);
+		}
+		ivBytes[BLOCK_SIZE_BYTES - 5] = 1;
+		for(int i = BLOCK_SIZE_BYTES - 4; i < BLOCK_SIZE_BYTES; i++) {
+			ivBytes[i] = 0;
+		}
+		iv = new IvParameterSpec(ivBytes);
+		cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
+		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
+		byte[] ciphertext1 =
+			new byte[cipher.getOutputSize(plaintext.length)];
+		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1, 0);
+		// The last nine blocks of the first ciphertext should be identical to
+		// the first nine blocks of the second ciphertext
+		for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) {
+			assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]);
+		}
+	}
+}