diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java index 504ed97055b130202be2d8a65c781512757276ad..6997072e54f7cad5c056a2e03dedafbf64d50d61 100644 --- a/api/net/sf/briar/api/crypto/CryptoComponent.java +++ b/api/net/sf/briar/api/crypto/CryptoComponent.java @@ -10,31 +10,31 @@ import javax.crypto.SecretKey; public interface CryptoComponent { - SecretKey deriveIncomingMacKey(byte[] secret); - SecretKey deriveIncomingFrameKey(byte[] secret); - SecretKey deriveIncomingTagKey(byte[] secret); + SecretKey deriveIncomingIvKey(byte[] secret); - SecretKey deriveOutgoingMacKey(byte[] secret); + SecretKey deriveIncomingMacKey(byte[] secret); SecretKey deriveOutgoingFrameKey(byte[] secret); - SecretKey deriveOutgoingTagKey(byte[] secret); + SecretKey deriveOutgoingIvKey(byte[] secret); + + SecretKey deriveOutgoingMacKey(byte[] secret); KeyPair generateKeyPair(); SecretKey generateSecretKey(); + Cipher getFrameCipher(); + + Cipher getIvCipher(); + KeyParser getKeyParser(); Mac getMac(); MessageDigest getMessageDigest(); - Cipher getFrameCipher(); - Signature getSignature(); - - Cipher getTagCipher(); } diff --git a/api/net/sf/briar/api/transport/ConnectionRecogniser.java b/api/net/sf/briar/api/transport/ConnectionRecogniser.java index 511f65cf573191d3cf77fcc5059d7b987d318236..b57af58d4beda85ddfb54fc41058e5a32f4484e5 100644 --- a/api/net/sf/briar/api/transport/ConnectionRecogniser.java +++ b/api/net/sf/briar/api/transport/ConnectionRecogniser.java @@ -10,8 +10,9 @@ import net.sf.briar.api.db.DbException; public interface ConnectionRecogniser { /** - * Returns the ID of the contact who created the tag if the connection - * should be accepted, or null if the connection should be rejected. + * Returns the ID of the contact who created the encrypted IV if the + * connection should be accepted, or null if the connection should be + * rejected. */ - ContactId acceptConnection(byte[] tag) throws DbException; + ContactId acceptConnection(byte[] encryptedIv) throws DbException; } diff --git a/api/net/sf/briar/api/transport/TransportConstants.java b/api/net/sf/briar/api/transport/TransportConstants.java index 2746a8c0dfd9e6e1f8cea917928eebf8a8239da5..0aa0c01afa9cd143928e12bfc78db5314ffeefc6 100644 --- a/api/net/sf/briar/api/transport/TransportConstants.java +++ b/api/net/sf/briar/api/transport/TransportConstants.java @@ -7,6 +7,9 @@ public interface TransportConstants { */ static final int MAX_FRAME_LENGTH = 65536; // 2^16 - /** The length in bytes of the tag that uniquely identifies a connection. */ - static final int TAG_LENGTH = 16; + /** + * The length in bytes of the encrypted IV that uniquely identifies a + * connection. + */ + static final int IV_LENGTH = 16; } diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java index f8816a1859a95bf1f4ffc977fa44b2ea9992d176..57ac876e5835953910d87ef7ebe02845bd0e8a01 100644 --- a/components/net/sf/briar/crypto/CryptoComponentImpl.java +++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java @@ -37,12 +37,12 @@ class CryptoComponentImpl implements CryptoComponent { private static final String KEY_PAIR_ALGO = "ECDSA"; private static final int KEY_PAIR_BITS = 256; private static final String SECRET_STORAGE_ALGO = "AES/CTR/NoPadding"; - private static final String MAC_ALGO = "HMacSHA256"; - private static final String PACKET_CIPHER_ALGO = "AES/CTR/NoPadding"; + private static final String FRAME_CIPHER_ALGO = "AES/CTR/NoPadding"; private static final String SECRET_KEY_ALGO = "AES"; private static final int SECRET_KEY_BITS = 256; + private static final String IV_CIPHER_ALGO = "AES/ECB/NoPadding"; + private static final String MAC_ALGO = "HMacSHA256"; private static final String SIGNATURE_ALGO = "ECDSA"; - private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding"; private final SecretKey secretStorageKey; private final KeyParser keyParser; @@ -68,14 +68,14 @@ class CryptoComponentImpl implements CryptoComponent { } } - public SecretKey deriveIncomingMacKey(byte[] secret) { + public SecretKey deriveIncomingFrameKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - return deriveMacKey(s, !s.getAlice()); + return deriveFrameKey(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()); + private SecretKey deriveFrameKey(SharedSecret s, boolean alice) { + if(alice) return deriveKey("F_A", s.getIv(), s.getCiphertext()); + else return deriveKey("F_B", s.getIv(), s.getCiphertext()); } private SecretKey deriveKey(String name, IvParameterSpec iv, @@ -114,39 +114,39 @@ class CryptoComponentImpl implements CryptoComponent { } } - public SecretKey deriveIncomingFrameKey(byte[] secret) { + public SecretKey deriveIncomingIvKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - return deriveFrameKey(s, !s.getAlice()); + return deriveIvKey(s, !s.getAlice()); } - private SecretKey deriveFrameKey(SharedSecret s, boolean alice) { - if(alice) return deriveKey("PKTA", s.getIv(), s.getCiphertext()); - else return deriveKey("PKTB", s.getIv(), s.getCiphertext()); + private SecretKey deriveIvKey(SharedSecret s, boolean alice) { + if(alice) return deriveKey("I_A", s.getIv(), s.getCiphertext()); + else return deriveKey("I_B", s.getIv(), s.getCiphertext()); } - public SecretKey deriveIncomingTagKey(byte[] secret) { + public SecretKey deriveIncomingMacKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - return deriveTagKey(s, !s.getAlice()); + return deriveMacKey(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()); + private SecretKey deriveMacKey(SharedSecret s, boolean alice) { + if(alice) return deriveKey("M_A", s.getIv(), s.getCiphertext()); + else return deriveKey("M_B", s.getIv(), s.getCiphertext()); } - public SecretKey deriveOutgoingMacKey(byte[] secret) { + public SecretKey deriveOutgoingFrameKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - return deriveMacKey(s, s.getAlice()); + return deriveFrameKey(s, s.getAlice()); } - public SecretKey deriveOutgoingFrameKey(byte[] secret) { + public SecretKey deriveOutgoingIvKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - return deriveFrameKey(s, s.getAlice()); + return deriveIvKey(s, s.getAlice()); } - public SecretKey deriveOutgoingTagKey(byte[] secret) { + public SecretKey deriveOutgoingMacKey(byte[] secret) { SharedSecret s = new SharedSecret(secret); - return deriveTagKey(s, s.getAlice()); + return deriveMacKey(s, s.getAlice()); } public KeyPair generateKeyPair() { @@ -157,45 +157,47 @@ class CryptoComponentImpl implements CryptoComponent { return keyGenerator.generateKey(); } - public KeyParser getKeyParser() { - return keyParser; - } - - public Mac getMac() { + public Cipher getFrameCipher() { try { - return Mac.getInstance(MAC_ALGO, PROVIDER); + return Cipher.getInstance(FRAME_CIPHER_ALGO, PROVIDER); } catch(NoSuchAlgorithmException e) { throw new RuntimeException(e); + } catch(NoSuchPaddingException e) { + throw new RuntimeException(e); } catch(NoSuchProviderException e) { throw new RuntimeException(e); } } - public MessageDigest getMessageDigest() { + public Cipher getIvCipher() { try { - return MessageDigest.getInstance(DIGEST_ALGO, PROVIDER); + return Cipher.getInstance(IV_CIPHER_ALGO, PROVIDER); } catch(NoSuchAlgorithmException e) { throw new RuntimeException(e); + } catch(NoSuchPaddingException e) { + throw new RuntimeException(e); } catch(NoSuchProviderException e) { throw new RuntimeException(e); } } - public Cipher getFrameCipher() { + public KeyParser getKeyParser() { + return keyParser; + } + + public Mac getMac() { try { - return Cipher.getInstance(PACKET_CIPHER_ALGO, PROVIDER); + return Mac.getInstance(MAC_ALGO, PROVIDER); } catch(NoSuchAlgorithmException e) { throw new RuntimeException(e); - } catch(NoSuchPaddingException e) { - throw new RuntimeException(e); } catch(NoSuchProviderException e) { throw new RuntimeException(e); } } - public Signature getSignature() { + public MessageDigest getMessageDigest() { try { - return Signature.getInstance(SIGNATURE_ALGO, PROVIDER); + return MessageDigest.getInstance(DIGEST_ALGO, PROVIDER); } catch(NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch(NoSuchProviderException e) { @@ -203,13 +205,11 @@ class CryptoComponentImpl implements CryptoComponent { } } - public Cipher getTagCipher() { + public Signature getSignature() { try { - return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER); + return Signature.getInstance(SIGNATURE_ALGO, PROVIDER); } catch(NoSuchAlgorithmException e) { throw new RuntimeException(e); - } catch(NoSuchPaddingException e) { - throw new RuntimeException(e); } catch(NoSuchProviderException e) { throw new RuntimeException(e); } diff --git a/components/net/sf/briar/transport/ConnectionDecrypterImpl.java b/components/net/sf/briar/transport/ConnectionDecrypterImpl.java index 4ab3b618d76caf95b496475cad15fc084d119025..9e74fe792b33ca71317c892c3421ad1a362b2603 100644 --- a/components/net/sf/briar/transport/ConnectionDecrypterImpl.java +++ b/components/net/sf/briar/transport/ConnectionDecrypterImpl.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; import java.io.EOFException; @@ -24,7 +24,7 @@ implements ConnectionDecrypter { private final long connection; private final Cipher frameCipher; private final SecretKey frameKey; - private final byte[] buf, tag; + private final byte[] buf, iv; private int bufOff = 0, bufLen = 0; private long frame = 0L; @@ -37,8 +37,8 @@ implements ConnectionDecrypter { this.connection = connection; this.frameCipher = frameCipher; this.frameKey = frameKey; - buf = new byte[TAG_LENGTH]; - tag = new byte[TAG_LENGTH]; + buf = new byte[IV_LENGTH]; + iv = new byte[IV_LENGTH]; } public InputStream getInputStream() { @@ -132,11 +132,11 @@ implements ConnectionDecrypter { private void initialiseCipher() { assert betweenFrames; if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); - TagEncoder.encodeTag(tag, transportId, connection, frame); - // Use the plaintext tag to initialise the packet cipher - IvParameterSpec iv = new IvParameterSpec(tag); + IvEncoder.encodeIv(iv, transportId, connection, frame); + // Use the plaintext IV to initialise the frame cipher + IvParameterSpec ivSpec = new IvParameterSpec(iv); try { - frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv); + frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec); } catch(InvalidAlgorithmParameterException badIv) { throw new RuntimeException(badIv); } catch(InvalidKeyException badKey) { diff --git a/components/net/sf/briar/transport/ConnectionEncrypterImpl.java b/components/net/sf/briar/transport/ConnectionEncrypterImpl.java index 0fccc905f9cf23abd14f8c980078a31cd4d5eef8..ad1d2d72859043f460087493357f3fed5f674382 100644 --- a/components/net/sf/briar/transport/ConnectionEncrypterImpl.java +++ b/components/net/sf/briar/transport/ConnectionEncrypterImpl.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; import java.io.FilterOutputStream; @@ -20,29 +20,29 @@ implements ConnectionEncrypter { private final int transportId; private final long connection; - private final Cipher tagCipher, frameCipher; + private final Cipher ivCipher, frameCipher; private final SecretKey frameKey; - private final byte[] tag; + private final byte[] iv; private long frame = 0L; private boolean started = false, betweenFrames = false; ConnectionEncrypterImpl(OutputStream out, int transportId, - long connection, Cipher tagCipher, Cipher frameCipher, - SecretKey tagKey, SecretKey frameKey) { + long connection, Cipher ivCipher, Cipher frameCipher, + SecretKey ivKey, SecretKey frameKey) { super(out); this.transportId = transportId; this.connection = connection; - this.tagCipher = tagCipher; + this.ivCipher = ivCipher; this.frameCipher = frameCipher; this.frameKey = frameKey; - tag = new byte[TAG_LENGTH]; + iv = new byte[IV_LENGTH]; try { - tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); + ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); } catch(InvalidKeyException badKey) { throw new IllegalArgumentException(badKey); } - if(tagCipher.getOutputSize(TAG_LENGTH) != TAG_LENGTH) + if(ivCipher.getOutputSize(IV_LENGTH) != IV_LENGTH) throw new IllegalArgumentException(); } @@ -64,7 +64,7 @@ implements ConnectionEncrypter { @Override public void write(int b) throws IOException { - if(!started) writeTag(); + if(!started) writeIv(); if(betweenFrames) initialiseCipher(); byte[] ciphertext = frameCipher.update(new byte[] {(byte) b}); if(ciphertext != null) out.write(ciphertext); @@ -77,18 +77,18 @@ implements ConnectionEncrypter { @Override public void write(byte[] b, int off, int len) throws IOException { - if(!started) writeTag(); + if(!started) writeIv(); if(betweenFrames) initialiseCipher(); byte[] ciphertext = frameCipher.update(b, off, len); if(ciphertext != null) out.write(ciphertext); } - private void writeTag() throws IOException { + private void writeIv() throws IOException { assert !started; assert !betweenFrames; - TagEncoder.encodeTag(tag, transportId, connection, 0L); + IvEncoder.encodeIv(iv, transportId, connection, 0L); try { - out.write(tagCipher.doFinal(tag)); + out.write(ivCipher.doFinal(iv)); } catch(BadPaddingException badCipher) { throw new IOException(badCipher); } catch(IllegalBlockSizeException badCipher) { @@ -102,10 +102,10 @@ implements ConnectionEncrypter { assert started; assert betweenFrames; if(frame > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); - TagEncoder.encodeTag(tag, transportId, connection, frame); - IvParameterSpec iv = new IvParameterSpec(tag); + IvEncoder.encodeIv(iv, transportId, connection, frame); + IvParameterSpec ivSpec = new IvParameterSpec(iv); try { - frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); } catch(InvalidAlgorithmParameterException badIv) { throw new RuntimeException(badIv); } catch(InvalidKeyException badKey) { diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java index 5f662503c0b5c4f55360cc1828c56b24b075f35d..7915ecf7189bb648894947f35270a1b6d4637d63 100644 --- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java +++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import java.security.InvalidKeyException; import java.util.HashMap; @@ -27,9 +27,9 @@ DatabaseListener { private final int transportId; private final CryptoComponent crypto; private final DatabaseComponent db; - private final Map<Bytes, ContactId> tagToContact; - private final Map<Bytes, Long> tagToConnectionNumber; - private final Map<ContactId, Map<Long, Bytes>> contactToTags; + private final Map<Bytes, ContactId> ivToContact; + private final Map<Bytes, Long> ivToConnectionNumber; + private final Map<ContactId, Map<Long, Bytes>> contactToIvs; private final Map<ContactId, Cipher> contactToCipher; private final Map<ContactId, ConnectionWindow> contactToWindow; private boolean initialised = false; @@ -40,9 +40,9 @@ DatabaseListener { this.crypto = crypto; this.db = db; // FIXME: There's probably a tidier way of maintaining all this state - tagToContact = new HashMap<Bytes, ContactId>(); - tagToConnectionNumber = new HashMap<Bytes, Long>(); - contactToTags = new HashMap<ContactId, Map<Long, Bytes>>(); + ivToContact = new HashMap<Bytes, ContactId>(); + ivToConnectionNumber = new HashMap<Bytes, Long>(); + contactToIvs = new HashMap<ContactId, Map<Long, Bytes>>(); contactToCipher = new HashMap<ContactId, Cipher>(); contactToWindow = new HashMap<ContactId, ConnectionWindow>(); db.addListener(this); @@ -51,26 +51,26 @@ DatabaseListener { private synchronized void initialise() throws DbException { for(ContactId c : db.getContacts()) { try { - // Initialise and store the contact's tag cipher + // Initialise and store the contact's IV cipher byte[] secret = db.getSharedSecret(c); - SecretKey tagKey = crypto.deriveIncomingTagKey(secret); - Cipher cipher = crypto.getTagCipher(); + SecretKey ivKey = crypto.deriveIncomingIvKey(secret); + Cipher cipher = crypto.getIvCipher(); try { - cipher.init(Cipher.ENCRYPT_MODE, tagKey); + cipher.init(Cipher.ENCRYPT_MODE, ivKey); } catch(InvalidKeyException badKey) { throw new RuntimeException(badKey); } contactToCipher.put(c, cipher); - // Calculate the tags for the contact's connection window + // Calculate the IVs for the contact's connection window ConnectionWindow w = db.getConnectionWindow(c, transportId); - Map<Long, Bytes> tags = new HashMap<Long, Bytes>(); + Map<Long, Bytes> ivs = new HashMap<Long, Bytes>(); for(Long unseen : w.getUnseenConnectionNumbers()) { - Bytes expectedTag = new Bytes(calculateTag(c, unseen)); - tagToContact.put(expectedTag, c); - tagToConnectionNumber.put(expectedTag, unseen); - tags.put(unseen, expectedTag); + Bytes expectedIv = new Bytes(encryptIv(c, unseen)); + ivToContact.put(expectedIv, c); + ivToConnectionNumber.put(expectedIv, unseen); + ivs.put(unseen, expectedIv); } - contactToTags.put(c, tags); + contactToIvs.put(c, ivs); contactToWindow.put(c, w); } catch(NoSuchContactException e) { // The contact was removed after the call to getContacts() @@ -80,12 +80,12 @@ DatabaseListener { initialised = true; } - private synchronized byte[] calculateTag(ContactId c, long connection) { - byte[] tag = TagEncoder.encodeTag(transportId, connection); + private synchronized byte[] encryptIv(ContactId c, long connection) { + byte[] iv = IvEncoder.encodeIv(transportId, connection); Cipher cipher = contactToCipher.get(c); assert cipher != null; try { - return cipher.doFinal(tag); + return cipher.doFinal(iv); } catch(BadPaddingException badCipher) { throw new RuntimeException(badCipher); } catch(IllegalBlockSizeException badCipher) { @@ -93,36 +93,36 @@ DatabaseListener { } } - public synchronized ContactId acceptConnection(byte[] tag) + public synchronized ContactId acceptConnection(byte[] encryptedIv) throws DbException { - if(tag.length != TAG_LENGTH) + if(encryptedIv.length != IV_LENGTH) throw new IllegalArgumentException(); if(!initialised) initialise(); - Bytes b = new Bytes(tag); - ContactId contactId = tagToContact.remove(b); - Long connection = tagToConnectionNumber.remove(b); + Bytes b = new Bytes(encryptedIv); + ContactId contactId = ivToContact.remove(b); + Long connection = ivToConnectionNumber.remove(b); assert (contactId == null) == (connection == null); if(contactId == null) return null; - // The tag was expected - update and save the connection window + // The IV was expected - update and save the connection window ConnectionWindow w = contactToWindow.get(contactId); assert w != null; w.setSeen(connection); db.setConnectionWindow(contactId, transportId, w); - // Update the set of expected tags - Map<Long, Bytes> oldTags = contactToTags.remove(contactId); - assert oldTags != null; - assert oldTags.containsKey(connection); - Map<Long, Bytes> newTags = new HashMap<Long, Bytes>(); + // Update the set of expected IVs + Map<Long, Bytes> oldIvs = contactToIvs.remove(contactId); + assert oldIvs != null; + assert oldIvs.containsKey(connection); + Map<Long, Bytes> newIvs = new HashMap<Long, Bytes>(); for(Long unseen : w.getUnseenConnectionNumbers()) { - Bytes expectedTag = oldTags.get(unseen); - if(expectedTag == null) { - expectedTag = new Bytes(calculateTag(contactId, unseen)); - tagToContact.put(expectedTag, contactId); - tagToConnectionNumber.put(expectedTag, connection); + Bytes expectedIv = oldIvs.get(unseen); + if(expectedIv == null) { + expectedIv = new Bytes(encryptIv(contactId, unseen)); + ivToContact.put(expectedIv, contactId); + ivToConnectionNumber.put(expectedIv, connection); } - newTags.put(unseen, expectedTag); + newIvs.put(unseen, expectedIv); } - contactToTags.put(contactId, newTags); + contactToIvs.put(contactId, newIvs); return contactId; } diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java index ae12ef4b1c4384ce4fb3fd6440f8a4e2dc3cc835..9d0768c03c78164aab7e26af713c38de264fb7e6 100644 --- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java +++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java @@ -25,9 +25,9 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory { public ConnectionWriter createConnectionWriter(OutputStream out, int transportId, long connection, byte[] secret) { SecretKey macKey = crypto.deriveOutgoingMacKey(secret); - SecretKey tagKey = crypto.deriveOutgoingTagKey(secret); + SecretKey ivKey = crypto.deriveOutgoingIvKey(secret); SecretKey frameKey = crypto.deriveOutgoingFrameKey(secret); - Cipher tagCipher = crypto.getTagCipher(); + Cipher ivCipher = crypto.getIvCipher(); Cipher frameCipher = crypto.getFrameCipher(); Mac mac = crypto.getMac(); try { @@ -36,7 +36,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory { throw new IllegalArgumentException(badKey); } ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out, - transportId, connection, tagCipher, frameCipher, tagKey, + transportId, connection, ivCipher, frameCipher, ivKey, frameKey); return new ConnectionWriterImpl(encrypter, mac); } diff --git a/components/net/sf/briar/transport/IvEncoder.java b/components/net/sf/briar/transport/IvEncoder.java new file mode 100644 index 0000000000000000000000000000000000000000..481f0304a59226d303661c58b02817e528505d26 --- /dev/null +++ b/components/net/sf/briar/transport/IvEncoder.java @@ -0,0 +1,33 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; +import net.sf.briar.util.ByteUtils; + +class IvEncoder { + + static byte[] encodeIv(int transportId, long connection) { + byte[] iv = new byte[IV_LENGTH]; + // Encode the transport identifier as an unsigned 16-bit integer + ByteUtils.writeUint16(transportId, iv, 4); + // Encode the connection number as an unsigned 32-bit integer + ByteUtils.writeUint32(connection, iv, 6); + return iv; + } + + static void encodeIv(byte[] iv, int transportId, long connection, + long frame) { + if(iv.length != IV_LENGTH) throw new IllegalArgumentException(); + // The first 16 bits of the IV must be zero (reserved) + iv[0] = 0; + iv[1] = 0; + // Encode the transport identifier as an unsigned 16-bit integer + ByteUtils.writeUint16(transportId, iv, 4); + // Encode the connection number as an unsigned 32-bit integer + ByteUtils.writeUint32(connection, iv, 6); + // Encode the frame number as an unsigned 32-bit integer + ByteUtils.writeUint32(frame, iv, 10); + // The last 16 bits of the IV must be zero (block number) + iv[14] = 0; + iv[15] = 0; + } +} diff --git a/components/net/sf/briar/transport/TagDecoder.java b/components/net/sf/briar/transport/TagDecoder.java deleted file mode 100644 index a83be3d847f5b68e77c387d44b7c840c0c4727d0..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/TagDecoder.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.briar.transport; - -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; -import net.sf.briar.util.ByteUtils; - -class TagDecoder { - - static boolean decodeTag(byte[] tag, int transportId, long connection) { - if(tag.length != TAG_LENGTH) return false; - // First 32 bits must be zero (reserved) - for(int i = 0; i < 4; i++) if(tag[i] != 0) return false; - // Transport identifier is encoded as an unsigned 16-bit integer - if(ByteUtils.readUint16(tag, 4) != transportId) return false; - // Connection number is encoded as an unsigned 32-bit integer - if(ByteUtils.readUint32(tag, 6) != connection) return false; - // Last 48 bits must be zero (frame number and block number) - for(int i = 10; i < 16; i++) if(tag[i] != 0) return false; - return true; - } -} diff --git a/components/net/sf/briar/transport/TagEncoder.java b/components/net/sf/briar/transport/TagEncoder.java deleted file mode 100644 index dbde2fa50538021ad218f4ff86d2b653a55ad209..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/TagEncoder.java +++ /dev/null @@ -1,31 +0,0 @@ -package net.sf.briar.transport; - -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; -import net.sf.briar.util.ByteUtils; - -class TagEncoder { - - static byte[] encodeTag(int transportId, long connection) { - byte[] tag = new byte[TAG_LENGTH]; - // Encode the transport identifier as an unsigned 16-bit integer - ByteUtils.writeUint16(transportId, tag, 4); - // Encode the connection number as an unsigned 32-bit integer - ByteUtils.writeUint32(connection, tag, 6); - return tag; - } - - static void encodeTag(byte[] tag, int transportId, long connection, - long frame) { - if(tag.length != TAG_LENGTH) throw new IllegalArgumentException(); - // The first 16 bits of the tag must be zero (reserved) - ByteUtils.writeUint16(0, tag, 0); - // Encode the transport identifier as an unsigned 16-bit integer - ByteUtils.writeUint16(transportId, tag, 4); - // Encode the connection number as an unsigned 32-bit integer - ByteUtils.writeUint32(connection, tag, 6); - // Encode the frame number as an unsigned 32-bit integer - ByteUtils.writeUint32(frame, tag, 10); - // The last 16 bits of the tag must be zero (block number) - ByteUtils.writeUint16(0, tag, 14); - } -} diff --git a/test/net/sf/briar/FileReadWriteTest.java b/test/net/sf/briar/FileReadWriteTest.java index e7a6c86c9b5cd2b56808ef875d4f0243c8b8617a..a831946ca58668bed06feab6a227c0eaba9d7471 100644 --- a/test/net/sf/briar/FileReadWriteTest.java +++ b/test/net/sf/briar/FileReadWriteTest.java @@ -185,10 +185,10 @@ public class FileReadWriteTest extends TestCase { testWriteFile(); InputStream in = new FileInputStream(file); - byte[] firstTag = new byte[16]; + byte[] iv = new byte[16]; int offset = 0; while(offset < 16) { - int read = in.read(firstTag, offset, firstTag.length - offset); + int read = in.read(iv, offset, iv.length - offset); if(read == -1) break; offset += read; } diff --git a/test/net/sf/briar/crypto/CryptoComponentTest.java b/test/net/sf/briar/crypto/CryptoComponentTest.java index b8ea8ade6a8a36e136374a323035a58bb791c7e8..8e64765e2f81fd3b7c22b5a2cf17e43f0b7ff2a0 100644 --- a/test/net/sf/briar/crypto/CryptoComponentTest.java +++ b/test/net/sf/briar/crypto/CryptoComponentTest.java @@ -29,21 +29,21 @@ public class CryptoComponentTest extends TestCase { crypto.deriveOutgoingMacKey(bobSecret)); assertEquals(crypto.deriveIncomingFrameKey(aliceSecret), crypto.deriveOutgoingFrameKey(bobSecret)); - assertEquals(crypto.deriveIncomingTagKey(aliceSecret), - crypto.deriveOutgoingTagKey(bobSecret)); + assertEquals(crypto.deriveIncomingIvKey(aliceSecret), + crypto.deriveOutgoingIvKey(bobSecret)); // Check that Alice's outgoing keys match Bob's incoming keys assertEquals(crypto.deriveOutgoingMacKey(aliceSecret), crypto.deriveIncomingMacKey(bobSecret)); assertEquals(crypto.deriveOutgoingFrameKey(aliceSecret), crypto.deriveIncomingFrameKey(bobSecret)); - assertEquals(crypto.deriveOutgoingTagKey(aliceSecret), - crypto.deriveIncomingTagKey(bobSecret)); + assertEquals(crypto.deriveOutgoingIvKey(aliceSecret), + crypto.deriveIncomingIvKey(bobSecret)); // Check that Alice's incoming and outgoing keys are different assertFalse(crypto.deriveIncomingMacKey(aliceSecret).equals( crypto.deriveOutgoingMacKey(aliceSecret))); assertFalse(crypto.deriveIncomingFrameKey(aliceSecret).equals( crypto.deriveOutgoingFrameKey(aliceSecret))); - assertFalse(crypto.deriveIncomingTagKey(aliceSecret).equals( - crypto.deriveOutgoingTagKey(aliceSecret))); + assertFalse(crypto.deriveIncomingIvKey(aliceSecret).equals( + crypto.deriveOutgoingIvKey(aliceSecret))); } } diff --git a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java index 9bf306fd844dc7470d0a80e28c5b1fc75e876e87..17870334683262853391d3b1dd8cceeaffb6eaa2 100644 --- a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import java.io.ByteArrayInputStream; import java.util.Arrays; @@ -54,16 +54,16 @@ public class ConnectionDecrypterImplTest extends TestCase { public void testDecryption() throws Exception { // Calculate the expected plaintext for the first frame byte[] ciphertext = new byte[123]; - byte[] ivBytes = new byte[TAG_LENGTH]; - TagEncoder.encodeTag(ivBytes, transportId, connection, 0L); - IvParameterSpec iv = new IvParameterSpec(ivBytes); - frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv); + byte[] iv = new byte[IV_LENGTH]; + IvEncoder.encodeIv(iv, transportId, connection, 0L); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec); byte[] plaintext = frameCipher.doFinal(ciphertext); // Calculate the expected plaintext for the second frame byte[] ciphertext1 = new byte[1234]; - TagEncoder.encodeTag(ivBytes, transportId, connection, 1L); - iv = new IvParameterSpec(ivBytes); - frameCipher.init(Cipher.DECRYPT_MODE, frameKey, iv); + IvEncoder.encodeIv(iv, transportId, connection, 1L); + ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.DECRYPT_MODE, frameKey, ivSpec); byte[] plaintext1 = frameCipher.doFinal(ciphertext1); assertEquals(ciphertext1.length, plaintext1.length); // Concatenate the ciphertexts diff --git a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java index 3b3a9145d2d3811e0fb18ab6eec23dc68d3acb64..59624159ddd541970e6f03aa7c659bc33b941e87 100644 --- a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import java.io.ByteArrayOutputStream; import java.util.Arrays; @@ -22,8 +22,8 @@ public class ConnectionEncrypterImplTest extends TestCase { private static final int MAC_LENGTH = 32; - private final Cipher tagCipher, frameCipher; - private final SecretKey tagKey, frameKey; + private final Cipher ivCipher, frameCipher; + private final SecretKey ivKey, frameKey; private final int transportId = 1234; private final long connection = 12345L; @@ -31,9 +31,9 @@ public class ConnectionEncrypterImplTest extends TestCase { super(); Injector i = Guice.createInjector(new CryptoModule()); CryptoComponent crypto = i.getInstance(CryptoComponent.class); - tagCipher = crypto.getTagCipher(); + ivCipher = crypto.getIvCipher(); frameCipher = crypto.getFrameCipher(); - tagKey = crypto.generateSecretKey(); + ivKey = crypto.generateSecretKey(); frameKey = crypto.generateSecretKey(); } @@ -41,27 +41,26 @@ public class ConnectionEncrypterImplTest extends TestCase { public void testSingleByteFrame() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId, - connection, tagCipher, frameCipher, tagKey, frameKey); + connection, ivCipher, frameCipher, ivKey, frameKey); e.getOutputStream().write((byte) 0); e.writeMac(new byte[MAC_LENGTH]); - assertEquals(TAG_LENGTH + 1 + MAC_LENGTH, out.toByteArray().length); + assertEquals(IV_LENGTH + 1 + MAC_LENGTH, out.toByteArray().length); } @Test public void testEncryption() throws Exception { - // Calculate the expected ciphertext for the tag - byte[] plaintextTag = TagEncoder.encodeTag(transportId, connection); - assertEquals(TAG_LENGTH, plaintextTag.length); - tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); - byte[] tag = tagCipher.doFinal(plaintextTag); - assertEquals(TAG_LENGTH, tag.length); + // Calculate the expected ciphertext for the IV + byte[] iv = IvEncoder.encodeIv(transportId, connection); + assertEquals(IV_LENGTH, iv.length); + ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); + byte[] encryptedIv = ivCipher.doFinal(iv); + assertEquals(IV_LENGTH, encryptedIv.length); // Calculate the expected ciphertext for the first frame byte[] plaintext = new byte[123]; byte[] plaintextMac = new byte[MAC_LENGTH]; - byte[] ivBytes = new byte[TAG_LENGTH]; - TagEncoder.encodeTag(ivBytes, transportId, connection, 0L); - IvParameterSpec iv = new IvParameterSpec(ivBytes); - frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv); + IvEncoder.encodeIv(iv, transportId, connection, 0L); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); byte[] ciphertext = new byte[plaintext.length + plaintextMac.length]; int offset = frameCipher.update(plaintext, 0, plaintext.length, ciphertext); @@ -69,9 +68,9 @@ public class ConnectionEncrypterImplTest extends TestCase { offset); // Calculate the expected ciphertext for the second frame byte[] plaintext1 = new byte[1234]; - TagEncoder.encodeTag(ivBytes, transportId, connection, 1L); - iv = new IvParameterSpec(ivBytes); - frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, iv); + IvEncoder.encodeIv(iv, transportId, connection, 1L); + ivSpec = new IvParameterSpec(iv); + frameCipher.init(Cipher.ENCRYPT_MODE, frameKey, ivSpec); byte[] ciphertext1 = new byte[plaintext1.length + plaintextMac.length]; offset = frameCipher.update(plaintext1, 0, plaintext1.length, ciphertext1); @@ -79,14 +78,14 @@ public class ConnectionEncrypterImplTest extends TestCase { offset); // Concatenate the ciphertexts ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write(tag); + out.write(encryptedIv); out.write(ciphertext); out.write(ciphertext1); byte[] expected = out.toByteArray(); // Use a ConnectionEncrypter to encrypt the plaintext out.reset(); ConnectionEncrypter e = new ConnectionEncrypterImpl(out, transportId, - connection, tagCipher, frameCipher, tagKey, frameKey); + connection, ivCipher, frameCipher, ivKey, frameKey); e.getOutputStream().write(plaintext); e.writeMac(plaintextMac); e.getOutputStream().write(plaintext1); diff --git a/test/net/sf/briar/transport/ConnectionReaderImplTest.java b/test/net/sf/briar/transport/ConnectionReaderImplTest.java index d73e43676e82a382dbfdd90fa3886447da9cf719..1e8fe8a5681b39b142731de4508ea6f0b370dcf8 100644 --- a/test/net/sf/briar/transport/ConnectionReaderImplTest.java +++ b/test/net/sf/briar/transport/ConnectionReaderImplTest.java @@ -33,6 +33,8 @@ public class ConnectionReaderImplTest extends TestCase { mac.init(crypto.generateSecretKey()); } + // FIXME: Test corner cases and corrupt frames + @Test public void testSingleByteFrame() throws Exception { // Six bytes for the header, one for the payload diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java index c94b242a8a57c9538276596b77517afc7604316b..4d8af92f52989c03dc952024933d046e8456daf9 100644 --- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java +++ b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import java.util.Collection; import java.util.Collections; @@ -41,7 +41,7 @@ public class ConnectionRecogniserImplTest extends TestCase { } @Test - public void testUnexpectedTag() throws Exception { + public void testUnexpectedIv() throws Exception { Mockery context = new Mockery(); final DatabaseComponent db = context.mock(DatabaseComponent.class); context.checking(new Expectations() {{ @@ -55,18 +55,18 @@ public class ConnectionRecogniserImplTest extends TestCase { }}); final ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(transportId, crypto, db); - assertNull(c.acceptConnection(new byte[TAG_LENGTH])); + assertNull(c.acceptConnection(new byte[IV_LENGTH])); context.assertIsSatisfied(); } @Test - public void testExpectedTag() throws Exception { - // Calculate the expected tag for connection number 3 - SecretKey tagKey = crypto.deriveIncomingTagKey(secret); - Cipher tagCipher = crypto.getTagCipher(); - tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); - byte[] tag = TagEncoder.encodeTag(transportId, 3L); - byte[] encryptedTag = tagCipher.doFinal(tag); + public void testExpectedIv() throws Exception { + // Calculate the expected IV for connection number 3 + SecretKey ivKey = crypto.deriveIncomingIvKey(secret); + Cipher ivCipher = crypto.getIvCipher(); + ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); + byte[] iv = IvEncoder.encodeIv(transportId, 3L); + byte[] encryptedIv = ivCipher.doFinal(iv); Mockery context = new Mockery(); final DatabaseComponent db = context.mock(DatabaseComponent.class); @@ -83,10 +83,10 @@ public class ConnectionRecogniserImplTest extends TestCase { }}); final ConnectionRecogniserImpl c = new ConnectionRecogniserImpl(transportId, crypto, db); - // First time - the tag should be expected - assertEquals(contactId, c.acceptConnection(encryptedTag)); - // Second time - the tag should no longer be expected - assertNull(c.acceptConnection(encryptedTag)); + // First time - the IV should be expected + assertEquals(contactId, c.acceptConnection(encryptedIv)); + // Second time - the IV should no longer be expected + assertNull(c.acceptConnection(encryptedIv)); // The window should have advanced assertEquals(4L, connectionWindow.getCentre()); Collection<Long> unseen = connectionWindow.getUnseenConnectionNumbers(); diff --git a/test/net/sf/briar/transport/ConnectionWriterImplTest.java b/test/net/sf/briar/transport/ConnectionWriterImplTest.java index 4892d58c9724296d8e9c08ecf37b14534d3c9f50..134b58ab69c9dae97f70f312a550a1036440e2a2 100644 --- a/test/net/sf/briar/transport/ConnectionWriterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionWriterImplTest.java @@ -30,6 +30,8 @@ public class ConnectionWriterImplTest extends TestCase { mac.init(crypto.generateSecretKey()); } + // FIXME: Test corner cases + @Test public void testSingleByteFrame() throws Exception { // Six bytes for the header, one for the payload diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java index 7fb8ef717728d91e14b14aa7a2e4bceb4fbe4187..d7c98f09a7ba3725fe56b6160741cf5c1752af64 100644 --- a/test/net/sf/briar/transport/FrameReadWriteTest.java +++ b/test/net/sf/briar/transport/FrameReadWriteTest.java @@ -1,6 +1,6 @@ package net.sf.briar.transport; -import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -27,8 +27,8 @@ import com.google.inject.Injector; public class FrameReadWriteTest extends TestCase { private final CryptoComponent crypto; - private final Cipher tagCipher, frameCipher; - private final SecretKey macKey, tagKey, frameKey; + private final Cipher ivCipher, frameCipher; + private final SecretKey ivKey, frameKey, macKey; private final Mac mac; private final Random random; private final byte[] secret = new byte[100]; @@ -39,24 +39,24 @@ public class FrameReadWriteTest extends TestCase { super(); Injector i = Guice.createInjector(new CryptoModule()); crypto = i.getInstance(CryptoComponent.class); - tagCipher = crypto.getTagCipher(); + ivCipher = crypto.getIvCipher(); frameCipher = crypto.getFrameCipher(); - // Since we're sending packets to ourselves, we only need outgoing keys - macKey = crypto.deriveOutgoingMacKey(secret); - tagKey = crypto.deriveOutgoingTagKey(secret); + // Since we're sending frames to ourselves, we only need outgoing keys + ivKey = crypto.deriveOutgoingIvKey(secret); frameKey = crypto.deriveOutgoingFrameKey(secret); + macKey = crypto.deriveOutgoingMacKey(secret); mac = crypto.getMac(); random = new Random(); } @Test public void testWriteAndRead() throws Exception { - // Calculate the expected ciphertext for the tag - byte[] plaintextTag = TagEncoder.encodeTag(transportId, connection); - assertEquals(TAG_LENGTH, plaintextTag.length); - tagCipher.init(Cipher.ENCRYPT_MODE, tagKey); - byte[] tag = tagCipher.doFinal(plaintextTag); - assertEquals(TAG_LENGTH, tag.length); + // Calculate the expected ciphertext for the IV + byte[] iv = IvEncoder.encodeIv(transportId, connection); + assertEquals(IV_LENGTH, iv.length); + ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); + byte[] encryptedIv = ivCipher.doFinal(iv); + assertEquals(IV_LENGTH, encryptedIv.length); // Generate two random frames byte[] frame = new byte[12345]; random.nextBytes(frame); @@ -65,7 +65,7 @@ public class FrameReadWriteTest extends TestCase { // Write the frames ByteArrayOutputStream out = new ByteArrayOutputStream(); ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out, - transportId, connection, tagCipher, frameCipher, tagKey, + transportId, connection, ivCipher, frameCipher, ivKey, frameKey); mac.init(macKey); ConnectionWriter writer = new ConnectionWriterImpl(encrypter, mac); @@ -76,9 +76,9 @@ public class FrameReadWriteTest extends TestCase { out1.flush(); // Read the frames back ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - byte[] recoveredTag = new byte[TAG_LENGTH]; - assertEquals(TAG_LENGTH, in.read(recoveredTag)); - assertTrue(Arrays.equals(tag, recoveredTag)); + byte[] recoveredIv = new byte[IV_LENGTH]; + assertEquals(IV_LENGTH, in.read(recoveredIv)); + assertTrue(Arrays.equals(encryptedIv, recoveredIv)); ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, transportId, connection, frameCipher, frameKey); ConnectionReader reader = new ConnectionReaderImpl(decrypter, mac);