mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 11:49:04 +01:00
Simpler key rotation: rotation period R = C + L, retention period = 3R.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package net.sf.briar;
|
||||
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -1511,12 +1511,13 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testTemporarySecrets() throws Exception {
|
||||
// Create an endpoint and three consecutive temporary secrets
|
||||
// Create an endpoint and four consecutive temporary secrets
|
||||
long epoch = 123, latency = 234;
|
||||
boolean alice = false;
|
||||
long outgoing1 = 345, centre1 = 456;
|
||||
long outgoing2 = 567, centre2 = 678;
|
||||
long outgoing3 = 789, centre3 = 890;
|
||||
long outgoing4 = 901, centre4 = 123;
|
||||
Endpoint ep = new Endpoint(contactId, transportId, epoch, alice);
|
||||
Random random = new Random();
|
||||
byte[] secret1 = new byte[32], bitmap1 = new byte[4];
|
||||
@@ -1534,6 +1535,11 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
random.nextBytes(bitmap3);
|
||||
TemporarySecret s3 = new TemporarySecret(contactId, transportId, epoch,
|
||||
alice, 2, secret3, outgoing3, centre3, bitmap3);
|
||||
byte[] secret4 = new byte[32], bitmap4 = new byte[4];
|
||||
random.nextBytes(secret4);
|
||||
random.nextBytes(bitmap4);
|
||||
TemporarySecret s4 = new TemporarySecret(contactId, transportId, epoch,
|
||||
alice, 3, secret4, outgoing4, centre4, bitmap4);
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
@@ -1541,18 +1547,18 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
// Initially there should be no secrets in the database
|
||||
assertEquals(Collections.emptyList(), db.getSecrets(txn));
|
||||
|
||||
// Add the contact, the transport, the endpoint and the first two
|
||||
// secrets (periods 0 and 1)
|
||||
// Add the contact, the transport, the endpoint and the first three
|
||||
// secrets (periods 0, 1 and 2)
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||
db.addTransport(txn, transportId, latency);
|
||||
db.addEndpoint(txn, ep);
|
||||
db.addSecrets(txn, Arrays.asList(s1, s2));
|
||||
db.addSecrets(txn, Arrays.asList(s1, s2, s3));
|
||||
|
||||
// Retrieve the first two secrets
|
||||
// Retrieve the first three secrets
|
||||
Collection<TemporarySecret> secrets = db.getSecrets(txn);
|
||||
assertEquals(2, secrets.size());
|
||||
boolean foundFirst = false, foundSecond = false;
|
||||
assertEquals(3, secrets.size());
|
||||
boolean foundFirst = false, foundSecond = false, foundThird = false;
|
||||
for(TemporarySecret s : secrets) {
|
||||
assertEquals(contactId, s.getContactId());
|
||||
assertEquals(transportId, s.getTransportId());
|
||||
@@ -1570,19 +1576,26 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertEquals(centre2, s.getWindowCentre());
|
||||
assertArrayEquals(bitmap2, s.getWindowBitmap());
|
||||
foundSecond = true;
|
||||
} else if(s.getPeriod() == 2) {
|
||||
assertArrayEquals(secret3, s.getSecret());
|
||||
assertEquals(outgoing3, s.getOutgoingConnectionCounter());
|
||||
assertEquals(centre3, s.getWindowCentre());
|
||||
assertArrayEquals(bitmap3, s.getWindowBitmap());
|
||||
foundThird = true;
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
assertTrue(foundFirst);
|
||||
assertTrue(foundSecond);
|
||||
assertTrue(foundThird);
|
||||
|
||||
// Adding the third secret (period 2) should delete the first (period 0)
|
||||
db.addSecrets(txn, Arrays.asList(s3));
|
||||
// Adding the fourth secret (period 3) should delete the first
|
||||
db.addSecrets(txn, Arrays.asList(s4));
|
||||
secrets = db.getSecrets(txn);
|
||||
assertEquals(2, secrets.size());
|
||||
foundSecond = false;
|
||||
boolean foundThird = false;
|
||||
assertEquals(3, secrets.size());
|
||||
foundSecond = foundThird = false;
|
||||
boolean foundFourth = false;
|
||||
for(TemporarySecret s : secrets) {
|
||||
assertEquals(contactId, s.getContactId());
|
||||
assertEquals(transportId, s.getTransportId());
|
||||
@@ -1600,12 +1613,19 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
assertEquals(centre3, s.getWindowCentre());
|
||||
assertArrayEquals(bitmap3, s.getWindowBitmap());
|
||||
foundThird = true;
|
||||
} else if(s.getPeriod() == 3) {
|
||||
assertArrayEquals(secret4, s.getSecret());
|
||||
assertEquals(outgoing4, s.getOutgoingConnectionCounter());
|
||||
assertEquals(centre4, s.getWindowCentre());
|
||||
assertArrayEquals(bitmap4, s.getWindowBitmap());
|
||||
foundFourth = true;
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
assertTrue(foundSecond);
|
||||
assertTrue(foundThird);
|
||||
assertTrue(foundFourth);
|
||||
|
||||
// Removing the contact should remove the secrets
|
||||
db.removeContact(txn, contactId);
|
||||
|
||||
193
briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
Normal file
193
briar-tests/src/net/sf/briar/transport/KeyManagerImplTest.java
Normal file
@@ -0,0 +1,193 @@
|
||||
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;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportId;
|
||||
import net.sf.briar.api.clock.Clock;
|
||||
import net.sf.briar.api.clock.Timer;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.transport.ConnectionRecogniser;
|
||||
import net.sf.briar.api.transport.Endpoint;
|
||||
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 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAndStop() 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);
|
||||
context.checking(new Expectations() {{
|
||||
// start()
|
||||
oneOf(db).addListener(with(any(DatabaseListener.class)));
|
||||
oneOf(db).getSecrets();
|
||||
will(returnValue(Collections.emptyList()));
|
||||
oneOf(db).getTransportLatencies();
|
||||
will(returnValue(Collections.emptyMap()));
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(epoch));
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadSecretsAtEpoch() 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);
|
||||
final TemporarySecret s1 = new TemporarySecret(ep, 1, secret1);
|
||||
final TemporarySecret s2 = new TemporarySecret(ep, 2, secret2);
|
||||
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,
|
||||
maxLatency)));
|
||||
// The current time is the second secret's activation time
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(epoch));
|
||||
// The secrets for periods 0 - 2 should be added to the recogniser
|
||||
oneOf(connectionRecogniser).addSecret(s0);
|
||||
oneOf(connectionRecogniser).addSecret(s1);
|
||||
oneOf(connectionRecogniser).addSecret(s2);
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadSecretsAtNewActivationTime() 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);
|
||||
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);
|
||||
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,
|
||||
maxLatency)));
|
||||
// The current time is the third secret's activation time
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(epoch + rotationPeriodLength));
|
||||
// A fourth secret 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));
|
||||
oneOf(db).addSecrets(Arrays.asList(s3));
|
||||
// The secrets for periods 1 - 3 should be added to the recogniser
|
||||
oneOf(connectionRecogniser).addSecret(s1);
|
||||
oneOf(connectionRecogniser).addSecret(s2);
|
||||
oneOf(connectionRecogniser).addSecret(s3);
|
||||
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());
|
||||
// The dead secret should have been erased
|
||||
assertArrayEquals(new byte[32], secret0);
|
||||
keyManager.stop();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user