Skip to content
Snippets Groups Projects
Commit fa295da4 authored by akwizgran's avatar akwizgran
Browse files

Merged prototype-test repo into prototype repo, as a separate Eclipse project.

parent f4f7b96d
No related branches found
No related tags found
No related merge requests found
Showing
with 1261 additions and 0 deletions
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry kind="lib" path="libs/hamcrest-core-1.1.jar"/>
<classpathentry kind="lib" path="libs/hamcrest-library-1.1.jar"/>
<classpathentry kind="lib" path="libs/jmock-2.5.1.jar"/>
<classpathentry kind="lib" path="libs/junit-4.9b3.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/briar-core"/>
<classpathentry kind="lib" path="/briar-core/android.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
bin
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>briar-tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
File added
File added
File added
File added
build
test.tmp
<project name='test' default='test'>
<fileset id='core-jars' dir='../../briar-core/libs'>
<include name='*.jar'/>
</fileset>
<fileset id='test-jars' dir='../libs'>
<include name='*.jar'/>
</fileset>
<path id='android-jar'>
<pathelement location='../../briar-core/android.jar'/>
</path>
<path id='core-classes'>
<pathelement location='../../briar-core/build'/>
</path>
<path id='test-classes'>
<pathelement location='../build'/>
</path>
<target name='clean'>
<delete dir='../../briar-core/build'/>
<delete dir='../build'/>
<delete dir='test.tmp'/>
</target>
<target name='compile'>
<mkdir dir='../../briar-core/build'/>
<javac srcdir='../../briar-core/src'
destdir='../../briar-core/build' source='1.5'
includeantruntime='false' debug='off'>
<classpath>
<fileset refid='core-jars'/>
<path refid='android-jar'/>
<path refid='core-classes'/>
</classpath>
</javac>
<mkdir dir='../build'/>
<javac srcdir='.' destdir='../build' source='1.5'
includeantruntime='false' debug='off'>
<classpath>
<fileset refid='core-jars'/>
<fileset refid='test-jars'/>
<path refid='android-jar'/>
<path refid='core-classes'/>
<path refid='test-classes'/>
</classpath>
</javac>
</target>
<target name='test' depends='compile'>
<junit printsummary='on' fork='yes' forkmode='once'>
<assertions>
<enable/>
</assertions>
<classpath>
<fileset refid='core-jars'/>
<fileset refid='test-jars'/>
<path refid='core-classes'/>
<path refid='test-classes'/>
</classpath>
<jvmarg value='-Djava.library.path=../../briar-core/libs'/>
<test name='net.sf.briar.LockFairnessTest'/>
<test name='net.sf.briar.ProtocolIntegrationTest'/>
<test name='net.sf.briar.crypto.CounterModeTest'/>
<test name='net.sf.briar.crypto.ErasableKeyTest'/>
<test name='net.sf.briar.crypto.KeyAgreementTest'/>
<test name='net.sf.briar.crypto.KeyDerivationTest'/>
<test name='net.sf.briar.db.BasicH2Test'/>
<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
<test name='net.sf.briar.db.DatabaseComponentImplTest'/>
<test name='net.sf.briar.lifecycle.ShutdownManagerImplTest'/>
<test name='net.sf.briar.lifecycle.WindowsShutdownManagerImplTest'/>
<test name='net.sf.briar.plugins.PluginManagerImplTest'/>
<test name='net.sf.briar.plugins.file.LinuxRemovableDriveFinderTest'/>
<test name='net.sf.briar.plugins.file.MacRemovableDriveFinderTest'/>
<test name='net.sf.briar.plugins.file.PollingRemovableDriveMonitorTest'/>
<test name='net.sf.briar.plugins.file.RemovableDrivePluginTest'/>
<test name='net.sf.briar.plugins.file.UnixRemovableDriveMonitorTest'/>
<test name='net.sf.briar.plugins.tcp.LanTcpPluginTest'/>
<test name='net.sf.briar.protocol.AckReaderTest'/>
<test name='net.sf.briar.protocol.BatchReaderTest'/>
<test name='net.sf.briar.protocol.ConstantsTest'/>
<test name='net.sf.briar.protocol.ConsumersTest'/>
<test name='net.sf.briar.protocol.OfferReaderTest'/>
<test name='net.sf.briar.protocol.ProtocolIntegrationTest'/>
<test name='net.sf.briar.protocol.ProtocolWriterImplTest'/>
<test name='net.sf.briar.protocol.RequestReaderTest'/>
<test name='net.sf.briar.protocol.UnverifiedBatchImplTest'/>
<test name='net.sf.briar.protocol.simplex.OutgoingSimplexConnectionTest'/>
<test name='net.sf.briar.protocol.simplex.SimplexProtocolIntegrationTest'/>
<test name='net.sf.briar.serial.ReaderImplTest'/>
<test name='net.sf.briar.serial.WriterImplTest'/>
<test name='net.sf.briar.transport.ConnectionReaderImplTest'/>
<test name='net.sf.briar.transport.ConnectionRegistryImplTest'/>
<test name='net.sf.briar.transport.ConnectionWindowTest'/>
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
<test name='net.sf.briar.transport.IncomingEncryptionLayerTest'/>
<test name='net.sf.briar.transport.OutgoingEncryptionLayerTest'/>
<test name='net.sf.briar.transport.TransportIntegrationTest'/>
<test name='net.sf.briar.transport.TransportConnectionRecogniserTest'/>
<test name='net.sf.briar.util.ByteUtilsTest'/>
<test name='net.sf.briar.util.FileUtilsTest'/>
<test name='net.sf.briar.util.StringUtilsTest'/>
<test name='net.sf.briar.util.ZipUtilsTest'/>
</junit>
</target>
<target name='test-slow' depends='compile'>
<junit printsummary='withOutAndErr' fork='yes' forkmode='once'>
<assertions>
<enable/>
</assertions>
<classpath>
<fileset refid='core-jars'/>
<fileset refid='test-jars'/>
<path refid='core-classes'/>
<path refid='test-classes'/>
</classpath>
<jvmarg value='-Djava.library.path=../../briar-core/libs'/>
<test name='net.sf.briar.db.H2DatabaseTest'/>
<test name='net.sf.briar.plugins.tor.TorPluginTest'/>
</junit>
</target>
</project>
package net.sf.briar;
import java.lang.Thread.UncaughtExceptionHandler;
import junit.framework.TestCase;
public abstract class BriarTestCase extends TestCase {
public BriarTestCase() {
super();
// Ensure exceptions thrown on worker threads cause tests to fail
UncaughtExceptionHandler fail = new UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable throwable) {
fail();
}
};
Thread.setDefaultUncaughtExceptionHandler(fail);
}
}
package net.sf.briar;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.junit.Test;
public class LockFairnessTest extends BriarTestCase {
@Test
public void testReadersCanShareTheLock() throws Exception {
// Use a fair lock
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
final CountDownLatch firstReaderHasLock = new CountDownLatch(1);
final CountDownLatch firstReaderHasFinished = new CountDownLatch(1);
final CountDownLatch secondReaderHasLock = new CountDownLatch(1);
final CountDownLatch secondReaderHasFinished = new CountDownLatch(1);
// First reader
Thread first = new Thread() {
@Override
public void run() {
try {
// Acquire the lock
lock.readLock().lock();
try {
// Allow the second reader to acquire the lock
firstReaderHasLock.countDown();
// Wait for the second reader to acquire the lock
assertTrue(secondReaderHasLock.await(10, SECONDS));
} finally {
// Release the lock
lock.readLock().unlock();
}
} catch(InterruptedException e) {
fail();
}
firstReaderHasFinished.countDown();
}
};
first.start();
// Second reader
Thread second = new Thread() {
@Override
public void run() {
try {
// Wait for the first reader to acquire the lock
assertTrue(firstReaderHasLock.await(10, SECONDS));
// Acquire the lock
lock.readLock().lock();
try {
// Allow the first reader to release the lock
secondReaderHasLock.countDown();
} finally {
// Release the lock
lock.readLock().unlock();
}
} catch(InterruptedException e) {
fail();
}
secondReaderHasFinished.countDown();
}
};
second.start();
// Wait for both readers to finish
assertTrue(firstReaderHasFinished.await(10, SECONDS));
assertTrue(secondReaderHasFinished.await(10, SECONDS));
}
@Test
public void testWritersDoNotStarve() throws Exception {
// Use a fair lock
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
final CountDownLatch firstReaderHasLock = new CountDownLatch(1);
final CountDownLatch firstReaderHasFinished = new CountDownLatch(1);
final CountDownLatch secondReaderHasFinished = new CountDownLatch(1);
final CountDownLatch writerHasFinished = new CountDownLatch(1);
final AtomicBoolean secondReaderHasHeldLock = new AtomicBoolean(false);
final AtomicBoolean writerHasHeldLock = new AtomicBoolean(false);
// First reader
Thread first = new Thread() {
@Override
public void run() {
try {
// Acquire the lock
lock.readLock().lock();
try {
// Allow the other threads to acquire the lock
firstReaderHasLock.countDown();
// Wait for both other threads to wait for the lock
while(lock.getQueueLength() < 2) Thread.sleep(10);
// No other thread should have acquired the lock
assertFalse(secondReaderHasHeldLock.get());
assertFalse(writerHasHeldLock.get());
} finally {
// Release the lock
lock.readLock().unlock();
}
} catch(InterruptedException e) {
fail();
}
firstReaderHasFinished.countDown();
}
};
first.start();
// Writer
Thread writer = new Thread() {
@Override
public void run() {
try {
// Wait for the first reader to acquire the lock
assertTrue(firstReaderHasLock.await(10, SECONDS));
// Acquire the lock
lock.writeLock().lock();
try {
writerHasHeldLock.set(true);
// The second reader should not overtake the writer
assertFalse(secondReaderHasHeldLock.get());
} finally {
lock.writeLock().unlock();
}
} catch(InterruptedException e) {
fail();
}
writerHasFinished.countDown();
}
};
writer.start();
// Second reader
Thread second = new Thread() {
@Override
public void run() {
try {
// Wait for the first reader to acquire the lock
assertTrue(firstReaderHasLock.await(10, SECONDS));
// Wait for the writer to wait for the lock
while(lock.getQueueLength() < 1) Thread.sleep(10);
// Acquire the lock
lock.readLock().lock();
try {
secondReaderHasHeldLock.set(true);
// The second reader should not overtake the writer
assertTrue(writerHasHeldLock.get());
} finally {
lock.readLock().unlock();
}
} catch(InterruptedException e) {
fail();
}
secondReaderHasFinished.countDown();
}
};
second.start();
// Wait for all the threads to finish
assertTrue(firstReaderHasFinished.await(10, SECONDS));
assertTrue(secondReaderHasFinished.await(10, SECONDS));
assertTrue(writerHasFinished.await(10, SECONDS));
}
}
package net.sf.briar;
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyPair;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageFactory;
import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.PacketFactory;
import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.ProtocolWriter;
import net.sf.briar.api.protocol.ProtocolWriterFactory;
import net.sf.briar.api.protocol.RawBatch;
import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.api.transport.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory;
import net.sf.briar.clock.ClockModule;
import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.db.DatabaseModule;
import net.sf.briar.lifecycle.LifecycleModule;
import net.sf.briar.protocol.ProtocolModule;
import net.sf.briar.protocol.duplex.DuplexProtocolModule;
import net.sf.briar.protocol.simplex.SimplexProtocolModule;
import net.sf.briar.serial.SerialModule;
import net.sf.briar.transport.TransportModule;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class ProtocolIntegrationTest extends BriarTestCase {
private final BatchId ack = new BatchId(TestUtils.getRandomId());
private final long timestamp = System.currentTimeMillis();
private final ConnectionReaderFactory connectionReaderFactory;
private final ConnectionWriterFactory connectionWriterFactory;
private final ProtocolReaderFactory protocolReaderFactory;
private final ProtocolWriterFactory protocolWriterFactory;
private final PacketFactory packetFactory;
private final CryptoComponent crypto;
private final ContactId contactId;
private final TransportId transportId;
private final byte[] secret;
private final Author author;
private final Group group, group1;
private final Message message, message1, message2, message3;
private final String authorName = "Alice";
private final String subject = "Hello";
private final String messageBody = "Hello world";
private final Collection<Transport> transports;
public ProtocolIntegrationTest() throws Exception {
super();
Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(),
new DatabaseModule(), new LifecycleModule(),
new ProtocolModule(), new SerialModule(),
new TestDatabaseModule(), new SimplexProtocolModule(),
new TransportModule(), new DuplexProtocolModule());
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
packetFactory = i.getInstance(PacketFactory.class);
crypto = i.getInstance(CryptoComponent.class);
contactId = new ContactId(234);
transportId = new TransportId(TestUtils.getRandomId());
// Create a shared secret
Random r = new Random();
secret = new byte[32];
r.nextBytes(secret);
// Create two groups: one restricted, one unrestricted
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
group = groupFactory.createGroup("Unrestricted group", null);
KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
group1 = groupFactory.createGroup("Restricted group",
groupKeyPair.getPublic().getEncoded());
// Create an author
AuthorFactory authorFactory = i.getInstance(AuthorFactory.class);
KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
author = authorFactory.createAuthor(authorName,
authorKeyPair.getPublic().getEncoded());
// Create two messages to each group: one anonymous, one pseudonymous
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
message = messageFactory.createMessage(null, group, subject,
messageBody.getBytes("UTF-8"));
message1 = messageFactory.createMessage(null, group1,
groupKeyPair.getPrivate(), subject,
messageBody.getBytes("UTF-8"));
message2 = messageFactory.createMessage(null, group, author,
authorKeyPair.getPrivate(), subject,
messageBody.getBytes("UTF-8"));
message3 = messageFactory.createMessage(null, group1,
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
subject, messageBody.getBytes("UTF-8"));
// Create some transports
TransportId transportId = new TransportId(TestUtils.getRandomId());
Transport transport = new Transport(transportId,
Collections.singletonMap("bar", "baz"));
transports = Collections.singletonList(transport);
}
@Test
public void testWriteAndRead() throws Exception {
read(write());
}
private byte[] write() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
secret.clone(), 0L, true);
ConnectionWriter conn = connectionWriterFactory.createConnectionWriter(
out, Long.MAX_VALUE, ctx, false, true);
OutputStream out1 = conn.getOutputStream();
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1,
false);
Ack a = packetFactory.createAck(Collections.singletonList(ack));
writer.writeAck(a);
Collection<byte[]> batch = Arrays.asList(message.getSerialised(),
message1.getSerialised(), message2.getSerialised(),
message3.getSerialised());
RawBatch b = packetFactory.createBatch(batch);
writer.writeBatch(b);
Collection<MessageId> offer = Arrays.asList(message.getId(),
message1.getId(), message2.getId(), message3.getId());
Offer o = packetFactory.createOffer(offer);
writer.writeOffer(o);
BitSet requested = new BitSet(4);
requested.set(1);
requested.set(3);
Request r = packetFactory.createRequest(requested, 4);
writer.writeRequest(r);
// Use a LinkedHashMap for predictable iteration order
Map<Group, Long> subs = new LinkedHashMap<Group, Long>();
subs.put(group, 0L);
subs.put(group1, 0L);
SubscriptionUpdate s = packetFactory.createSubscriptionUpdate(
Collections.<GroupId, GroupId>emptyMap(), subs, 0L, timestamp);
writer.writeSubscriptionUpdate(s);
TransportUpdate t = packetFactory.createTransportUpdate(transports,
timestamp);
writer.writeTransportUpdate(t);
writer.flush();
return out.toByteArray();
}
private void read(byte[] connectionData) throws Exception {
InputStream in = new ByteArrayInputStream(connectionData);
byte[] tag = new byte[TAG_LENGTH];
assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
// FIXME: Check that the expected tag was received
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
secret.clone(), 0L, false);
ConnectionReader conn = connectionReaderFactory.createConnectionReader(
in, ctx, true, true);
InputStream in1 = conn.getInputStream();
ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1);
// Read the ack
assertTrue(reader.hasAck());
Ack a = reader.readAck();
assertEquals(Collections.singletonList(ack), a.getBatchIds());
// Read and verify the batch
assertTrue(reader.hasBatch());
Batch b = reader.readBatch().verify();
Collection<Message> messages = b.getMessages();
assertEquals(4, messages.size());
Iterator<Message> it = messages.iterator();
checkMessageEquality(message, it.next());
checkMessageEquality(message1, it.next());
checkMessageEquality(message2, it.next());
checkMessageEquality(message3, it.next());
// Read the offer
assertTrue(reader.hasOffer());
Offer o = reader.readOffer();
Collection<MessageId> offered = o.getMessageIds();
assertEquals(4, offered.size());
Iterator<MessageId> it1 = offered.iterator();
assertEquals(message.getId(), it1.next());
assertEquals(message1.getId(), it1.next());
assertEquals(message2.getId(), it1.next());
assertEquals(message3.getId(), it1.next());
// Read the request
assertTrue(reader.hasRequest());
Request req = reader.readRequest();
BitSet requested = req.getBitmap();
assertFalse(requested.get(0));
assertTrue(requested.get(1));
assertFalse(requested.get(2));
assertTrue(requested.get(3));
// If there are any padding bits, they should all be zero
assertEquals(2, requested.cardinality());
// Read the subscription update
assertTrue(reader.hasSubscriptionUpdate());
SubscriptionUpdate s = reader.readSubscriptionUpdate();
Map<Group, Long> subs = s.getSubscriptions();
assertEquals(2, subs.size());
assertEquals(Long.valueOf(0L), subs.get(group));
assertEquals(Long.valueOf(0L), subs.get(group1));
assertTrue(s.getTimestamp() == timestamp);
// Read the transport update
assertTrue(reader.hasTransportUpdate());
TransportUpdate t = reader.readTransportUpdate();
assertEquals(transports, t.getTransports());
assertTrue(t.getTimestamp() == timestamp);
in.close();
}
private void checkMessageEquality(Message m1, Message m2) {
assertEquals(m1.getId(), m2.getId());
assertEquals(m1.getParent(), m2.getParent());
assertEquals(m1.getGroup(), m2.getGroup());
assertEquals(m1.getAuthor(), m2.getAuthor());
assertEquals(m1.getTimestamp(), m2.getTimestamp());
assertArrayEquals(m1.getSerialised(), m2.getSerialised());
}
}
package net.sf.briar;
import java.io.File;
import net.sf.briar.api.crypto.Password;
import net.sf.briar.api.db.DatabaseConfig;
public class TestDatabaseConfig implements DatabaseConfig {
private final File dir;
private final long maxSize;
public TestDatabaseConfig(File dir, long maxSize) {
this.dir = dir;
this.maxSize = maxSize;
}
public File getDataDirectory() {
return dir;
}
public Password getPassword() {
return new Password() {
public char[] getPassword() {
return "foo bar".toCharArray();
}
};
}
public long getMaxSize() {
return maxSize;
}
}
package net.sf.briar;
import java.io.File;
import net.sf.briar.api.db.DatabaseConfig;
import com.google.inject.AbstractModule;
public class TestDatabaseModule extends AbstractModule {
private final DatabaseConfig config;
public TestDatabaseModule() {
this(new File("."), Long.MAX_VALUE);
}
public TestDatabaseModule(File dir) {
this(dir, Long.MAX_VALUE);
}
public TestDatabaseModule(File dir, long maxSize) {
this.config = new TestDatabaseConfig(dir, maxSize);
}
@Override
protected void configure() {
bind(DatabaseConfig.class).toInstance(config);
}
}
package net.sf.briar;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase;
import net.sf.briar.api.protocol.UniqueId;
public class TestUtils {
private static final AtomicInteger nextTestDir =
new AtomicInteger((int) (Math.random() * 1000 * 1000));
private static final Random random = new Random();
public static void delete(File f) {
if(f.isDirectory()) for(File child : f.listFiles()) delete(child);
f.delete();
}
public static void createFile(File f, String s) throws IOException {
f.getParentFile().mkdirs();
PrintStream out = new PrintStream(new FileOutputStream(f));
out.print(s);
out.flush();
out.close();
}
public static File getTestDirectory() {
int name = nextTestDir.getAndIncrement();
File testDir = new File("test.tmp/" + name);
return testDir;
}
public static void deleteTestDirectory(File testDir) {
delete(testDir);
testDir.getParentFile().delete(); // Delete if empty
}
public static File getBuildDirectory() {
File build = new File("build"); // Ant
if(build.exists() && build.isDirectory()) return build;
File bin = new File("bin"); // Eclipse
if(bin.exists() && bin.isDirectory()) return bin;
throw new RuntimeException("Could not find build directory");
}
public static File getFontDirectory() {
File f = new File("i18n");
if(f.exists() && f.isDirectory()) return f;
f = new File("../i18n");
if(f.exists() && f.isDirectory()) return f;
throw new RuntimeException("Could not find font directory");
}
public static byte[] getRandomId() {
byte[] b = new byte[UniqueId.LENGTH];
random.nextBytes(b);
return b;
}
public static void readFully(InputStream in, byte[] b) throws IOException {
int offset = 0;
while(offset < b.length) {
int read = in.read(b, offset, b.length - offset);
if(read == -1) break;
offset += read;
}
TestCase.assertEquals(b.length, offset);
}
}
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 net.sf.briar.BriarTestCase;
import net.sf.briar.api.Bytes;
import org.junit.Test;
import org.spongycastle.jce.provider.BouncyCastleProvider;
public class CounterModeTest extends BriarTestCase {
private static final String CIPHER_ALGO = "AES";
private static final String CIPHER_MODE = "AES/CTR/NoPadding";
private static final String PROVIDER = "SC";
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);
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);
ciphertexts.add(new Bytes(ciphertext));
}
assertEquals(1, ciphertexts.size());
}
@Test
public void testLeastSignificantBitsUsedAsCounter()
throws GeneralSecurityException {
// Initialise the least significant 16 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);
ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
ivBytes[BLOCK_SIZE_BYTES - 1] = 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);
// Make sure the IV array hasn't been modified
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 2]);
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 1]);
// Initialise the least significant 16 bits of the IV to one and
// encrypt another ten blocks of zeroes
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);
// 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 testCounterUsesMoreThan16Bits()
throws GeneralSecurityException {
// Initialise the least significant bits of the IV to 2^16-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 - 3] = 0;
ivBytes[BLOCK_SIZE_BYTES - 2] = (byte) 255;
ivBytes[BLOCK_SIZE_BYTES - 1] = (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);
// Make sure the IV array hasn't been modified
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 3]);
assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 2]);
assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 1]);
// Initialise the least significant bits of the IV to 2^16 and
// encrypt another ten blocks of zeroes
ivBytes[BLOCK_SIZE_BYTES - 3] = 1;
ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
ivBytes[BLOCK_SIZE_BYTES - 1] = 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);
// 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]);
}
}
}
package net.sf.briar.crypto;
import static org.junit.Assert.assertArrayEquals;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.ErasableKey;
import org.junit.Test;
public class ErasableKeyTest extends BriarTestCase {
private static final String CIPHER = "AES";
private static final String CIPHER_MODE = "AES/CTR/NoPadding";
private static final int IV_BYTES = 16; // 128 bits
private static final int KEY_BYTES = 32; // 256 bits
private static final String MAC = "HMacSHA384";
private final Random random = new Random();
@Test
public void testCopiesAreErased() {
byte[] master = new byte[KEY_BYTES];
random.nextBytes(master);
ErasableKey k = new ErasableKeyImpl(master, CIPHER);
byte[] copy = k.getEncoded();
assertArrayEquals(master, copy);
k.erase();
byte[] blank = new byte[KEY_BYTES];
assertArrayEquals(blank, master);
assertArrayEquals(blank, copy);
}
@Test
public void testErasureDoesNotAffectCipher() throws Exception {
byte[] key = new byte[KEY_BYTES];
random.nextBytes(key);
ErasableKey k = new ErasableKeyImpl(key, CIPHER);
Cipher c = Cipher.getInstance(CIPHER_MODE);
IvParameterSpec iv = new IvParameterSpec(new byte[IV_BYTES]);
c.init(Cipher.ENCRYPT_MODE, k, iv);
// Encrypt a blank plaintext
byte[] plaintext = new byte[123];
byte[] ciphertext = c.doFinal(plaintext);
// Erase the key and encrypt again - erase() was called after doFinal()
k.erase();
byte[] ciphertext1 = c.doFinal(plaintext);
// Encrypt again - this time erase() was called before doFinal()
byte[] ciphertext2 = c.doFinal(plaintext);
// The ciphertexts should match
assertArrayEquals(ciphertext, ciphertext1);
assertArrayEquals(ciphertext, ciphertext2);
}
@Test
public void testErasureDoesNotAffectMac() throws Exception {
byte[] key = new byte[KEY_BYTES];
random.nextBytes(key);
ErasableKey k = new ErasableKeyImpl(key, CIPHER);
Mac m = Mac.getInstance(MAC);
m.init(k);
// Authenticate a blank plaintext
byte[] plaintext = new byte[123];
byte[] mac = m.doFinal(plaintext);
// Erase the key and authenticate again
k.erase();
byte[] mac1 = m.doFinal(plaintext);
// Authenticate again
byte[] mac2 = m.doFinal(plaintext);
// The MACs should match
assertArrayEquals(mac, mac1);
assertArrayEquals(mac, mac2);
}
}
package net.sf.briar.crypto;
import static org.junit.Assert.assertArrayEquals;
import java.security.KeyPair;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
import org.junit.Test;
public class KeyAgreementTest extends BriarTestCase {
@Test
public void testKeyAgreement() throws Exception {
CryptoComponent crypto = new CryptoComponentImpl();
KeyPair a = crypto.generateAgreementKeyPair();
byte[] aPub = a.getPublic().getEncoded();
KeyPair b = crypto.generateAgreementKeyPair();
byte[] bPub = b.getPublic().getEncoded();
byte[] aSecret = crypto.deriveInitialSecret(aPub, b, true);
byte[] bSecret = crypto.deriveInitialSecret(bPub, a, false);
assertArrayEquals(aSecret, bSecret);
}
}
package net.sf.briar.crypto;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import net.sf.briar.BriarTestCase;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.ErasableKey;
import org.junit.Test;
public class KeyDerivationTest extends BriarTestCase {
private final CryptoComponent crypto;
private final byte[] secret;
public KeyDerivationTest() {
super();
crypto = new CryptoComponentImpl();
secret = new byte[32];
new Random().nextBytes(secret);
}
@Test
public void testKeysAreDistinct() {
List<ErasableKey> keys = new ArrayList<ErasableKey>();
keys.add(crypto.deriveFrameKey(secret, 0, false, false));
keys.add(crypto.deriveFrameKey(secret, 0, false, true));
keys.add(crypto.deriveFrameKey(secret, 0, true, false));
keys.add(crypto.deriveFrameKey(secret, 0, true, true));
keys.add(crypto.deriveTagKey(secret, true));
keys.add(crypto.deriveTagKey(secret, false));
for(int i = 0; i < 4; i++) {
byte[] keyI = keys.get(i).getEncoded();
for(int j = 0; j < 4; j++) {
byte[] keyJ = keys.get(j).getEncoded();
assertEquals(i == j, Arrays.equals(keyI, keyJ));
}
}
}
@Test
public void testSecretAffectsDerivation() {
Random r = new Random();
List<byte[]> secrets = new ArrayList<byte[]>();
for(int i = 0; i < 20; i++) {
byte[] b = new byte[32];
r.nextBytes(b);
secrets.add(crypto.deriveNextSecret(b, 0));
}
for(int i = 0; i < 20; i++) {
byte[] secretI = secrets.get(i);
for(int j = 0; j < 20; j++) {
byte[] secretJ = secrets.get(j);
assertEquals(i == j, Arrays.equals(secretI, secretJ));
}
}
}
@Test
public void testConnectionNumberAffectsDerivation() {
List<byte[]> secrets = new ArrayList<byte[]>();
for(int i = 0; i < 20; i++) {
secrets.add(crypto.deriveNextSecret(secret.clone(), i));
}
for(int i = 0; i < 20; i++) {
byte[] secretI = secrets.get(i);
for(int j = 0; j < 20; j++) {
byte[] secretJ = secrets.get(j);
assertEquals(i == j, Arrays.equals(secretI, secretJ));
}
}
}
}
package net.sf.briar.db;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import net.sf.briar.BriarTestCase;
import net.sf.briar.TestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class BasicH2Test extends BriarTestCase {
private static final String CREATE_TABLE =
"CREATE TABLE foo"
+ " (uniqueId BINARY(32),"
+ " name VARCHAR NOT NULL)";
private final File testDir = TestUtils.getTestDirectory();
private final File db = new File(testDir, "db");
private final String url = "jdbc:h2:" + db.getPath();
private Connection connection = null;
@Before
public void setUp() throws Exception {
testDir.mkdirs();
Class.forName("org.h2.Driver");
connection = DriverManager.getConnection(url);
}
@Test
public void testCreateTableAndAddRow() throws Exception {
// Create the table
createTable(connection);
// Generate an ID
byte[] id = new byte[32];
new Random().nextBytes(id);
// Insert the ID and name into the table
addRow(id, "foo");
}
@Test
public void testCreateTableAddAndRetrieveRow() throws Exception {
// Create the table
createTable(connection);
// Generate an ID
byte[] id = new byte[32];
new Random().nextBytes(id);
// Insert the ID and name into the table
addRow(id, "foo");
// Check that the name can be retrieved using the ID
assertEquals("foo", getName(id));
}
@Test
public void testSortOrder() throws Exception {
byte[] first = new byte[] {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, -128
};
byte[] second = new byte[] {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
byte[] third = new byte[] {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 127
};
// Create the table
createTable(connection);
// Insert the rows
addRow(first, "first");
addRow(second, "second");
addRow(third, "third");
addRow(null, "null");
// Check the ordering of the < operator: the null ID is not comparable
assertNull(getPredecessor(first));
assertEquals("first", getPredecessor(second));
assertEquals("second", getPredecessor(third));
assertNull(getPredecessor(null));
// Check the ordering of ORDER BY: nulls come first
List<String> names = getNames();
assertEquals(4, names.size());
assertEquals("null", names.get(0));
assertEquals("first", names.get(1));
assertEquals("second", names.get(2));
assertEquals("third", names.get(3));
}
private void createTable(Connection connection) throws SQLException {
try {
Statement s = connection.createStatement();
s.executeUpdate(CREATE_TABLE);
s.close();
} catch(SQLException e) {
connection.close();
throw e;
}
}
private void addRow(byte[] id, String name) throws SQLException {
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
try {
PreparedStatement ps = connection.prepareStatement(sql);
if(id == null) ps.setNull(1, Types.BINARY);
else ps.setBytes(1, id);
ps.setString(2, name);
int rowsAffected = ps.executeUpdate();
ps.close();
assertEquals(1, rowsAffected);
} catch(SQLException e) {
connection.close();
throw e;
}
}
private String getName(byte[] id) throws SQLException {
String sql = "SELECT name FROM foo WHERE uniqueID = ?";
try {
PreparedStatement ps = connection.prepareStatement(sql);
if(id != null) ps.setBytes(1, id);
ResultSet rs = ps.executeQuery();
assertTrue(rs.next());
String name = rs.getString(1);
assertFalse(rs.next());
rs.close();
ps.close();
return name;
} catch(SQLException e) {
connection.close();
throw e;
}
}
private String getPredecessor(byte[] id) throws SQLException {
String sql = "SELECT name FROM foo WHERE uniqueId < ?"
+ " ORDER BY uniqueId DESC LIMIT ?";
try {
PreparedStatement ps = connection.prepareStatement(sql);
ps.setBytes(1, id);
ps.setInt(2, 1);
ResultSet rs = ps.executeQuery();
String name = rs.next() ? rs.getString(1) : null;
assertFalse(rs.next());
rs.close();
ps.close();
return name;
} catch(SQLException e) {
connection.close();
throw e;
}
}
private List<String> getNames() throws SQLException {
String sql = "SELECT name FROM foo ORDER BY uniqueId";
List<String> names = new ArrayList<String>();
try {
PreparedStatement ps = connection.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while(rs.next()) names.add(rs.getString(1));
rs.close();
ps.close();
return names;
} catch(SQLException e) {
connection.close();
throw e;
}
}
@After
public void tearDown() throws Exception {
if(connection != null) connection.close();
TestUtils.deleteTestDirectory(testDir);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment