From 9dea4d0299c6219e04607bda319f5d5fb75a0dba Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Fri, 19 Aug 2011 11:15:35 +0200 Subject: [PATCH] The word "tag" was overloaded, so from now on use "tag" for the predefined tags in the protocol and serial components, and "IV" for the encrypted IVs used to identify connections in the transport component. --- .../sf/briar/api/crypto/CryptoComponent.java | 18 ++-- .../api/transport/ConnectionRecogniser.java | 7 +- .../api/transport/TransportConstants.java | 7 +- .../sf/briar/crypto/CryptoComponentImpl.java | 84 +++++++++---------- .../transport/ConnectionDecrypterImpl.java | 16 ++-- .../transport/ConnectionEncrypterImpl.java | 34 ++++---- .../transport/ConnectionRecogniserImpl.java | 78 ++++++++--------- .../ConnectionWriterFactoryImpl.java | 6 +- .../net/sf/briar/transport/IvEncoder.java | 33 ++++++++ .../net/sf/briar/transport/TagDecoder.java | 20 ----- .../net/sf/briar/transport/TagEncoder.java | 31 ------- test/net/sf/briar/FileReadWriteTest.java | 4 +- .../sf/briar/crypto/CryptoComponentTest.java | 12 +-- .../ConnectionDecrypterImplTest.java | 16 ++-- .../ConnectionEncrypterImplTest.java | 43 +++++----- .../transport/ConnectionReaderImplTest.java | 2 + .../ConnectionRecogniserImplTest.java | 28 +++---- .../transport/ConnectionWriterImplTest.java | 2 + .../briar/transport/FrameReadWriteTest.java | 34 ++++---- 19 files changed, 232 insertions(+), 243 deletions(-) create mode 100644 components/net/sf/briar/transport/IvEncoder.java delete mode 100644 components/net/sf/briar/transport/TagDecoder.java delete mode 100644 components/net/sf/briar/transport/TagEncoder.java diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java index 504ed97055..6997072e54 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 511f65cf57..b57af58d4b 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 2746a8c0df..0aa0c01afa 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 f8816a1859..57ac876e58 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 4ab3b618d7..9e74fe792b 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 0fccc905f9..ad1d2d7285 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 5f662503c0..7915ecf718 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 ae12ef4b1c..9d0768c03c 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 0000000000..481f0304a5 --- /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 a83be3d847..0000000000 --- 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 dbde2fa505..0000000000 --- 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 e7a6c86c9b..a831946ca5 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 b8ea8ade6a..8e64765e2f 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 9bf306fd84..1787033468 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 3b3a9145d2..59624159dd 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 d73e43676e..1e8fe8a568 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 c94b242a8a..4d8af92f52 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 4892d58c97..134b58ab69 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 7fb8ef7177..d7c98f09a7 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); -- GitLab