diff --git a/briar-api/src/org/briarproject/api/crypto/StreamDecrypter.java b/briar-api/src/org/briarproject/api/crypto/StreamDecrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..9f217c13861ba4cc6525c7c69ddcc4abcfae3d0f --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamDecrypter.java @@ -0,0 +1,14 @@ +package org.briarproject.api.crypto; + +import java.io.IOException; + +public interface StreamDecrypter { + + /** + * Reads a frame, decrypts its payload into the given buffer and returns + * the payload length, or -1 if no more frames can be read from the stream. + * @throws java.io.IOException if an error occurs while reading the frame, + * or if authenticated decryption fails. + */ + int readFrame(byte[] payload) throws IOException; +} diff --git a/briar-api/src/org/briarproject/api/crypto/StreamDecrypterFactory.java b/briar-api/src/org/briarproject/api/crypto/StreamDecrypterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..46948f2a26625dc9b41fe434bd1e80cdebd8b78f --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamDecrypterFactory.java @@ -0,0 +1,18 @@ +package org.briarproject.api.crypto; + +import java.io.InputStream; + +import org.briarproject.api.transport.StreamContext; + +public interface StreamDecrypterFactory { + + /** Creates a {@link StreamDecrypter} for decrypting a transport stream. */ + StreamDecrypter createStreamDecrypter(InputStream in, int maxFrameLength, + StreamContext ctx); + + /** + * Creates a {@link StreamDecrypter} for decrypting an invitation stream. + */ + StreamDecrypter createInvitationStreamDecrypter(InputStream in, + int maxFrameLength, byte[] secret, boolean alice); +} diff --git a/briar-api/src/org/briarproject/api/crypto/StreamEncrypter.java b/briar-api/src/org/briarproject/api/crypto/StreamEncrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..d48deff3a9c2a8af6cea674542a43a4a1d2d5d99 --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamEncrypter.java @@ -0,0 +1,13 @@ +package org.briarproject.api.crypto; + +import java.io.IOException; + +public interface StreamEncrypter { + + /** Encrypts the given frame and writes it to the stream. */ + void writeFrame(byte[] payload, int payloadLength, boolean finalFrame) + throws IOException; + + /** Flushes the stream. */ + void flush() throws IOException; +} diff --git a/briar-api/src/org/briarproject/api/crypto/StreamEncrypterFactory.java b/briar-api/src/org/briarproject/api/crypto/StreamEncrypterFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..95912937e4e57a57ad7f39d64ec42bad0a7ba5ca --- /dev/null +++ b/briar-api/src/org/briarproject/api/crypto/StreamEncrypterFactory.java @@ -0,0 +1,18 @@ +package org.briarproject.api.crypto; + +import java.io.OutputStream; + +import org.briarproject.api.transport.StreamContext; + +public interface StreamEncrypterFactory { + + /** Creates a {@link StreamEncrypter} for encrypting a transport stream. */ + StreamEncrypter createStreamEncrypter(OutputStream out, + int maxFrameLength, StreamContext ctx); + + /** + * Creates a {@link StreamEncrypter} for encrypting an invitation stream. + */ + StreamEncrypter createInvitationStreamEncrypter(OutputStream out, + int maxFrameLength, byte[] secret, boolean alice); +} diff --git a/briar-api/src/org/briarproject/api/transport/StreamReader.java b/briar-api/src/org/briarproject/api/transport/StreamReader.java deleted file mode 100644 index 2fef7b4df0a913f119c7436090d85046f8fbb164..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/transport/StreamReader.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.api.transport; - -import java.io.InputStream; - -/** Decrypts and authenticates data received over an underlying transport. */ -public interface StreamReader { - - /** - * Returns an input stream from which the decrypted, authenticated data can - * be read. - */ - InputStream getInputStream(); -} diff --git a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java index 14459ec60a0d7e1c2e1f3e286fdaea0dd8554ea9..3bf4cf049041e2fc8d1cb25298c6377f40c14477 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java +++ b/briar-api/src/org/briarproject/api/transport/StreamReaderFactory.java @@ -4,11 +4,17 @@ import java.io.InputStream; public interface StreamReaderFactory { - /** Creates a {@link StreamReader} for a transport connection. */ - StreamReader createStreamReader(InputStream in, int maxFrameLength, + /** + * Creates an {@link java.io.InputStream InputStream} for reading from a + * transport stream. + */ + InputStream createStreamReader(InputStream in, int maxFrameLength, StreamContext ctx); - /** Creates a {@link StreamReader} for an invitation connection. */ - StreamReader createInvitationStreamReader(InputStream in, + /** + * Creates an {@link java.io.InputStream InputStream} for reading from an + * invitation stream. + */ + InputStream createInvitationStreamReader(InputStream in, int maxFrameLength, byte[] secret, boolean alice); } diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriter.java b/briar-api/src/org/briarproject/api/transport/StreamWriter.java deleted file mode 100644 index 2684742dbdf5889fcb382c8cadbaade65b2eb7f0..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/transport/StreamWriter.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.api.transport; - -import java.io.OutputStream; - -/** Encrypts and authenticates data to be sent over an underlying transport. */ -public interface StreamWriter { - - /** - * Returns an output stream to which unencrypted, unauthenticated data can - * be written. - */ - OutputStream getOutputStream(); -} diff --git a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java index f038a75221d52f0798ab9176ed353868d281d624..04a924cc674e6f9ddc1ae7b48289d117608f670a 100644 --- a/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java +++ b/briar-api/src/org/briarproject/api/transport/StreamWriterFactory.java @@ -4,11 +4,17 @@ import java.io.OutputStream; public interface StreamWriterFactory { - /** Creates a {@link StreamWriter} for a transport connection. */ - StreamWriter createStreamWriter(OutputStream out, int maxFrameLength, + /** + * Creates an {@link java.io.OutputStream OutputStream} for writing to a + * transport stream + */ + OutputStream createStreamWriter(OutputStream out, int maxFrameLength, StreamContext ctx); - /** Creates a {@link StreamWriter} for an invitation connection. */ - StreamWriter createInvitationStreamWriter(OutputStream out, + /** + * Creates an {@link java.io.OutputStream OutputStream} for writing to an + * invitation stream. + */ + OutputStream createInvitationStreamWriter(OutputStream out, int maxFrameLength, byte[] secret, boolean alice); } diff --git a/briar-core/src/org/briarproject/crypto/CryptoModule.java b/briar-core/src/org/briarproject/crypto/CryptoModule.java index d7dd2fdb49a6798281ea8132c20dc46efe4a63e1..d4ac20c6495d50fd7c21f3c8a339a0579cb8ec9b 100644 --- a/briar-core/src/org/briarproject/crypto/CryptoModule.java +++ b/briar-core/src/org/briarproject/crypto/CryptoModule.java @@ -14,6 +14,8 @@ import javax.inject.Singleton; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.crypto.PasswordStrengthEstimator; +import org.briarproject.api.crypto.StreamDecrypterFactory; +import org.briarproject.api.crypto.StreamEncrypterFactory; import org.briarproject.api.lifecycle.LifecycleManager; import com.google.inject.AbstractModule; @@ -44,6 +46,8 @@ public class CryptoModule extends AbstractModule { CryptoComponentImpl.class).in(Singleton.class); bind(PasswordStrengthEstimator.class).to( PasswordStrengthEstimatorImpl.class); + bind(StreamDecrypterFactory.class).to(StreamDecrypterFactoryImpl.class); + bind(StreamEncrypterFactory.class).to(StreamEncrypterFactoryImpl.class); } @Provides @Singleton @CryptoExecutor diff --git a/briar-core/src/org/briarproject/transport/FrameEncoder.java b/briar-core/src/org/briarproject/crypto/FrameEncoder.java similarity index 98% rename from briar-core/src/org/briarproject/transport/FrameEncoder.java rename to briar-core/src/org/briarproject/crypto/FrameEncoder.java index 684b8754248391d6d8c01dca3b89b12ca55cf252..5a30593db4bddac8d8fb3863b087ed00621f63d3 100644 --- a/briar-core/src/org/briarproject/transport/FrameEncoder.java +++ b/briar-core/src/org/briarproject/crypto/FrameEncoder.java @@ -1,4 +1,4 @@ -package org.briarproject.transport; +package org.briarproject.crypto; import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; diff --git a/briar-core/src/org/briarproject/crypto/StreamDecrypterFactoryImpl.java b/briar-core/src/org/briarproject/crypto/StreamDecrypterFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8f04e686ae86d21b492bfc77b7df94d52eeab1db --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/StreamDecrypterFactoryImpl.java @@ -0,0 +1,42 @@ +package org.briarproject.crypto; + +import java.io.InputStream; + +import javax.inject.Inject; + +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.api.crypto.StreamDecrypterFactory; +import org.briarproject.api.transport.StreamContext; + +class StreamDecrypterFactoryImpl implements StreamDecrypterFactory { + + private final CryptoComponent crypto; + + @Inject + StreamDecrypterFactoryImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + public StreamDecrypter createStreamDecrypter(InputStream in, + int maxFrameLength, StreamContext ctx) { + byte[] secret = ctx.getSecret(); + long streamNumber = ctx.getStreamNumber(); + boolean alice = !ctx.getAlice(); + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); + // Create the decrypter + return new StreamDecrypterImpl(in, crypto.getFrameCipher(), frameKey, + maxFrameLength); + } + + public StreamDecrypter createInvitationStreamDecrypter(InputStream in, + int maxFrameLength, byte[] secret, boolean alice) { + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); + // Create the decrypter + return new StreamDecrypterImpl(in, crypto.getFrameCipher(), frameKey, + maxFrameLength); + } +} diff --git a/briar-core/src/org/briarproject/transport/IncomingEncryptionLayer.java b/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java similarity index 78% rename from briar-core/src/org/briarproject/transport/IncomingEncryptionLayer.java rename to briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java index e5e4381d8cfcccf4af9444bc798026687995edf5..ff29697dfd469ee0e829ef0b24f33f294368454b 100644 --- a/briar-core/src/org/briarproject/transport/IncomingEncryptionLayer.java +++ b/briar-core/src/org/briarproject/crypto/StreamDecrypterImpl.java @@ -1,4 +1,4 @@ -package org.briarproject.transport; +package org.briarproject.crypto; import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; @@ -13,19 +13,20 @@ import java.security.GeneralSecurityException; import org.briarproject.api.FormatException; import org.briarproject.api.crypto.AuthenticatedCipher; import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamDecrypter; -class IncomingEncryptionLayer implements FrameReader { +class StreamDecrypterImpl implements StreamDecrypter { private final InputStream in; private final AuthenticatedCipher frameCipher; private final SecretKey frameKey; - private final byte[] iv, aad, ciphertext; + private final byte[] iv, aad, plaintext, ciphertext; private final int frameLength; private long frameNumber; private boolean finalFrame; - IncomingEncryptionLayer(InputStream in, AuthenticatedCipher frameCipher, + StreamDecrypterImpl(InputStream in, AuthenticatedCipher frameCipher, SecretKey frameKey, int frameLength) { this.in = in; this.frameCipher = frameCipher; @@ -33,12 +34,13 @@ class IncomingEncryptionLayer implements FrameReader { this.frameLength = frameLength; iv = new byte[IV_LENGTH]; aad = new byte[AAD_LENGTH]; + plaintext = new byte[frameLength - MAC_LENGTH]; ciphertext = new byte[frameLength]; frameNumber = 0; finalFrame = false; } - public int readFrame(byte[] frame) throws IOException { + public int readFrame(byte[] payload) throws IOException { if(finalFrame) return -1; // Read the frame int ciphertextLength = 0; @@ -61,23 +63,25 @@ class IncomingEncryptionLayer implements FrameReader { try { frameCipher.init(false, frameKey, iv, aad); int decrypted = frameCipher.doFinal(ciphertext, 0, ciphertextLength, - frame, 0); + plaintext, 0); if(decrypted != plaintextLength) throw new RuntimeException(); } catch(GeneralSecurityException e) { throw new FormatException(); } // Decode and validate the header - finalFrame = FrameEncoder.isFinalFrame(frame); + finalFrame = FrameEncoder.isFinalFrame(plaintext); if(!finalFrame && ciphertextLength < frameLength) throw new FormatException(); - int payloadLength = FrameEncoder.getPayloadLength(frame); + int payloadLength = FrameEncoder.getPayloadLength(plaintext); if(payloadLength > plaintextLength - HEADER_LENGTH) throw new FormatException(); // If there's any padding it must be all zeroes for(int i = HEADER_LENGTH + payloadLength; i < plaintextLength; i++) { - if(frame[i] != 0) throw new FormatException(); + if(plaintext[i] != 0) throw new FormatException(); } frameNumber++; + // Copy the payload + System.arraycopy(plaintext, HEADER_LENGTH, payload, 0, payloadLength); return payloadLength; } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java b/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..979ebb17c859e2251330c3672ab7b3e459385c33 --- /dev/null +++ b/briar-core/src/org/briarproject/crypto/StreamEncrypterFactoryImpl.java @@ -0,0 +1,49 @@ +package org.briarproject.crypto; + +import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; + +import java.io.OutputStream; + +import javax.inject.Inject; + +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypter; +import org.briarproject.api.crypto.StreamEncrypterFactory; +import org.briarproject.api.transport.StreamContext; + +class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { + + private final CryptoComponent crypto; + + @Inject + StreamEncrypterFactoryImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + public StreamEncrypter createStreamEncrypter(OutputStream out, + int maxFrameLength, StreamContext ctx) { + byte[] secret = ctx.getSecret(); + long streamNumber = ctx.getStreamNumber(); + boolean alice = ctx.getAlice(); + // Encode the tag + byte[] tag = new byte[TAG_LENGTH]; + SecretKey tagKey = crypto.deriveTagKey(secret, alice); + crypto.encodeTag(tag, tagKey, streamNumber); + tagKey.erase(); + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); + // Create the encrypter + return new StreamEncrypterImpl(out, crypto.getFrameCipher(), frameKey, + maxFrameLength, tag); + } + + public StreamEncrypter createInvitationStreamEncrypter(OutputStream out, + int maxFrameLength, byte[] secret, boolean alice) { + // Derive the frame key + SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); + // Create the encrypter + return new StreamEncrypterImpl(out, crypto.getFrameCipher(), frameKey, + maxFrameLength, null); + } +} diff --git a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java b/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java similarity index 79% rename from briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java rename to briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java index 1bb90c1e8cf51d321fe8318a557052eb7d045d3a..d3e2e43ce19af8d9742270d0d6cd52a6abb33988 100644 --- a/briar-core/src/org/briarproject/transport/OutgoingEncryptionLayer.java +++ b/briar-core/src/org/briarproject/crypto/StreamEncrypterImpl.java @@ -1,4 +1,4 @@ -package org.briarproject.transport; +package org.briarproject.crypto; import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; @@ -12,19 +12,20 @@ import java.security.GeneralSecurityException; import org.briarproject.api.crypto.AuthenticatedCipher; import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypter; -class OutgoingEncryptionLayer implements FrameWriter { +class StreamEncrypterImpl implements StreamEncrypter { private final OutputStream out; private final AuthenticatedCipher frameCipher; private final SecretKey frameKey; - private final byte[] tag, iv, aad, ciphertext; + private final byte[] tag, iv, aad, plaintext, ciphertext; private final int frameLength; private long frameNumber; private boolean writeTag; - OutgoingEncryptionLayer(OutputStream out, AuthenticatedCipher frameCipher, + StreamEncrypterImpl(OutputStream out, AuthenticatedCipher frameCipher, SecretKey frameKey, int frameLength, byte[] tag) { this.out = out; this.frameCipher = frameCipher; @@ -33,13 +34,14 @@ class OutgoingEncryptionLayer implements FrameWriter { this.tag = tag; iv = new byte[IV_LENGTH]; aad = new byte[AAD_LENGTH]; + plaintext = new byte[frameLength - MAC_LENGTH]; ciphertext = new byte[frameLength]; frameNumber = 0; writeTag = (tag != null); } - public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame) - throws IOException { + public void writeFrame(byte[] payload, int payloadLength, + boolean finalFrame) throws IOException { if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException(); // Write the tag if required if(writeTag) { @@ -51,8 +53,6 @@ class OutgoingEncryptionLayer implements FrameWriter { } writeTag = false; } - // Encode the header - FrameEncoder.encodeHeader(frame, finalFrame, payloadLength); // Don't pad the final frame int plaintextLength, ciphertextLength; if(finalFrame) { @@ -62,16 +62,19 @@ class OutgoingEncryptionLayer implements FrameWriter { plaintextLength = frameLength - MAC_LENGTH; ciphertextLength = frameLength; } + // Encode the header + FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength); + // Copy the payload + System.arraycopy(payload, 0, plaintext, HEADER_LENGTH, payloadLength); // If there's any padding it must all be zeroes - for(int i = HEADER_LENGTH + payloadLength; i < plaintextLength; i++) { - frame[i] = 0; - } + for(int i = HEADER_LENGTH + payloadLength; i < plaintextLength; i++) + plaintext[i] = 0; // Encrypt and authenticate the frame FrameEncoder.encodeIv(iv, frameNumber); FrameEncoder.encodeAad(aad, frameNumber, plaintextLength); try { frameCipher.init(true, frameKey, iv, aad); - int encrypted = frameCipher.doFinal(frame, 0, plaintextLength, + int encrypted = frameCipher.doFinal(plaintext, 0, plaintextLength, ciphertext, 0); if(encrypted != ciphertextLength) throw new RuntimeException(); } catch(GeneralSecurityException badCipher) { diff --git a/briar-core/src/org/briarproject/invitation/AliceConnector.java b/briar-core/src/org/briarproject/invitation/AliceConnector.java index 9c7fd31f3aa0d0adf90501dbb6b2170954998d20..8c0bd78130b535ac9c0729d99dd84084addbfa97 100644 --- a/briar-core/src/org/briarproject/invitation/AliceConnector.java +++ b/briar-core/src/org/briarproject/invitation/AliceConnector.java @@ -29,9 +29,7 @@ import org.briarproject.api.serial.ReaderFactory; import org.briarproject.api.serial.Writer; import org.briarproject.api.serial.WriterFactory; import org.briarproject.api.system.Clock; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; /** A connection thread for the peer being Alice in the invitation protocol. */ @@ -51,9 +49,9 @@ class AliceConnector extends Connector { Map<TransportId, TransportProperties> localProps, PseudoRandom random) { super(crypto, db, readerFactory, writerFactory, streamReaderFactory, - streamWriterFactory, authorFactory, groupFactory, - keyManager, connectionManager, clock, reuseConnection, group, - plugin, localAuthor, localProps, random); + streamWriterFactory, authorFactory, groupFactory, keyManager, + connectionManager, clock, reuseConnection, group, plugin, + localAuthor, localProps, random); } @Override @@ -131,14 +129,16 @@ class AliceConnector extends Connector { if(LOG.isLoggable(INFO)) LOG.info(pluginName + " confirmation succeeded"); int maxFrameLength = conn.getReader().getMaxFrameLength(); - StreamReader streamReader = + // Create the readers + InputStream streamReader = streamReaderFactory.createInvitationStreamReader(in, maxFrameLength, secret, false); // Bob's stream - r = readerFactory.createReader(streamReader.getInputStream()); - StreamWriter streamWriter = + r = readerFactory.createReader(streamReader); + // Create the writers + OutputStream streamWriter = streamWriterFactory.createInvitationStreamWriter(out, maxFrameLength, secret, true); // Alice's stream - w = writerFactory.createWriter(streamWriter.getOutputStream()); + w = writerFactory.createWriter(streamWriter); // Derive the invitation nonces byte[][] nonces = crypto.deriveInvitationNonces(secret); byte[] aliceNonce = nonces[0], bobNonce = nonces[1]; diff --git a/briar-core/src/org/briarproject/invitation/BobConnector.java b/briar-core/src/org/briarproject/invitation/BobConnector.java index 205b3446ddbbb54d0dcb0080531318cc0d679ab1..6b435fd2a6c12e92d3d1bf3d54ec4307f871ab3b 100644 --- a/briar-core/src/org/briarproject/invitation/BobConnector.java +++ b/briar-core/src/org/briarproject/invitation/BobConnector.java @@ -29,9 +29,7 @@ import org.briarproject.api.serial.ReaderFactory; import org.briarproject.api.serial.Writer; import org.briarproject.api.serial.WriterFactory; import org.briarproject.api.system.Clock; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; /** A connection thread for the peer being Bob in the invitation protocol. */ @@ -51,9 +49,9 @@ class BobConnector extends Connector { Map<TransportId, TransportProperties> localProps, PseudoRandom random) { super(crypto, db, readerFactory, writerFactory, streamReaderFactory, - streamWriterFactory, authorFactory, groupFactory, - keyManager, connectionManager, clock, reuseConnection, group, - plugin, localAuthor, localProps, random); + streamWriterFactory, authorFactory, groupFactory, keyManager, + connectionManager, clock, reuseConnection, group, plugin, + localAuthor, localProps, random); } @Override @@ -131,14 +129,16 @@ class BobConnector extends Connector { if(LOG.isLoggable(INFO)) LOG.info(pluginName + " confirmation succeeded"); int maxFrameLength = conn.getReader().getMaxFrameLength(); - StreamReader streamReader = + // Create the readers + InputStream streamReader = streamReaderFactory.createInvitationStreamReader(in, maxFrameLength, secret, true); // Alice's stream - r = readerFactory.createReader(streamReader.getInputStream()); - StreamWriter streamWriter = + r = readerFactory.createReader(streamReader); + // Create the writers + OutputStream streamWriter = streamWriterFactory.createInvitationStreamWriter(out, maxFrameLength, secret, false); // Bob's stream - w = writerFactory.createWriter(streamWriter.getOutputStream()); + w = writerFactory.createWriter(streamWriter); // Derive the nonces byte[][] nonces = crypto.deriveInvitationNonces(secret); byte[] aliceNonce = nonces[0], bobNonce = nonces[1]; diff --git a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java index 9dad32f2e28a8f6f2b443d768147098528af9844..b72efb688db1d08dff6ea213022ccfa01ef1d7be 100644 --- a/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java +++ b/briar-core/src/org/briarproject/plugins/ConnectionManagerImpl.java @@ -6,6 +6,7 @@ import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -24,9 +25,7 @@ import org.briarproject.api.plugins.TransportConnectionReader; import org.briarproject.api.plugins.TransportConnectionWriter; import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.TagRecogniser; import org.briarproject.util.ByteUtils; @@ -97,11 +96,10 @@ class ConnectionManagerImpl implements ConnectionManager { private MessagingSession createIncomingSession(StreamContext ctx, TransportConnectionReader r) throws IOException { try { - StreamReader streamReader = streamReaderFactory.createStreamReader( + InputStream streamReader = streamReaderFactory.createStreamReader( r.getInputStream(), r.getMaxFrameLength(), ctx); return messagingSessionFactory.createIncomingSession( - ctx.getContactId(), ctx.getTransportId(), - streamReader.getInputStream()); + ctx.getContactId(), ctx.getTransportId(), streamReader); } finally { ByteUtils.erase(ctx.getSecret()); } @@ -110,11 +108,11 @@ class ConnectionManagerImpl implements ConnectionManager { private MessagingSession createSimplexOutgoingSession(StreamContext ctx, TransportConnectionWriter w) throws IOException { try { - StreamWriter streamWriter = streamWriterFactory.createStreamWriter( + OutputStream streamWriter = streamWriterFactory.createStreamWriter( w.getOutputStream(), w.getMaxFrameLength(), ctx); return messagingSessionFactory.createSimplexOutgoingSession( ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), - streamWriter.getOutputStream()); + streamWriter); } finally { ByteUtils.erase(ctx.getSecret()); } @@ -123,11 +121,11 @@ class ConnectionManagerImpl implements ConnectionManager { private MessagingSession createDuplexOutgoingSession(StreamContext ctx, TransportConnectionWriter w) throws IOException { try { - StreamWriter streamWriter = streamWriterFactory.createStreamWriter( + OutputStream streamWriter = streamWriterFactory.createStreamWriter( w.getOutputStream(), w.getMaxFrameLength(), ctx); return messagingSessionFactory.createDuplexOutgoingSession( ctx.getContactId(), ctx.getTransportId(), w.getMaxLatency(), - w.getMaxIdleTime(), streamWriter.getOutputStream()); + w.getMaxIdleTime(), streamWriter); } finally { ByteUtils.erase(ctx.getSecret()); } diff --git a/briar-core/src/org/briarproject/transport/FrameReader.java b/briar-core/src/org/briarproject/transport/FrameReader.java deleted file mode 100644 index 8284c47b8d4e322a8f18cc57ae071d8f2bef97c5..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/transport/FrameReader.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.briarproject.transport; - -import java.io.IOException; - -interface FrameReader { - - /** - * Reads a frame into the given buffer and returns its payload length, or - * -1 if no more frames can be read from the connection. - */ - int readFrame(byte[] frame) throws IOException; -} diff --git a/briar-core/src/org/briarproject/transport/FrameWriter.java b/briar-core/src/org/briarproject/transport/FrameWriter.java deleted file mode 100644 index 4f29b7999a08fa1c7964fc65140124232f0b9028..0000000000000000000000000000000000000000 --- a/briar-core/src/org/briarproject/transport/FrameWriter.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.transport; - -import java.io.IOException; - -interface FrameWriter { - - /** Writes the given frame. */ - void writeFrame(byte[] frame, int payloadLength, boolean finalFrame) - throws IOException; - - /** Flushes the stream. */ - void flush() throws IOException; -} diff --git a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java index d71bfc50ac24c8cc72009a9df6689abee11ddfaa..daa4f2bb769f93d9da0664cb7b67e8b718ebc5e0 100644 --- a/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamReaderFactoryImpl.java @@ -4,37 +4,32 @@ import java.io.InputStream; import javax.inject.Inject; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.api.crypto.StreamDecrypterFactory; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; class StreamReaderFactoryImpl implements StreamReaderFactory { - private final CryptoComponent crypto; + private final StreamDecrypterFactory streamDecrypterFactory; @Inject - StreamReaderFactoryImpl(CryptoComponent crypto) { - this.crypto = crypto; + StreamReaderFactoryImpl(StreamDecrypterFactory streamDecrypterFactory) { + this.streamDecrypterFactory = streamDecrypterFactory; } - public StreamReader createStreamReader(InputStream in, - int maxFrameLength, StreamContext ctx) { - byte[] secret = ctx.getSecret(); - long streamNumber = ctx.getStreamNumber(); - boolean alice = !ctx.getAlice(); - SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); - FrameReader frameReader = new IncomingEncryptionLayer(in, - crypto.getFrameCipher(), frameKey, maxFrameLength); - return new StreamReaderImpl(frameReader, maxFrameLength); + public InputStream createStreamReader(InputStream in, int maxFrameLength, + StreamContext ctx) { + StreamDecrypter s = streamDecrypterFactory.createStreamDecrypter(in, + maxFrameLength, ctx); + return new StreamReaderImpl(s, maxFrameLength); } - public StreamReader createInvitationStreamReader(InputStream in, + public InputStream createInvitationStreamReader(InputStream in, int maxFrameLength, byte[] secret, boolean alice) { - SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); - FrameReader frameReader = new IncomingEncryptionLayer(in, - crypto.getFrameCipher(), frameKey, maxFrameLength); - return new StreamReaderImpl(frameReader, maxFrameLength); + StreamDecrypter s = + streamDecrypterFactory.createInvitationStreamDecrypter(in, + maxFrameLength, secret, alice); + return new StreamReaderImpl(s, maxFrameLength); } } diff --git a/briar-core/src/org/briarproject/transport/StreamReaderImpl.java b/briar-core/src/org/briarproject/transport/StreamReaderImpl.java index 0de048a6fa67f805425a645c01affdc8fbd46cf0..e9a30b24a96d5bd871087c49f4bcf5aef78b9ffc 100644 --- a/briar-core/src/org/briarproject/transport/StreamReaderImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamReaderImpl.java @@ -6,22 +6,18 @@ import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import java.io.IOException; import java.io.InputStream; -import org.briarproject.api.transport.StreamReader; +import org.briarproject.api.crypto.StreamDecrypter; -class StreamReaderImpl extends InputStream implements StreamReader { +class StreamReaderImpl extends InputStream { - private final FrameReader in; - private final byte[] frame; + private final StreamDecrypter decrypter; + private final byte[] payload; private int offset = 0, length = 0; - StreamReaderImpl(FrameReader in, int frameLength) { - this.in = in; - frame = new byte[frameLength - MAC_LENGTH]; - } - - public InputStream getInputStream() { - return this; + StreamReaderImpl(StreamDecrypter decrypter, int frameLength) { + this.decrypter = decrypter; + payload = new byte[frameLength - HEADER_LENGTH - MAC_LENGTH]; } @Override @@ -30,7 +26,7 @@ class StreamReaderImpl extends InputStream implements StreamReader { if(length == -1) return -1; readFrame(); } - int b = frame[offset] & 0xff; + int b = payload[offset] & 0xff; offset++; length--; return b; @@ -48,7 +44,7 @@ class StreamReaderImpl extends InputStream implements StreamReader { readFrame(); } len = Math.min(len, length); - System.arraycopy(frame, offset, b, off, len); + System.arraycopy(payload, offset, b, off, len); offset += len; length -= len; return len; @@ -56,7 +52,7 @@ class StreamReaderImpl extends InputStream implements StreamReader { private void readFrame() throws IOException { assert length == 0; - offset = HEADER_LENGTH; - length = in.readFrame(frame); + offset = 0; + length = decrypter.readFrame(payload); } } diff --git a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java index 638ecdff03d2c162eec5ff323876f24db65cb5a0..f57a8f69bf96c413bbae7f88bbbf9ae5523b905f 100644 --- a/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamWriterFactoryImpl.java @@ -1,46 +1,35 @@ package org.briarproject.transport; -import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; - import java.io.OutputStream; import javax.inject.Inject; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.crypto.StreamEncrypter; +import org.briarproject.api.crypto.StreamEncrypterFactory; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; class StreamWriterFactoryImpl implements StreamWriterFactory { - private final CryptoComponent crypto; + private final StreamEncrypterFactory streamEncrypterFactory; @Inject - StreamWriterFactoryImpl(CryptoComponent crypto) { - this.crypto = crypto; + StreamWriterFactoryImpl(StreamEncrypterFactory streamEncrypterFactory) { + this.streamEncrypterFactory = streamEncrypterFactory; } - public StreamWriter createStreamWriter(OutputStream out, - int maxFrameLength, StreamContext ctx) { - byte[] secret = ctx.getSecret(); - long streamNumber = ctx.getStreamNumber(); - boolean alice = ctx.getAlice(); - byte[] tag = new byte[TAG_LENGTH]; - SecretKey tagKey = crypto.deriveTagKey(secret, alice); - crypto.encodeTag(tag, tagKey, streamNumber); - tagKey.erase(); - SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice); - FrameWriter frameWriter = new OutgoingEncryptionLayer(out, - crypto.getFrameCipher(), frameKey, maxFrameLength, tag); - return new StreamWriterImpl(frameWriter, maxFrameLength); + public OutputStream createStreamWriter(OutputStream out, int maxFrameLength, + StreamContext ctx) { + StreamEncrypter s = streamEncrypterFactory.createStreamEncrypter(out, + maxFrameLength, ctx); + return new StreamWriterImpl(s, maxFrameLength); } - public StreamWriter createInvitationStreamWriter(OutputStream out, + public OutputStream createInvitationStreamWriter(OutputStream out, int maxFrameLength, byte[] secret, boolean alice) { - SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice); - FrameWriter frameWriter = new OutgoingEncryptionLayer(out, - crypto.getFrameCipher(), frameKey, maxFrameLength, null); - return new StreamWriterImpl(frameWriter, maxFrameLength); + StreamEncrypter s = + streamEncrypterFactory.createInvitationStreamEncrypter(out, + maxFrameLength, secret, alice); + return new StreamWriterImpl(s, maxFrameLength); } } \ No newline at end of file diff --git a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java index 7e38809a8baa6b8c440a9e801153c844e581d54a..05653fd42a4754b005d6b49e9e3b8617876a64ec 100644 --- a/briar-core/src/org/briarproject/transport/StreamWriterImpl.java +++ b/briar-core/src/org/briarproject/transport/StreamWriterImpl.java @@ -6,7 +6,7 @@ import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import java.io.IOException; import java.io.OutputStream; -import org.briarproject.api.transport.StreamWriter; +import org.briarproject.api.crypto.StreamEncrypter; /** * A {@link org.briarproject.api.transport.StreamWriter StreamWriter} that @@ -15,43 +15,36 @@ import org.briarproject.api.transport.StreamWriter; * <p> * This class is not thread-safe. */ -class StreamWriterImpl extends OutputStream implements StreamWriter { +class StreamWriterImpl extends OutputStream { - private final FrameWriter out; - private final byte[] frame; - private final int frameLength; + private final StreamEncrypter encrypter; + private final byte[] payload; private int length = 0; - StreamWriterImpl(FrameWriter out, int frameLength) { - this.out = out; - this.frameLength = frameLength; - frame = new byte[frameLength - MAC_LENGTH]; - } - - public OutputStream getOutputStream() { - return this; + StreamWriterImpl(StreamEncrypter encrypter, int maxFrameLength) { + this.encrypter = encrypter; + payload = new byte[maxFrameLength - HEADER_LENGTH - MAC_LENGTH]; } @Override public void close() throws IOException { writeFrame(true); - out.flush(); + encrypter.flush(); super.close(); } @Override public void flush() throws IOException { writeFrame(false); - out.flush(); + encrypter.flush(); } @Override public void write(int b) throws IOException { - frame[HEADER_LENGTH + length] = (byte) b; + payload[length] = (byte) b; length++; - if(HEADER_LENGTH + length + MAC_LENGTH == frameLength) - writeFrame(false); + if(length == payload.length) writeFrame(false); } @Override @@ -61,21 +54,21 @@ class StreamWriterImpl extends OutputStream implements StreamWriter { @Override public void write(byte[] b, int off, int len) throws IOException { - int available = frameLength - HEADER_LENGTH - length - MAC_LENGTH; + int available = payload.length - length; while(available <= len) { - System.arraycopy(b, off, frame, HEADER_LENGTH + length, available); + System.arraycopy(b, off, payload, length, available); length += available; writeFrame(false); off += available; len -= available; - available = frameLength - HEADER_LENGTH - length - MAC_LENGTH; + available = payload.length - length; } - System.arraycopy(b, off, frame, HEADER_LENGTH + length, len); + System.arraycopy(b, off, payload, length, len); length += len; } private void writeFrame(boolean finalFrame) throws IOException { - out.writeFrame(frame, length, finalFrame); + encrypter.writeFrame(payload, length, finalFrame); length = 0; } } diff --git a/briar-tests/build.xml b/briar-tests/build.xml index 2ab788bba3b4ecc03415ed0f4aabe0343e183b1f..561a3fbe55f269efd55d54370c17649efb808a7d 100644 --- a/briar-tests/build.xml +++ b/briar-tests/build.xml @@ -102,6 +102,8 @@ <test name="org.briarproject.crypto.PasswordBasedKdfTest"/> <test name="org.briarproject.crypto.PasswordStrengthEstimatorTest"/> <test name='org.briarproject.crypto.SecretKeyImplTest'/> + <test name='org.briarproject.crypto.StreamDecrypterImplTest'/> + <test name='org.briarproject.crypto.StreamEncrypterImplTest'/> <test name='org.briarproject.db.BasicH2Test'/> <test name='org.briarproject.db.DatabaseCleanerImplTest'/> <test name='org.briarproject.db.DatabaseComponentImplTest'/> @@ -126,10 +128,8 @@ <test name='org.briarproject.serial.ReaderImplTest'/> <test name='org.briarproject.serial.WriterImplTest'/> <test name='org.briarproject.system.LinuxSeedProviderTest'/> - <test name='org.briarproject.transport.IncomingEncryptionLayerTest'/> <test name='org.briarproject.transport.KeyManagerImplTest'/> <test name='org.briarproject.transport.KeyRotationIntegrationTest'/> - <test name='org.briarproject.transport.OutgoingEncryptionLayerTest'/> <test name='org.briarproject.transport.ReorderingWindowTest'/> <test name='org.briarproject.transport.StreamReaderImplTest'/> <test name='org.briarproject.transport.StreamWriterImplTest'/> diff --git a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java index cebd634d4d63631403419599edaf4a15af47edd3..a3983b5a2eaf490f3030f72db9e9371a80fb9117 100644 --- a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java +++ b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertArrayEquals; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -36,9 +37,7 @@ import org.briarproject.api.messaging.SubscriptionUpdate; import org.briarproject.api.messaging.TransportUpdate; import org.briarproject.api.messaging.UnverifiedMessage; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.crypto.CryptoModule; import org.briarproject.db.DatabaseModule; @@ -119,10 +118,10 @@ public class ProtocolIntegrationTest extends BriarTestCase { ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamContext ctx = new StreamContext(contactId, transportId, secret.clone(), 0, true); - StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, + OutputStream streamWriter = streamWriterFactory.createStreamWriter(out, MAX_FRAME_LENGTH, ctx); PacketWriter packetWriter = packetWriterFactory.createPacketWriter( - streamWriter.getOutputStream()); + streamWriter); packetWriter.writeAck(new Ack(messageIds)); @@ -140,7 +139,7 @@ public class ProtocolIntegrationTest extends BriarTestCase { transportProperties, 1); packetWriter.writeTransportUpdate(tu); - streamWriter.getOutputStream().flush(); + streamWriter.flush(); return out.toByteArray(); } @@ -151,10 +150,10 @@ public class ProtocolIntegrationTest extends BriarTestCase { // FIXME: Check that the expected tag was received StreamContext ctx = new StreamContext(contactId, transportId, secret.clone(), 0, false); - StreamReader streamReader = streamReaderFactory.createStreamReader(in, + InputStream streamReader = streamReaderFactory.createStreamReader(in, MAX_FRAME_LENGTH, ctx); PacketReader packetReader = packetReaderFactory.createPacketReader( - streamReader.getInputStream()); + streamReader); // Read the ack assertTrue(packetReader.hasAck()); diff --git a/briar-tests/src/org/briarproject/transport/IncomingEncryptionLayerTest.java b/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java similarity index 80% rename from briar-tests/src/org/briarproject/transport/IncomingEncryptionLayerTest.java rename to briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java index 8e1791785632439f24962584d36c03e6d41f06de..1ee05436a6af717656b71924aa968d8697ea4307 100644 --- a/briar-tests/src/org/briarproject/transport/IncomingEncryptionLayerTest.java +++ b/briar-tests/src/org/briarproject/crypto/StreamDecrypterImplTest.java @@ -1,4 +1,4 @@ -package org.briarproject.transport; +package org.briarproject.crypto; import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; @@ -14,13 +14,12 @@ import org.briarproject.api.FormatException; import org.briarproject.api.crypto.AuthenticatedCipher; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.SecretKey; -import org.briarproject.crypto.CryptoModule; import org.junit.Test; import com.google.inject.Guice; import com.google.inject.Injector; -public class IncomingEncryptionLayerTest extends BriarTestCase { +public class StreamDecrypterImplTest extends BriarTestCase { // FIXME: This is an integration test, not a unit test @@ -32,7 +31,7 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { private final AuthenticatedCipher frameCipher; private final SecretKey frameKey; - public IncomingEncryptionLayerTest() { + public StreamDecrypterImplTest() { Injector i = Guice.createInjector(new CryptoModule(), new TestLifecycleModule(), new TestSystemModule()); crypto = i.getInstance(CryptoComponent.class); @@ -51,11 +50,11 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { System.arraycopy(frame1, 0, valid, FRAME_LENGTH, FRAME_LENGTH); // Read the frames ByteArrayInputStream in = new ByteArrayInputStream(valid); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); - byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; - assertEquals(123, i.readFrame(buf)); - assertEquals(123, i.readFrame(buf)); + byte[] payload = new byte[MAX_PAYLOAD_LENGTH]; + assertEquals(123, i.readFrame(payload)); + assertEquals(123, i.readFrame(payload)); } @Test @@ -67,10 +66,10 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { System.arraycopy(frame, 0, truncated, 0, FRAME_LENGTH - 1); // Try to read the frame, which should fail due to truncation ByteArrayInputStream in = new ByteArrayInputStream(truncated); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + i.readFrame(new byte[MAX_PAYLOAD_LENGTH]); fail(); } catch(FormatException expected) {} } @@ -83,10 +82,10 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { frame[(int) (Math.random() * FRAME_LENGTH)] ^= 1; // Try to read the frame, which should fail due to modification ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + i.readFrame(new byte[MAX_PAYLOAD_LENGTH]); fail(); } catch(FormatException expected) {} } @@ -97,10 +96,10 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { byte[] frame = generateFrame(0, FRAME_LENGTH - 1, 123, false, false); // Try to read the frame, which should fail due to invalid length ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + i.readFrame(new byte[MAX_PAYLOAD_LENGTH]); fail(); } catch(FormatException expected) {} } @@ -111,9 +110,9 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { byte[] frame = generateFrame(0, FRAME_LENGTH - 1, 123, true, false); // Read the frame ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); - int length = i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + int length = i.readFrame(new byte[MAX_PAYLOAD_LENGTH]); assertEquals(123, length); } @@ -124,10 +123,10 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { false, false); // Try to read the frame, which should fail due to invalid length ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + i.readFrame(new byte[MAX_PAYLOAD_LENGTH]); fail(); } catch(FormatException expected) {} } @@ -138,10 +137,10 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { byte[] frame = generateFrame(0, FRAME_LENGTH, 123, false, true); // Try to read the frame, which should fail due to bad padding ByteArrayInputStream in = new ByteArrayInputStream(frame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); try { - i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + i.readFrame(new byte[MAX_PAYLOAD_LENGTH]); fail(); } catch(FormatException expected) {} } @@ -158,12 +157,12 @@ public class IncomingEncryptionLayerTest extends BriarTestCase { System.arraycopy(frame1, 0, extraFrame, FRAME_LENGTH, FRAME_LENGTH); // Read the final frame, which should first read the tag ByteArrayInputStream in = new ByteArrayInputStream(extraFrame); - IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + StreamDecrypterImpl i = new StreamDecrypterImpl(in, frameCipher, frameKey, FRAME_LENGTH); - byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; - assertEquals(MAX_PAYLOAD_LENGTH, i.readFrame(buf)); + byte[] payload = new byte[MAX_PAYLOAD_LENGTH]; + assertEquals(MAX_PAYLOAD_LENGTH, i.readFrame(payload)); // The frame after the final frame should not be read - assertEquals(-1, i.readFrame(buf)); + assertEquals(-1, i.readFrame(payload)); } private byte[] generateFrame(long frameNumber, int frameLength, diff --git a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java similarity index 88% rename from briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java rename to briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java index d9b7340335a1afb4fe960525e593d5104f44ff6e..d4f8b33a5b767f1fb128594fc551884ef2808145 100644 --- a/briar-tests/src/org/briarproject/transport/OutgoingEncryptionLayerTest.java +++ b/briar-tests/src/org/briarproject/crypto/StreamEncrypterImplTest.java @@ -1,4 +1,4 @@ -package org.briarproject.transport; +package org.briarproject.crypto; import static org.briarproject.api.transport.TransportConstants.AAD_LENGTH; import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; @@ -15,13 +15,12 @@ import org.briarproject.TestSystemModule; import org.briarproject.api.crypto.AuthenticatedCipher; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.SecretKey; -import org.briarproject.crypto.CryptoModule; import org.junit.Test; import com.google.inject.Guice; import com.google.inject.Injector; -public class OutgoingEncryptionLayerTest extends BriarTestCase { +public class StreamEncrypterImplTest extends BriarTestCase { // FIXME: This is an integration test, not a unit test @@ -30,7 +29,7 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { private final CryptoComponent crypto; private final AuthenticatedCipher frameCipher; - public OutgoingEncryptionLayerTest() { + public StreamEncrypterImplTest() { Injector i = Guice.createInjector(new CryptoModule(), new TestLifecycleModule(), new TestSystemModule()); crypto = i.getInstance(CryptoComponent.class); @@ -52,9 +51,9 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); // Check that the actual ciphertext matches what's expected ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + StreamEncrypterImpl o = new StreamEncrypterImpl(out, frameCipher, frameKey, FRAME_LENGTH, null); - o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); + o.writeFrame(new byte[payloadLength], payloadLength, false); byte[] actual = out.toByteArray(); assertEquals(FRAME_LENGTH, actual.length); for(int i = 0; i < FRAME_LENGTH; i++) @@ -78,9 +77,9 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); // Check that the actual tag and ciphertext match what's expected ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + StreamEncrypterImpl o = new StreamEncrypterImpl(out, frameCipher, frameKey, FRAME_LENGTH, tag); - o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); + o.writeFrame(new byte[payloadLength], payloadLength, false); byte[] actual = out.toByteArray(); assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length); for(int i = 0; i < TAG_LENGTH; i++) assertEquals(tag[i], actual[i]); @@ -94,7 +93,7 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase { new Random().nextBytes(tag); ByteArrayOutputStream out = new ByteArrayOutputStream(); // Initiator's constructor - OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + StreamEncrypterImpl o = new StreamEncrypterImpl(out, frameCipher, crypto.generateSecretKey(), FRAME_LENGTH, tag); // Write an empty final frame without having written any other frames o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); diff --git a/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java index 7a24f0c94141d812b1f80eef88d52744bd79feb2..3bce2a6e39d6b004dfa4daaa3feae094bf1b7ad0 100644 --- a/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java +++ b/briar-tests/src/org/briarproject/messaging/SimplexMessagingIntegrationTest.java @@ -9,6 +9,8 @@ import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Random; import org.briarproject.BriarTestCase; @@ -39,9 +41,7 @@ import org.briarproject.api.messaging.PacketWriter; import org.briarproject.api.messaging.PacketWriterFactory; import org.briarproject.api.transport.Endpoint; import org.briarproject.api.transport.StreamContext; -import org.briarproject.api.transport.StreamReader; import org.briarproject.api.transport.StreamReaderFactory; -import org.briarproject.api.transport.StreamWriter; import org.briarproject.api.transport.StreamWriterFactory; import org.briarproject.api.transport.TagRecogniser; import org.briarproject.crypto.CryptoModule; @@ -136,26 +136,27 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { Message message = messageFactory.createAnonymousMessage(null, group, contentType, timestamp, body); db.addLocalMessage(message); + // Get a stream context + StreamContext ctx = keyManager.getStreamContext(contactId, transportId); + assertNotNull(ctx); // Create a stream writer ByteArrayOutputStream out = new ByteArrayOutputStream(); StreamWriterFactory streamWriterFactory = alice.getInstance(StreamWriterFactory.class); - StreamContext ctx = keyManager.getStreamContext(contactId, transportId); - assertNotNull(ctx); - StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out, + OutputStream streamWriter = streamWriterFactory.createStreamWriter(out, MAX_FRAME_LENGTH, ctx); // Create an outgoing messaging session EventBus eventBus = alice.getInstance(EventBus.class); PacketWriterFactory packetWriterFactory = alice.getInstance(PacketWriterFactory.class); PacketWriter packetWriter = packetWriterFactory.createPacketWriter( - streamWriter.getOutputStream()); + streamWriter); MessagingSession session = new SimplexOutgoingSession(db, new ImmediateExecutor(), eventBus, contactId, transportId, MAX_LATENCY, packetWriter); // Write whatever needs to be written session.run(); - streamWriter.getOutputStream().close(); + streamWriter.close(); // Clean up keyManager.stop(); db.close(); @@ -204,7 +205,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { // Create a stream reader StreamReaderFactory streamReaderFactory = bob.getInstance(StreamReaderFactory.class); - StreamReader streamReader = streamReaderFactory.createStreamReader(in, + InputStream streamReader = streamReaderFactory.createStreamReader(in, MAX_FRAME_LENGTH, ctx); // Create an incoming messaging session EventBus eventBus = bob.getInstance(EventBus.class); @@ -213,7 +214,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { PacketReaderFactory packetReaderFactory = bob.getInstance(PacketReaderFactory.class); PacketReader packetReader = packetReaderFactory.createPacketReader( - streamReader.getInputStream()); + streamReader); MessagingSession session = new IncomingSession(db, new ImmediateExecutor(), new ImmediateExecutor(), eventBus, messageVerifier, contactId, transportId, packetReader); @@ -221,7 +222,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase { assertFalse(listener.messageAdded); // Read whatever needs to be read session.run(); - streamReader.getInputStream().close(); + streamReader.close(); // The private message from Alice should have been added assertTrue(listener.messageAdded); // Clean up diff --git a/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java b/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java index 67337a50ca4fca64e76f3ffa6b82fd54450e84ce..988299852d0506172aebf4280feedeb6e8148789 100644 --- a/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java +++ b/briar-tests/src/org/briarproject/transport/StreamReaderImplTest.java @@ -4,6 +4,7 @@ import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import org.briarproject.BriarTestCase; +import org.briarproject.api.crypto.StreamDecrypter; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; @@ -17,18 +18,18 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testEmptyFramesAreSkipped() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(2)); // Non-empty frame with two payload bytes - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter, FRAME_LENGTH); assertEquals(0, r.read()); // Skip the first empty frame, read a byte assertEquals(0, r.read()); // Read another byte assertEquals(-1, r.read()); // Skip the second empty frame, reach EOF @@ -40,18 +41,18 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testEmptyFramesAreSkippedWithBuffer() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(2)); // Non-empty frame with two payload bytes - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(0)); // Empty frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter, FRAME_LENGTH); byte[] buf = new byte[MAX_PAYLOAD_LENGTH]; // Skip the first empty frame, read the two payload bytes assertEquals(2, r.read(buf)); @@ -66,14 +67,14 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testMultipleReadsPerFrame() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter, FRAME_LENGTH); byte[] buf = new byte[MAX_PAYLOAD_LENGTH / 2]; // Read the first half of the payload assertEquals(MAX_PAYLOAD_LENGTH / 2, r.read(buf)); @@ -88,14 +89,14 @@ public class StreamReaderImplTest extends BriarTestCase { @Test public void testMultipleReadsPerFrameWithOffsets() throws Exception { Mockery context = new Mockery(); - final FrameReader reader = context.mock(FrameReader.class); + final StreamDecrypter decrypter = context.mock(StreamDecrypter.class); context.checking(new Expectations() {{ - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame - oneOf(reader).readFrame(with(any(byte[].class))); + oneOf(decrypter).readFrame(with(any(byte[].class))); will(returnValue(-1)); // No more frames }}); - StreamReaderImpl r = new StreamReaderImpl(reader, FRAME_LENGTH); + StreamReaderImpl r = new StreamReaderImpl(decrypter, FRAME_LENGTH); byte[] buf = new byte[MAX_PAYLOAD_LENGTH]; // Read the first half of the payload assertEquals(MAX_PAYLOAD_LENGTH / 2, r.read(buf, MAX_PAYLOAD_LENGTH / 2, diff --git a/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java b/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java index 708769b2cded12d94da47794678553df3e52003f..688621bd1511766e0bc8f7bf6f1ff1a170e4c385 100644 --- a/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java +++ b/briar-tests/src/org/briarproject/transport/StreamWriterImplTest.java @@ -4,6 +4,7 @@ import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; import org.briarproject.BriarTestCase; +import org.briarproject.api.crypto.StreamEncrypter; import org.jmock.Expectations; import org.jmock.Mockery; import org.junit.Test; @@ -17,15 +18,15 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testCloseWithoutWritingWritesFinalFrame() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); context.checking(new Expectations() {{ // Write an empty final frame - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), with(true)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + StreamWriterImpl w = new StreamWriterImpl(encrypter, FRAME_LENGTH); w.close(); context.assertIsSatisfied(); } @@ -34,14 +35,14 @@ public class StreamWriterImplTest extends BriarTestCase { public void testFlushWithoutBufferedDataWritesFrameAndFlushes() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter, FRAME_LENGTH); context.checking(new Expectations() {{ // Write a non-final frame with an empty payload - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), with(false)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.flush(); context.assertIsSatisfied(); @@ -49,9 +50,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), with(true)); - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -61,14 +62,14 @@ public class StreamWriterImplTest extends BriarTestCase { public void testFlushWithBufferedDataWritesFrameAndFlushes() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter, FRAME_LENGTH); context.checking(new Expectations() {{ // Write a non-final frame with one payload byte - oneOf(writer).writeFrame(with(any(byte[].class)), with(1), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(1), with(false)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.write(0); w.flush(); @@ -77,9 +78,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), with(true)); - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -88,11 +89,11 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testSingleByteWritesWriteFullFrame() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter, FRAME_LENGTH); context.checking(new Expectations() {{ // Write a full non-final frame - oneOf(writer).writeFrame(with(any(byte[].class)), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(MAX_PAYLOAD_LENGTH), with(false)); }}); for(int i = 0; i < MAX_PAYLOAD_LENGTH; i++) { @@ -103,9 +104,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), with(true)); - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -114,11 +115,11 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testMultiByteWritesWriteFullFrames() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter, FRAME_LENGTH); context.checking(new Expectations() {{ // Write two full non-final frames - exactly(2).of(writer).writeFrame(with(any(byte[].class)), + exactly(2).of(encrypter).writeFrame(with(any(byte[].class)), with(MAX_PAYLOAD_LENGTH), with(false)); }}); // Sanity check @@ -134,9 +135,9 @@ public class StreamWriterImplTest extends BriarTestCase { // Clean up context.checking(new Expectations() {{ // Closing the writer writes a final frame and flushes again - oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(0), with(true)); - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); w.close(); context.assertIsSatisfied(); @@ -145,17 +146,17 @@ public class StreamWriterImplTest extends BriarTestCase { @Test public void testLargeMultiByteWriteWritesFullFrames() throws Exception { Mockery context = new Mockery(); - final FrameWriter writer = context.mock(FrameWriter.class); - StreamWriterImpl w = new StreamWriterImpl(writer, FRAME_LENGTH); + final StreamEncrypter encrypter = context.mock(StreamEncrypter.class); + StreamWriterImpl w = new StreamWriterImpl(encrypter, FRAME_LENGTH); context.checking(new Expectations() {{ // Write two full non-final frames - exactly(2).of(writer).writeFrame(with(any(byte[].class)), + exactly(2).of(encrypter).writeFrame(with(any(byte[].class)), with(MAX_PAYLOAD_LENGTH), with(false)); // Write a final frame with a one-byte payload - oneOf(writer).writeFrame(with(any(byte[].class)), with(1), + oneOf(encrypter).writeFrame(with(any(byte[].class)), with(1), with(true)); // Flush the stream - oneOf(writer).flush(); + oneOf(encrypter).flush(); }}); // Write two full payloads using one large multi-byte write byte[] b = new byte[MAX_PAYLOAD_LENGTH * 2 + 1]; diff --git a/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java b/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..b0438801e6beb25915c8566418921e6d04d6e9a7 --- /dev/null +++ b/briar-tests/src/org/briarproject/transport/TestStreamDecrypter.java @@ -0,0 +1,44 @@ +package org.briarproject.transport; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.briarproject.api.FormatException; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.util.ByteUtils; + +class TestStreamDecrypter implements StreamDecrypter { + + private final InputStream in; + private final byte[] frame; + + TestStreamDecrypter(InputStream in, int frameLength) { + this.in = in; + frame = new byte[frameLength]; + } + + public int readFrame(byte[] payload) throws IOException { + int offset = 0; + while(offset < HEADER_LENGTH) { + int read = in.read(frame, offset, HEADER_LENGTH - offset); + if(read == -1) throw new EOFException(); + offset += read; + } + boolean finalFrame = (frame[0] & 0x80) == 0x80; + int payloadLength = ByteUtils.readUint16(frame, 0) & 0x7FFF; + while(offset < frame.length) { + int read = in.read(frame, offset, frame.length - offset); + if(read == -1) break; + offset += read; + } + if(!finalFrame && offset < frame.length) throw new EOFException(); + if(offset < HEADER_LENGTH + payloadLength + MAC_LENGTH) + throw new FormatException(); + System.arraycopy(frame, HEADER_LENGTH, payload, 0, payloadLength); + return payloadLength; + } +} diff --git a/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java b/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java new file mode 100644 index 0000000000000000000000000000000000000000..4ad4c667c6203ef7d0a7d01f9c56c5238f5d986f --- /dev/null +++ b/briar-tests/src/org/briarproject/transport/TestStreamEncrypter.java @@ -0,0 +1,44 @@ +package org.briarproject.transport; + +import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH; +import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH; + +import java.io.IOException; +import java.io.OutputStream; + +import org.briarproject.api.crypto.StreamEncrypter; +import org.briarproject.util.ByteUtils; + +class TestStreamEncrypter implements StreamEncrypter { + + private final OutputStream out; + private final byte[] tag, frame; + + private boolean writeTag = true; + + TestStreamEncrypter(OutputStream out, int frameLength, byte[] tag) { + this.out = out; + this.tag = tag; + frame = new byte[frameLength]; + } + + public void writeFrame(byte[] payload, int payloadLength, + boolean finalFrame) throws IOException { + if(writeTag) { + out.write(tag); + writeTag = false; + } + ByteUtils.writeUint16(payloadLength, frame, 0); + if(finalFrame) frame[0] |= 0x80; + System.arraycopy(payload, 0, frame, HEADER_LENGTH, payloadLength); + for(int i = HEADER_LENGTH + payloadLength; i < frame.length; i++) + frame[i] = 0; + if(finalFrame) + out.write(frame, 0, HEADER_LENGTH + payloadLength + MAC_LENGTH); + else out.write(frame, 0, frame.length); + } + + public void flush() throws IOException { + out.flush(); + } +} diff --git a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java index 19153db75a48546db0f4f12a6c827568ffd7511c..f55e5b6e1a8ef83fc5d504ca5a5ef79fe6fb2a1b 100644 --- a/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java +++ b/briar-tests/src/org/briarproject/transport/TransportIntegrationTest.java @@ -11,48 +11,18 @@ import java.io.OutputStream; import java.util.Random; import org.briarproject.BriarTestCase; -import org.briarproject.TestLifecycleModule; -import org.briarproject.TestSystemModule; -import org.briarproject.api.crypto.AuthenticatedCipher; -import org.briarproject.api.crypto.CryptoComponent; -import org.briarproject.api.crypto.SecretKey; -import org.briarproject.api.transport.StreamWriterFactory; -import org.briarproject.crypto.CryptoModule; +import org.briarproject.api.crypto.StreamDecrypter; +import org.briarproject.api.crypto.StreamEncrypter; import org.junit.Test; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Module; - public class TransportIntegrationTest extends BriarTestCase { private final int FRAME_LENGTH = 2048; - private final CryptoComponent crypto; - private final AuthenticatedCipher frameCipher; private final Random random; - private final byte[] secret; - private final SecretKey tagKey, frameKey; public TransportIntegrationTest() { - Module testModule = new AbstractModule() { - @Override - public void configure() { - bind(StreamWriterFactory.class).to( - StreamWriterFactoryImpl.class); - } - }; - Injector i = Guice.createInjector(testModule, new CryptoModule(), - new TestLifecycleModule(), new TestSystemModule()); - crypto = i.getInstance(CryptoComponent.class); - frameCipher = crypto.getFrameCipher(); random = new Random(); - // Since we're sending frames to ourselves, we only need outgoing keys - secret = new byte[32]; - random.nextBytes(secret); - tagKey = crypto.deriveTagKey(secret, true); - frameKey = crypto.deriveFrameKey(secret, 0, true); } @Test @@ -66,27 +36,24 @@ public class TransportIntegrationTest extends BriarTestCase { } private void testWriteAndRead(boolean initiator) throws Exception { - // Encode the tag + // Generate a random tag byte[] tag = new byte[TAG_LENGTH]; - crypto.encodeTag(tag, tagKey, 0); - // Generate two random frames - byte[] frame = new byte[1234]; - random.nextBytes(frame); - byte[] frame1 = new byte[321]; - random.nextBytes(frame1); - // Copy the frame key - the copy will be erased - SecretKey frameCopy = frameKey.copy(); + random.nextBytes(tag); + // Generate two frames with random payloads + byte[] payload1 = new byte[1234]; + random.nextBytes(payload1); + byte[] payload2 = new byte[321]; + random.nextBytes(payload2); // Write the tag and the frames ByteArrayOutputStream out = new ByteArrayOutputStream(); - FrameWriter frameWriter = new OutgoingEncryptionLayer(out, - frameCipher, frameCopy, FRAME_LENGTH, tag); - StreamWriterImpl streamWriter = new StreamWriterImpl(frameWriter, + StreamEncrypter encrypter = new TestStreamEncrypter(out, FRAME_LENGTH, + tag); + OutputStream streamWriter = new StreamWriterImpl(encrypter, FRAME_LENGTH); - OutputStream out1 = streamWriter.getOutputStream(); - out1.write(frame); - out1.flush(); - out1.write(frame1); - out1.flush(); + streamWriter.write(payload1); + streamWriter.flush(); + streamWriter.write(payload2); + streamWriter.flush(); byte[] output = out.toByteArray(); assertEquals(TAG_LENGTH + FRAME_LENGTH * 2, output.length); // Read the tag back @@ -95,17 +62,15 @@ public class TransportIntegrationTest extends BriarTestCase { read(in, recoveredTag); assertArrayEquals(tag, recoveredTag); // Read the frames back - FrameReader frameReader = new IncomingEncryptionLayer(in, frameCipher, - frameKey, FRAME_LENGTH); - StreamReaderImpl streamReader = new StreamReaderImpl(frameReader, + StreamDecrypter decrypter = new TestStreamDecrypter(in, FRAME_LENGTH); + InputStream streamReader = new StreamReaderImpl(decrypter, FRAME_LENGTH); - InputStream in1 = streamReader.getInputStream(); - byte[] recoveredFrame = new byte[frame.length]; - read(in1, recoveredFrame); - assertArrayEquals(frame, recoveredFrame); - byte[] recoveredFrame1 = new byte[frame1.length]; - read(in1, recoveredFrame1); - assertArrayEquals(frame1, recoveredFrame1); + byte[] recoveredPayload1 = new byte[payload1.length]; + read(streamReader, recoveredPayload1); + assertArrayEquals(payload1, recoveredPayload1); + byte[] recoveredPayload2 = new byte[payload2.length]; + read(streamReader, recoveredPayload2); + assertArrayEquals(payload2, recoveredPayload2); streamWriter.close(); streamReader.close(); }