diff --git a/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java b/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
index b1b1355a0dd68944036f87368c48127f80514b5b..6ab4d1730247cdc4040f9d963bfaedfa85009805 100644
--- a/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
+++ b/briar-core/src/net/sf/briar/transport/KeyManagerImpl.java
@@ -1,5 +1,6 @@
 package net.sf.briar.transport;
 
+import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
 import static net.sf.briar.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
 
@@ -114,6 +115,7 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 			// Discard the secret if the transport has been removed
 			Long maxLatency = maxLatencies.get(s.getTransportId());
 			if(maxLatency == null) {
+				if(LOG.isLoggable(INFO)) LOG.info("Discarding obsolete secret");
 				ByteUtils.erase(s.getSecret());
 				continue;
 			}
@@ -165,8 +167,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 			TemporarySecret s1 = new TemporarySecret(s, currentPeriod - 1, b1);
 			TemporarySecret s2 = new TemporarySecret(s, currentPeriod, b2);
 			TemporarySecret s3 = new TemporarySecret(s, currentPeriod + 1, b3);
-			// Add the secrets to their respective maps - the old and current
-			// secrets may already exist, in which case erase the duplicates
+			// Add the secrets to their respective maps - copies may already
+			// exist, in which case erase the duplicates
 			EndpointKey k = new EndpointKey(s);
 			TemporarySecret exists = oldSecrets.put(k, s1);
 			if(exists == null) created.add(s1);
@@ -174,8 +176,9 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 			exists = currentSecrets.put(k, s2);
 			if(exists == null) created.add(s2);
 			else ByteUtils.erase(exists.getSecret());
-			newSecrets.put(k, s3);
-			created.add(s3);
+			exists = newSecrets.put(k, s3);
+			if(exists == null) created.add(s3);
+			else ByteUtils.erase(exists.getSecret());
 		}
 		return created;
 	}
@@ -199,10 +202,17 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 	public synchronized ConnectionContext getConnectionContext(ContactId c,
 			TransportId t) {
 		TemporarySecret s = currentSecrets.get(new EndpointKey(c, t));
-		if(s == null) return null;
+		if(s == null) {
+			if(LOG.isLoggable(INFO)) LOG.info("No secret for endpoint");
+			return null;
+		}
 		long connection;
 		try {
 			connection = db.incrementConnectionCounter(c, t, s.getPeriod());
+			if(connection == -1) {
+				if(LOG.isLoggable(INFO)) LOG.info("No counter for period");
+				return null;
+			}
 		} catch(DbException e) {
 			if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 			return null;
@@ -214,7 +224,8 @@ class KeyManagerImpl extends TimerTask implements KeyManager, DatabaseListener {
 	public synchronized void endpointAdded(Endpoint ep, byte[] initialSecret) {
 		Long maxLatency = maxLatencies.get(ep.getTransportId());
 		if(maxLatency == null) {
-			if(LOG.isLoggable(WARNING)) LOG.warning("No such transport");
+			if(LOG.isLoggable(INFO))
+				LOG.info("No such transport, ignoring endpoint");
 			return;
 		}
 		// Work out which rotation period we're in
diff --git a/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java b/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
index 631134b6eaffe263ac5b50a545814bdbbda0e120..1e6bf41c9e36537b01b644cb77555f9c4f8da868 100644
--- a/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
+++ b/briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
@@ -1,11 +1,9 @@
 package net.sf.briar.transport;
 
 import static net.sf.briar.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
-import static org.junit.Assert.assertArrayEquals;
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Random;
 import java.util.TimerTask;
 
 import net.sf.briar.BriarTestCase;
@@ -23,36 +21,32 @@ import net.sf.briar.api.transport.TemporarySecret;
 
 import org.jmock.Expectations;
 import org.jmock.Mockery;
-import org.junit.Before;
 import org.junit.Test;
 
 public class KeyManagerImplTest extends BriarTestCase {
 
-	private final Random random = new Random();
+	private static final long EPOCH = 1000L * 1000L * 1000L * 1000L;
+	private static final long MAX_LATENCY = 2 * 60 * 1000; // 2 minutes
+	private static final long ROTATION_PERIOD_LENGTH =
+			MAX_LATENCY + MAX_CLOCK_DIFFERENCE;
+
 	private final ContactId contactId;
 	private final TransportId transportId;
-	private final long maxLatency;
-	private final long rotationPeriodLength;
-	private final byte[] secret0, secret1, secret2, secret3;
-	private final long epoch = 1000L * 1000L * 1000L * 1000L;
+	private final byte[] secret0, secret1, secret2, secret3, secret4;
 
 	public KeyManagerImplTest() {
 		contactId = new ContactId(234);
 		transportId = new TransportId(TestUtils.getRandomId());
-		maxLatency = 2 * 60 * 1000; // 2 minutes
-		rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
 		secret0 = new byte[32];
 		secret1 = new byte[32];
 		secret2 = new byte[32];
 		secret3 = new byte[32];
-	}
-
-	@Before
-	public void setUp() {
-		random.nextBytes(secret0);
-		random.nextBytes(secret1);
-		random.nextBytes(secret2);
-		random.nextBytes(secret3);
+		secret4 = new byte[32];
+		for(int i = 0; i < secret0.length; i++) secret0[i] = 1;
+		for(int i = 0; i < secret1.length; i++) secret1[i] = 2;
+		for(int i = 0; i < secret2.length; i++) secret2[i] = 3;
+		for(int i = 0; i < secret3.length; i++) secret3[i] = 4;
+		for(int i = 0; i < secret4.length; i++) secret4[i] = 5;
 	}
 
 	@Test
@@ -64,8 +58,10 @@ public class KeyManagerImplTest extends BriarTestCase {
 				context.mock(ConnectionRecogniser.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
+
 		final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db,
 				connectionRecogniser, clock, timer);
+
 		context.checking(new Expectations() {{
 			// start()
 			oneOf(db).addListener(with(any(DatabaseListener.class)));
@@ -74,7 +70,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 			oneOf(db).getTransportLatencies();
 			will(returnValue(Collections.emptyMap()));
 			oneOf(clock).currentTimeMillis();
-			will(returnValue(epoch));
+			will(returnValue(EPOCH));
 			oneOf(timer).scheduleAtFixedRate(with(any(TimerTask.class)),
 					with(any(long.class)), with(any(long.class)));
 			// stop()
@@ -98,13 +94,16 @@ public class KeyManagerImplTest extends BriarTestCase {
 				context.mock(ConnectionRecogniser.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
+
 		final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db,
 				connectionRecogniser, clock, timer);
+
 		// The DB contains secrets for periods 0 - 2
-		Endpoint ep = new Endpoint(contactId, transportId, epoch, true);
-		final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
-		final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
-		final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
+		Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
+		final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone());
+		final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone());
+		final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone());
+
 		context.checking(new Expectations() {{
 			// start()
 			oneOf(db).addListener(with(any(DatabaseListener.class)));
@@ -112,10 +111,10 @@ public class KeyManagerImplTest extends BriarTestCase {
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
 			will(returnValue(Collections.singletonMap(transportId,
-					maxLatency)));
-			// The current time is the second secret's activation time
+					MAX_LATENCY)));
+			// The current time is the epoch, the start of period 1
 			oneOf(clock).currentTimeMillis();
-			will(returnValue(epoch));
+			will(returnValue(EPOCH));
 			// The secrets for periods 0 - 2 should be added to the recogniser
 			oneOf(connectionRecogniser).addSecret(s0);
 			oneOf(connectionRecogniser).addSecret(s1);
@@ -135,7 +134,7 @@ public class KeyManagerImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testLoadSecretsAtNewActivationTime() throws Exception {
+	public void testLoadSecretsAtStartOfPeriod2() throws Exception {
 		Mockery context = new Mockery();
 		final CryptoComponent crypto = context.mock(CryptoComponent.class);
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
@@ -143,15 +142,18 @@ public class KeyManagerImplTest extends BriarTestCase {
 				context.mock(ConnectionRecogniser.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
+
 		final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db,
 				connectionRecogniser, clock, timer);
+
 		// The DB contains secrets for periods 0 - 2
-		Endpoint ep = new Endpoint(contactId, transportId, epoch, true);
-		final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0);
-		final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
-		final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
-		// A fourth secret should be derived and stored
-		final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3);
+		Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
+		final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone());
+		final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone());
+		final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone());
+		// The secret for period 3 should be derived and stored
+		final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone());
+
 		context.checking(new Expectations() {{
 			// start()
 			oneOf(db).addListener(with(any(DatabaseListener.class)));
@@ -159,17 +161,17 @@ public class KeyManagerImplTest extends BriarTestCase {
 			will(returnValue(Arrays.asList(s0, s1, s2)));
 			oneOf(db).getTransportLatencies();
 			will(returnValue(Collections.singletonMap(transportId,
-					maxLatency)));
-			// The current time is the third secret's activation time
+					MAX_LATENCY)));
+			// The current time is the start of period 2
 			oneOf(clock).currentTimeMillis();
-			will(returnValue(epoch + rotationPeriodLength));
-			// A fourth secret should be derived and stored
+			will(returnValue(EPOCH + ROTATION_PERIOD_LENGTH));
+			// The secret for period 3 should be derived and stored
 			oneOf(crypto).deriveNextSecret(secret0, 1);
 			will(returnValue(secret1.clone()));
 			oneOf(crypto).deriveNextSecret(secret1, 2);
 			will(returnValue(secret2.clone()));
 			oneOf(crypto).deriveNextSecret(secret2, 3);
-			will(returnValue(secret3));
+			will(returnValue(secret3.clone()));
 			oneOf(db).addSecrets(Arrays.asList(s3));
 			// The secrets for periods 1 - 3 should be added to the recogniser
 			oneOf(connectionRecogniser).addSecret(s1);
@@ -184,8 +186,75 @@ public class KeyManagerImplTest extends BriarTestCase {
 		}});
 
 		assertTrue(keyManager.start());
-		// The dead secret should have been erased
-		assertArrayEquals(new byte[32], secret0);
+		keyManager.stop();
+
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testLoadSecretsAtStartOfPeriod3() throws Exception {
+		Mockery context = new Mockery();
+		final CryptoComponent crypto = context.mock(CryptoComponent.class);
+		final DatabaseComponent db = context.mock(DatabaseComponent.class);
+		final ConnectionRecogniser connectionRecogniser =
+				context.mock(ConnectionRecogniser.class);
+		final Clock clock = context.mock(Clock.class);
+		final Timer timer = context.mock(Timer.class);
+
+		final KeyManagerImpl keyManager = new KeyManagerImpl(crypto, db,
+				connectionRecogniser, clock, timer);
+
+		// The DB contains secrets for periods 0 - 2
+		Endpoint ep = new Endpoint(contactId, transportId, EPOCH, true);
+		final TemporarySecret s0 = new TemporarySecret(ep, 0, secret0.clone());
+		final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1.clone());
+		final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2.clone());
+		// The secrets for periods 3 and 4 should be derived and stored
+		final TemporarySecret s3 = new TemporarySecret(ep, 3, secret3.clone());
+		final TemporarySecret s4 = new TemporarySecret(ep, 4, secret4.clone());
+
+		context.checking(new Expectations() {{
+			// start()
+			oneOf(db).addListener(with(any(DatabaseListener.class)));
+			oneOf(db).getSecrets();
+			will(returnValue(Arrays.asList(s0, s1, s2)));
+			oneOf(db).getTransportLatencies();
+			will(returnValue(Collections.singletonMap(transportId,
+					MAX_LATENCY)));
+			// The current time is the start of period 3
+			oneOf(clock).currentTimeMillis();
+			will(returnValue(EPOCH + 2 * ROTATION_PERIOD_LENGTH));
+			// The secrets for periods 3 and 4 should be derived from secret 0
+			oneOf(crypto).deriveNextSecret(secret0, 1);
+			will(returnValue(secret1.clone()));
+			oneOf(crypto).deriveNextSecret(secret1, 2);
+			will(returnValue(secret2.clone()));
+			oneOf(crypto).deriveNextSecret(secret2, 3);
+			will(returnValue(secret3.clone()));
+			oneOf(crypto).deriveNextSecret(secret3, 4);
+			will(returnValue(secret4.clone()));
+			// The secrets for periods 3 and 4 should be derived from secret 1
+			oneOf(crypto).deriveNextSecret(secret1, 2);
+			will(returnValue(secret2.clone()));
+			oneOf(crypto).deriveNextSecret(secret2, 3);
+			will(returnValue(secret3.clone()));
+			oneOf(crypto).deriveNextSecret(secret3, 4);
+			will(returnValue(secret4.clone()));
+			// One copy of each of the new secrets should be stored
+			oneOf(db).addSecrets(Arrays.asList(s3, s4));
+			// The secrets for periods 2 - 3 should be added to the recogniser
+			oneOf(connectionRecogniser).addSecret(s2);
+			oneOf(connectionRecogniser).addSecret(s3);
+			oneOf(connectionRecogniser).addSecret(s4);
+			oneOf(timer).scheduleAtFixedRate(with(any(TimerTask.class)),
+					with(any(long.class)), with(any(long.class)));
+			// stop()
+			oneOf(db).removeListener(with(any(DatabaseListener.class)));
+			oneOf(timer).cancel();
+			oneOf(connectionRecogniser).removeSecrets();
+		}});
+
+		assertTrue(keyManager.start());
 		keyManager.stop();
 
 		context.assertIsSatisfied();