diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java index a8f3530f459e442954af5cb4d81bb17bb85dbb95..373daea801c4430184d185c2c74a47dfd45e4048 100644 --- a/api/net/sf/briar/api/crypto/CryptoComponent.java +++ b/api/net/sf/briar/api/crypto/CryptoComponent.java @@ -10,11 +10,17 @@ import javax.crypto.SecretKey; public interface CryptoComponent { - SecretKey deriveMacKey(byte[] secret); + SecretKey deriveIncomingMacKey(byte[] secret); - SecretKey derivePacketKey(byte[] secret); + SecretKey deriveIncomingPacketKey(byte[] secret); - SecretKey deriveTagKey(byte[] secret); + SecretKey deriveIncomingTagKey(byte[] secret); + + SecretKey deriveOutgoingMacKey(byte[] secret); + + SecretKey deriveOutgoingPacketKey(byte[] secret); + + SecretKey deriveOutgoingTagKey(byte[] secret); KeyPair generateKeyPair(); diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java index 99cbe824d79198553fb776de824c274c44d836b4..366c37fd54949a7f8220f5a2545030f9c9c4e682 100644 --- a/components/net/sf/briar/crypto/CryptoComponentImpl.java +++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java @@ -68,9 +68,13 @@ class CryptoComponentImpl implements CryptoComponent { } } - public SecretKey deriveMacKey(byte[] secret) { + public SecretKey deriveIncomingMacKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - if(s.getAlice()) return deriveKey("MACA", s.getIv(), s.getCiphertext()); + return deriveMacKey(s, !s.getAlice()); + } + + private SecretKey deriveMacKey(SharedSecret s, boolean alice) { + if(alice) return deriveKey("MACA", s.getIv(), s.getCiphertext()); else return deriveKey("MACB", s.getIv(), s.getCiphertext()); } @@ -110,18 +114,41 @@ class CryptoComponentImpl implements CryptoComponent { } } - public SecretKey derivePacketKey(byte[] secret) { + public SecretKey deriveIncomingPacketKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - if(s.getAlice()) return deriveKey("PKTA", s.getIv(), s.getCiphertext()); + return derivePacketKey(s, !s.getAlice()); + } + + private SecretKey derivePacketKey(SharedSecret s, boolean alice) { + if(alice) return deriveKey("PKTA", s.getIv(), s.getCiphertext()); else return deriveKey("PKTB", s.getIv(), s.getCiphertext()); } - public SecretKey deriveTagKey(byte[] secret) { + public SecretKey deriveIncomingTagKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - if(s.getAlice()) return deriveKey("TAGA", s.getIv(), s.getCiphertext()); + return deriveTagKey(s, !s.getAlice()); + } + + private SecretKey deriveTagKey(SharedSecret s, boolean alice) { + if(alice) return deriveKey("TAGA", s.getIv(), s.getCiphertext()); else return deriveKey("TAGB", s.getIv(), s.getCiphertext()); } + public SecretKey deriveOutgoingMacKey(byte[] secret) { + SharedSecret s = new SharedSecret(secret); + return deriveMacKey(s, s.getAlice()); + } + + public SecretKey deriveOutgoingPacketKey(byte[] secret) { + SharedSecret s = new SharedSecret(secret); + return derivePacketKey(s, s.getAlice()); + } + + public SecretKey deriveOutgoingTagKey(byte[] secret) { + SharedSecret s = new SharedSecret(secret); + return deriveTagKey(s, s.getAlice()); + } + public KeyPair generateKeyPair() { return keyPairGenerator.generateKeyPair(); } diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java index 4fcf5deff1dfef570d55fd64496373bc024f12a2..77865ddfc11b4ae838cb2ab448de18178d269674 100644 --- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java +++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java @@ -50,7 +50,8 @@ DatabaseListener { for(ContactId c : db.getContacts()) { try { // Initialise and store the contact's tag cipher - SecretKey tagKey = crypto.deriveTagKey(db.getSharedSecret(c)); + byte[] secret = db.getSharedSecret(c); + SecretKey tagKey = crypto.deriveIncomingTagKey(secret); Cipher cipher = crypto.getTagCipher(); try { cipher.init(Cipher.ENCRYPT_MODE, tagKey); diff --git a/components/net/sf/briar/transport/PacketReaderFactoryImpl.java b/components/net/sf/briar/transport/PacketReaderFactoryImpl.java index 5e5a7f2bff4bf5bbd3ae273d4a63e7ff4d6f2645..3d8ccbdafe380b8ec5de4ae6163abcb048a4bc36 100644 --- a/components/net/sf/briar/transport/PacketReaderFactoryImpl.java +++ b/components/net/sf/briar/transport/PacketReaderFactoryImpl.java @@ -24,9 +24,9 @@ class PacketReaderFactoryImpl implements PacketReaderFactory { public PacketReader createPacketReader(byte[] firstTag, InputStream in, int transportId, long connection, byte[] secret) { - SecretKey macKey = crypto.deriveMacKey(secret); - SecretKey tagKey = crypto.deriveTagKey(secret); - SecretKey packetKey = crypto.derivePacketKey(secret); + SecretKey macKey = crypto.deriveIncomingMacKey(secret); + SecretKey tagKey = crypto.deriveIncomingTagKey(secret); + SecretKey packetKey = crypto.deriveIncomingPacketKey(secret); Cipher tagCipher = crypto.getTagCipher(); Cipher packetCipher = crypto.getPacketCipher(); Mac mac = crypto.getMac(); diff --git a/components/net/sf/briar/transport/PacketWriterFactoryImpl.java b/components/net/sf/briar/transport/PacketWriterFactoryImpl.java index cf411c2e08ae46a3cddcf9e0e7979b50975654e0..a59d201c4c27ae2ffc88b4101872fb38eed11c2a 100644 --- a/components/net/sf/briar/transport/PacketWriterFactoryImpl.java +++ b/components/net/sf/briar/transport/PacketWriterFactoryImpl.java @@ -24,9 +24,9 @@ class PacketWriterFactoryImpl implements PacketWriterFactory { public PacketWriter createPacketWriter(OutputStream out, int transportId, long connection, byte[] secret) { - SecretKey macKey = crypto.deriveMacKey(secret); - SecretKey tagKey = crypto.deriveTagKey(secret); - SecretKey packetKey = crypto.derivePacketKey(secret); + SecretKey macKey = crypto.deriveOutgoingMacKey(secret); + SecretKey tagKey = crypto.deriveOutgoingTagKey(secret); + SecretKey packetKey = crypto.deriveOutgoingPacketKey(secret); Cipher tagCipher = crypto.getTagCipher(); Cipher packetCipher = crypto.getPacketCipher(); Mac mac = crypto.getMac(); diff --git a/test/build.xml b/test/build.xml index d12ada885d0073a7257998f68fc8bcd874b98c4b..f63488bbfffcdf781d493a939c4e0da5bd8d5b76 100644 --- a/test/build.xml +++ b/test/build.xml @@ -16,6 +16,8 @@ <test name='net.sf.briar.FileReadWriteTest'/> <test name='net.sf.briar.LockFairnessTest'/> <test name='net.sf.briar.crypto.CounterModeTest'/> + <test name='net.sf.briar.crypto.CryptoComponentTest'/> + <test name='net.sf.briar.crypto.SharedSecretTest'/> <test name='net.sf.briar.db.BasicH2Test'/> <test name='net.sf.briar.db.DatabaseCleanerImplTest'/> <test name='net.sf.briar.db.H2DatabaseTest'/> diff --git a/test/net/sf/briar/FileReadWriteTest.java b/test/net/sf/briar/FileReadWriteTest.java index 4988507957df41291d9977927d801662c90d2488..363710f53ba5c94c1b4bc7563cfa0bd9518efd25 100644 --- a/test/net/sf/briar/FileReadWriteTest.java +++ b/test/net/sf/briar/FileReadWriteTest.java @@ -71,7 +71,7 @@ public class FileReadWriteTest extends TestCase { private final ProtocolReaderFactory protocolReaderFactory; private final ProtocolWriterFactory protocolWriterFactory; private final CryptoComponent crypto; - private final byte[] secret = new byte[45]; + private final byte[] aliceSecret, bobSecret; private final int transportId = 123; private final long connection = 234L; private final Author author; @@ -94,6 +94,10 @@ public class FileReadWriteTest extends TestCase { crypto = i.getInstance(CryptoComponent.class); assertEquals(crypto.getMessageDigest().getDigestLength(), UniqueId.LENGTH); + // Create matching secrets: one for Alice, one for Bob + aliceSecret = new byte[45]; + aliceSecret[16] = (byte) 1; + bobSecret = new byte[45]; // Create two groups: one restricted, one unrestricted GroupFactory groupFactory = i.getInstance(GroupFactory.class); group = groupFactory.createGroup("Unrestricted group", null); @@ -129,8 +133,9 @@ public class FileReadWriteTest extends TestCase { @Test public void testWriteFile() throws Exception { OutputStream out = new FileOutputStream(file); + // Use Alice's secret for writing PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out, - transportId, connection, secret); + transportId, connection, aliceSecret); out = packetWriter.getOutputStream(); AckWriter a = protocolWriterFactory.createAckWriter(out); @@ -194,8 +199,9 @@ public class FileReadWriteTest extends TestCase { offset += read; } assertEquals(16, offset); + // Use Bob's secret for reading PacketReader packetReader = packetReaderFactory.createPacketReader( - firstTag, in, transportId, connection, secret); + firstTag, in, transportId, connection, bobSecret); in = packetReader.getInputStream(); ProtocolReader protocolReader = protocolReaderFactory.createProtocolReader(in); diff --git a/test/net/sf/briar/crypto/CryptoComponentTest.java b/test/net/sf/briar/crypto/CryptoComponentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d7dad93ed9b8933ad64725111c383580401161f9 --- /dev/null +++ b/test/net/sf/briar/crypto/CryptoComponentTest.java @@ -0,0 +1,49 @@ +package net.sf.briar.crypto; + +import junit.framework.TestCase; +import net.sf.briar.api.crypto.CryptoComponent; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class CryptoComponentTest extends TestCase { + + private final CryptoComponent crypto; + + public CryptoComponentTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + crypto = i.getInstance(CryptoComponent.class); + } + + @Test + public void testKeyDerivation() { + // Create matching secrets: one for Alice, one for Bob + byte[] aliceSecret = new byte[123]; + aliceSecret[SharedSecret.IV_BYTES] = (byte) 1; + byte[] bobSecret = new byte[123]; + // Check that Alice's incoming keys match Bob's outgoing keys + assertEquals(crypto.deriveIncomingMacKey(aliceSecret), + crypto.deriveOutgoingMacKey(bobSecret)); + assertEquals(crypto.deriveIncomingPacketKey(aliceSecret), + crypto.deriveOutgoingPacketKey(bobSecret)); + assertEquals(crypto.deriveIncomingTagKey(aliceSecret), + crypto.deriveOutgoingTagKey(bobSecret)); + // Check that Alice's outgoing keys match Bob's incoming keys + assertEquals(crypto.deriveOutgoingMacKey(aliceSecret), + crypto.deriveIncomingMacKey(bobSecret)); + assertEquals(crypto.deriveOutgoingPacketKey(aliceSecret), + crypto.deriveIncomingPacketKey(bobSecret)); + assertEquals(crypto.deriveOutgoingTagKey(aliceSecret), + crypto.deriveIncomingTagKey(bobSecret)); + // Check that Alice's incoming and outgoing keys are different + assertFalse(crypto.deriveIncomingMacKey(aliceSecret).equals( + crypto.deriveOutgoingMacKey(aliceSecret))); + assertFalse(crypto.deriveIncomingPacketKey(aliceSecret).equals( + crypto.deriveOutgoingPacketKey(aliceSecret))); + assertFalse(crypto.deriveIncomingTagKey(aliceSecret).equals( + crypto.deriveOutgoingTagKey(aliceSecret))); + } +} diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java index 948a7872ae05661597d8fc581ba5fcc5fa7fb013..c5009db87de80dffeaeb594e821ce13798993955 100644 --- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java +++ b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java @@ -60,7 +60,7 @@ public class ConnectionRecogniserImplTest extends TestCase { @Test public void testExpectedTag() throws Exception { // Calculate the expected tag for connection number 3 - SecretKey tagKey = crypto.deriveTagKey(secret); + SecretKey tagKey = crypto.deriveIncomingTagKey(secret); Cipher tagCipher = crypto.getTagCipher(); tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); byte[] tag = TagEncoder.encodeTag(transportId, 3L, 0); diff --git a/test/net/sf/briar/transport/PacketReadWriteTest.java b/test/net/sf/briar/transport/PacketReadWriteTest.java index a4f47e1bfa1a5df8ac645d00d26f1de1c6f9827d..106dff792f7d086725d739e888a037e5ce0e0b7c 100644 --- a/test/net/sf/briar/transport/PacketReadWriteTest.java +++ b/test/net/sf/briar/transport/PacketReadWriteTest.java @@ -39,9 +39,10 @@ public class PacketReadWriteTest extends TestCase { crypto = i.getInstance(CryptoComponent.class); tagCipher = crypto.getTagCipher(); packetCipher = crypto.getPacketCipher(); - macKey = crypto.deriveMacKey(secret); - tagKey = crypto.deriveTagKey(secret); - packetKey = crypto.derivePacketKey(secret); + // Since we're sending packets to ourselves, we only need outgoing keys + macKey = crypto.deriveOutgoingMacKey(secret); + tagKey = crypto.deriveOutgoingTagKey(secret); + packetKey = crypto.deriveOutgoingPacketKey(secret); mac = crypto.getMac(); random = new Random(); }