Pull-Merge of latest changes from main repo

This commit is contained in:
Abraham Kiggundu
2015-01-08 11:54:47 +03:00
118 changed files with 2538 additions and 1820 deletions

View File

@@ -20,7 +20,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
this.macLength = macLength;
}
public int doFinal(byte[] input, int inputOff, int len, byte[] output,
public int process(byte[] input, int inputOff, int len, byte[] output,
int outputOff) throws GeneralSecurityException {
int processed = 0;
if(len != 0) {
@@ -38,7 +38,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
public void init(boolean encrypt, SecretKey key, byte[] iv, byte[] aad)
throws GeneralSecurityException {
KeyParameter k = new KeyParameter(key.getEncoded());
KeyParameter k = new KeyParameter(key.getBytes());
AEADParameters params = new AEADParameters(k, macLength * 8, iv, aad);
try {
cipher.init(encrypt, params);

View File

@@ -7,8 +7,6 @@ import static org.briarproject.crypto.EllipticCurveConstants.P;
import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -31,6 +29,7 @@ import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.Signature;
import org.briarproject.api.system.SeedProvider;
import org.briarproject.util.ByteUtils;
import org.briarproject.util.StringUtils;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.CipherParameters;
@@ -44,11 +43,11 @@ import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.spongycastle.crypto.macs.HMac;
import org.spongycastle.crypto.modes.AEADBlockCipher;
import org.spongycastle.crypto.modes.GCMBlockCipher;
import org.spongycastle.crypto.modes.gcm.BasicGCMMultiplier;
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.util.Strings;
class CryptoComponentImpl implements CryptoComponent {
@@ -114,7 +113,7 @@ class CryptoComponentImpl implements CryptoComponent {
public SecretKey generateSecretKey() {
byte[] b = new byte[CIPHER_KEY_BYTES];
secureRandom.nextBytes(b);
return new SecretKeyImpl(b);
return new SecretKey(b);
}
public MessageDigest getMessageDigest() {
@@ -188,8 +187,6 @@ class CryptoComponentImpl implements CryptoComponent {
int[] codes = new int[2];
codes[0] = ByteUtils.readUint(alice, CODE_BITS);
codes[1] = ByteUtils.readUint(bob, CODE_BITS);
ByteUtils.erase(alice);
ByteUtils.erase(bob);
return codes;
}
@@ -223,9 +220,7 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] raw = deriveSharedSecret(ourPriv, theirPub);
// Derive the cooked secret from the raw secret using the
// concatenation KDF
byte[] cooked = concatenationKdf(raw, MASTER, aliceInfo, bobInfo);
ByteUtils.erase(raw);
return cooked;
return concatenationKdf(raw, MASTER, aliceInfo, bobInfo);
}
// Package access for testing
@@ -296,12 +291,16 @@ class CryptoComponentImpl implements CryptoComponent {
}
private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
byte[] key = counterModeKdf(secret, label, context);
return new SecretKeyImpl(key);
return new SecretKey(counterModeKdf(secret, label, context));
}
public AuthenticatedCipher getFrameCipher() {
AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine());
return getAuthenticatedCipher();
}
private AuthenticatedCipher getAuthenticatedCipher() {
AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine(),
new BasicGCMMultiplier());
return new AuthenticatedCipherImpl(a, MAC_BYTES);
}
@@ -313,21 +312,19 @@ class CryptoComponentImpl implements CryptoComponent {
ByteUtils.writeUint32(streamNumber, tag, 0);
BlockCipher cipher = new AESLightEngine();
assert cipher.getBlockSize() == TAG_LENGTH;
KeyParameter k = new KeyParameter(tagKey.getEncoded());
KeyParameter k = new KeyParameter(tagKey.getBytes());
cipher.init(true, k);
cipher.processBlock(tag, 0, tag, 0);
ByteUtils.erase(k.getKey());
}
public byte[] encryptWithPassword(byte[] input, char[] password) {
public byte[] encryptWithPassword(byte[] input, String password) {
// Generate a random salt
byte[] salt = new byte[PBKDF_SALT_BYTES];
secureRandom.nextBytes(salt);
// Calibrate the KDF
int iterations = chooseIterationCount(PBKDF_TARGET_MILLIS);
// Derive the key from the password
byte[] keyBytes = pbkdf2(password, salt, iterations);
SecretKey key = new SecretKeyImpl(keyBytes);
SecretKey key = new SecretKey(pbkdf2(password, salt, iterations));
// Generate a random IV
byte[] iv = new byte[STORAGE_IV_BYTES];
secureRandom.nextBytes(iv);
@@ -338,22 +335,18 @@ class CryptoComponentImpl implements CryptoComponent {
ByteUtils.writeUint32(iterations, output, salt.length);
System.arraycopy(iv, 0, output, salt.length + 4, iv.length);
// Initialise the cipher and encrypt the plaintext
AuthenticatedCipher cipher = getAuthenticatedCipher();
try {
AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine());
AuthenticatedCipher cipher = new AuthenticatedCipherImpl(a,
MAC_BYTES);
cipher.init(true, key, iv, null);
int outputOff = salt.length + 4 + iv.length;
cipher.doFinal(input, 0, input.length, output, outputOff);
cipher.process(input, 0, input.length, output, outputOff);
return output;
} catch(GeneralSecurityException e) {
throw new RuntimeException(e);
} finally {
key.erase();
}
}
public byte[] decryptWithPassword(byte[] input, char[] password) {
public byte[] decryptWithPassword(byte[] input, String password) {
// The input contains the salt, iterations, IV, ciphertext and MAC
if(input.length < PBKDF_SALT_BYTES + 4 + STORAGE_IV_BYTES + MAC_BYTES)
return null; // Invalid
@@ -365,16 +358,12 @@ class CryptoComponentImpl implements CryptoComponent {
byte[] iv = new byte[STORAGE_IV_BYTES];
System.arraycopy(input, salt.length + 4, iv, 0, iv.length);
// Derive the key from the password
byte[] keyBytes = pbkdf2(password, salt, (int) iterations);
SecretKey key = new SecretKeyImpl(keyBytes);
SecretKey key = new SecretKey(pbkdf2(password, salt, (int) iterations));
// Initialise the cipher
AuthenticatedCipher cipher;
AuthenticatedCipher cipher = getAuthenticatedCipher();
try {
AEADBlockCipher a = new GCMBlockCipher(new AESLightEngine());
cipher = new AuthenticatedCipherImpl(a, MAC_BYTES);
cipher.init(false, key, iv, null);
} catch(GeneralSecurityException e) {
key.erase();
throw new RuntimeException(e);
}
// Try to decrypt the ciphertext (may be invalid)
@@ -382,12 +371,10 @@ class CryptoComponentImpl implements CryptoComponent {
int inputOff = salt.length + 4 + iv.length;
int inputLen = input.length - inputOff;
byte[] output = new byte[inputLen - MAC_BYTES];
cipher.doFinal(input, inputOff, inputLen, output, 0);
cipher.process(input, inputOff, inputLen, output, 0);
return output;
} catch(GeneralSecurityException e) {
return null; // Invalid
} finally {
key.erase();
return null; // Invalid ciphertext
}
}
@@ -417,7 +404,6 @@ class CryptoComponentImpl implements CryptoComponent {
// The secret is the first CIPHER_KEY_BYTES bytes of the hash
byte[] output = new byte[CIPHER_KEY_BYTES];
System.arraycopy(hash, 0, output, 0, output.length);
ByteUtils.erase(hash);
return output;
}
@@ -447,20 +433,17 @@ class CryptoComponentImpl implements CryptoComponent {
prf.update((byte) CIPHER_KEY_BYTES); // Output length
prf.doFinal(mac, 0);
System.arraycopy(mac, 0, output, 0, output.length);
ByteUtils.erase(mac);
ByteUtils.erase(k.getKey());
return output;
}
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
private byte[] pbkdf2(char[] password, byte[] salt, int iterations) {
byte[] utf8 = toUtf8ByteArray(password);
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
byte[] utf8 = StringUtils.toUtf8(password);
Digest digest = new SHA384Digest();
PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest);
gen.init(utf8, salt, iterations);
int keyLengthInBits = CIPHER_KEY_BYTES * 8;
CipherParameters p = gen.generateDerivedParameters(keyLengthInBits);
ByteUtils.erase(utf8);
return ((KeyParameter) p).getKey();
}
@@ -512,18 +495,4 @@ class CryptoComponentImpl implements CryptoComponent {
if(size % 2 == 1) return list.get(size / 2);
return list.get(size / 2 - 1) + list.get(size / 2) / 2;
}
private byte[] toUtf8ByteArray(char[] c) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
Strings.toUTF8ByteArray(c, out);
byte[] utf8 = out.toByteArray();
// Erase the output stream's buffer
out.reset();
out.write(new byte[utf8.length]);
return utf8;
} catch(IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,48 @@
package org.briarproject.crypto;
import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.IV_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import org.briarproject.util.ByteUtils;
class FrameEncoder {
static void encodeIv(byte[] iv, long frameNumber, boolean header) {
if(iv.length < IV_LENGTH) throw new IllegalArgumentException();
if(frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
ByteUtils.writeUint32(frameNumber, iv, 0);
if(header) iv[4] = 1;
else iv[4] = 0;
for(int i = 5; i < IV_LENGTH; i++) iv[i] = 0;
}
static void encodeHeader(byte[] header, boolean finalFrame,
int payloadLength, int paddingLength) {
if(header.length < HEADER_LENGTH) throw new IllegalArgumentException();
if(payloadLength < 0) throw new IllegalArgumentException();
if(paddingLength < 0) throw new IllegalArgumentException();
if(payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
throw new IllegalArgumentException();
ByteUtils.writeUint16(payloadLength, header, 0);
ByteUtils.writeUint16(paddingLength, header, 2);
if(finalFrame) header[0] |= 0x80;
}
static boolean isFinalFrame(byte[] header) {
if(header.length < HEADER_LENGTH) throw new IllegalArgumentException();
return (header[0] & 0x80) == 0x80;
}
static int getPayloadLength(byte[] header) {
if(header.length < HEADER_LENGTH) throw new IllegalArgumentException();
return ByteUtils.readUint16(header, 0) & 0x7FFF;
}
static int getPaddingLength(byte[] header) {
if(header.length < HEADER_LENGTH) throw new IllegalArgumentException();
return ByteUtils.readUint16(header, 2);
}
}

View File

@@ -13,9 +13,10 @@ class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
private static final double STRONG = Math.log(Math.pow(LOWER + UPPER +
DIGIT + OTHER, 10));
public float estimateStrength(char[] password) {
public float estimateStrength(String password) {
HashSet<Character> unique = new HashSet<Character>();
for(char c : password) unique.add(c);
int length = password.length();
for(int i = 0; i < length; i++) unique.add(password.charAt(i));
boolean lower = false, upper = false, digit = false, other = false;
for(char c : unique) {
if(Character.isLowerCase(c)) lower = true;

View File

@@ -1,48 +0,0 @@
package org.briarproject.crypto;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.util.ByteUtils;
class SecretKeyImpl implements SecretKey {
private final byte[] key;
private boolean erased = false;
private final Lock synchLock = new ReentrantLock();
SecretKeyImpl(byte[] key) {
this.key = key;
}
public byte[] getEncoded() {
synchLock.lock();
try{
if(erased) throw new IllegalStateException();
return key;
}
finally{
synchLock.unlock();
}
}
public SecretKey copy() {
return new SecretKeyImpl(key.clone());
}
public void erase() {
synchLock.lock();
try{
if(erased) throw new IllegalStateException();
ByteUtils.erase(key);
erased = true;
}
finally{
synchLock.unlock();
}
}
}

View File

@@ -0,0 +1,40 @@
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,
StreamContext ctx) {
// Derive the frame key
byte[] secret = ctx.getSecret();
long streamNumber = ctx.getStreamNumber();
boolean alice = !ctx.getAlice();
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
// Create the decrypter
return new StreamDecrypterImpl(in, crypto.getFrameCipher(), frameKey);
}
public StreamDecrypter createInvitationStreamDecrypter(InputStream in,
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);
}
}

View File

@@ -0,0 +1,97 @@
package org.briarproject.crypto;
import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.IV_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
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 StreamDecrypterImpl implements StreamDecrypter {
private final InputStream in;
private final AuthenticatedCipher frameCipher;
private final SecretKey frameKey;
private final byte[] iv, aad, header, ciphertext;
private long frameNumber;
private boolean finalFrame;
StreamDecrypterImpl(InputStream in, AuthenticatedCipher frameCipher,
SecretKey frameKey) {
this.in = in;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
iv = new byte[IV_LENGTH];
aad = new byte[IV_LENGTH];
header = new byte[HEADER_LENGTH];
ciphertext = new byte[MAX_FRAME_LENGTH];
frameNumber = 0;
finalFrame = false;
}
public int readFrame(byte[] payload) throws IOException {
if(payload.length < MAX_PAYLOAD_LENGTH)
throw new IllegalArgumentException();
if(finalFrame) return -1;
// Read the header
int offset = 0;
while(offset < HEADER_LENGTH) {
int read = in.read(ciphertext, offset, HEADER_LENGTH - offset);
if(read == -1) throw new EOFException();
offset += read;
}
// Decrypt and authenticate the header
FrameEncoder.encodeIv(iv, frameNumber, true);
FrameEncoder.encodeIv(aad, frameNumber, true);
try {
frameCipher.init(false, frameKey, iv, aad);
int decrypted = frameCipher.process(ciphertext, 0, HEADER_LENGTH,
header, 0);
if(decrypted != HEADER_LENGTH - MAC_LENGTH)
throw new RuntimeException();
} catch(GeneralSecurityException e) {
throw new FormatException();
}
// Decode and validate the header
finalFrame = FrameEncoder.isFinalFrame(header);
int payloadLength = FrameEncoder.getPayloadLength(header);
int paddingLength = FrameEncoder.getPaddingLength(header);
if(payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
throw new FormatException();
// Read the payload and padding
int frameLength = HEADER_LENGTH + payloadLength + paddingLength
+ MAC_LENGTH;
while(offset < frameLength) {
int read = in.read(ciphertext, offset, frameLength - offset);
if(read == -1) throw new EOFException();
offset += read;
}
// Decrypt and authenticate the payload and padding
FrameEncoder.encodeIv(iv, frameNumber, false);
FrameEncoder.encodeIv(aad, frameNumber, false);
try {
frameCipher.init(false, frameKey, iv, aad);
int decrypted = frameCipher.process(ciphertext, HEADER_LENGTH,
payloadLength + paddingLength + MAC_LENGTH, payload, 0);
if(decrypted != payloadLength + paddingLength)
throw new RuntimeException();
} catch(GeneralSecurityException e) {
throw new FormatException();
}
// If there's any padding it must be all zeroes
for(int i = 0; i < paddingLength; i++)
if(payload[payloadLength + i] != 0) throw new FormatException();
frameNumber++;
return payloadLength;
}
}

View File

@@ -0,0 +1,48 @@
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,
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);
// Derive the frame key
SecretKey frameKey = crypto.deriveFrameKey(secret, streamNumber, alice);
// Create the encrypter
return new StreamEncrypterImpl(out, crypto.getFrameCipher(), frameKey,
tag);
}
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
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,
null);
}
}

View File

@@ -0,0 +1,97 @@
package org.briarproject.crypto;
import static org.briarproject.api.transport.TransportConstants.HEADER_LENGTH;
import static org.briarproject.api.transport.TransportConstants.IV_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import org.briarproject.api.crypto.AuthenticatedCipher;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.StreamEncrypter;
class StreamEncrypterImpl implements StreamEncrypter {
private final OutputStream out;
private final AuthenticatedCipher frameCipher;
private final SecretKey frameKey;
private final byte[] tag, iv, aad, plaintext, ciphertext;
private long frameNumber;
private boolean writeTag;
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher frameCipher,
SecretKey frameKey, byte[] tag) {
this.out = out;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
this.tag = tag;
iv = new byte[IV_LENGTH];
aad = new byte[IV_LENGTH];
plaintext = new byte[HEADER_LENGTH + MAX_PAYLOAD_LENGTH];
ciphertext = new byte[MAX_FRAME_LENGTH];
frameNumber = 0;
writeTag = (tag != null);
}
public void writeFrame(byte[] payload, int payloadLength,
int paddingLength, boolean finalFrame) throws IOException {
if(payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
throw new IllegalArgumentException();
// Don't allow the frame counter to wrap
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IOException();
// Write the tag if required
if(writeTag) {
out.write(tag, 0, tag.length);
writeTag = false;
}
// Encode the header
FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength,
paddingLength);
// Encrypt and authenticate the header
FrameEncoder.encodeIv(iv, frameNumber, true);
FrameEncoder.encodeIv(aad, frameNumber, true);
try {
frameCipher.init(true, frameKey, iv, aad);
int encrypted = frameCipher.process(plaintext, 0,
HEADER_LENGTH - MAC_LENGTH, ciphertext, 0);
if(encrypted != HEADER_LENGTH) throw new RuntimeException();
} catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);
}
// Combine the payload and padding
System.arraycopy(payload, 0, plaintext, HEADER_LENGTH, payloadLength);
for(int i = 0; i < paddingLength; i++)
plaintext[HEADER_LENGTH + payloadLength + i] = 0;
// Encrypt and authenticate the payload and padding
FrameEncoder.encodeIv(iv, frameNumber, false);
FrameEncoder.encodeIv(aad, frameNumber, false);
try {
frameCipher.init(true, frameKey, iv, aad);
int encrypted = frameCipher.process(plaintext, HEADER_LENGTH,
payloadLength + paddingLength, ciphertext, HEADER_LENGTH);
if(encrypted != payloadLength + paddingLength + MAC_LENGTH)
throw new RuntimeException();
} catch(GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);
}
// Write the frame
out.write(ciphertext, 0, HEADER_LENGTH + payloadLength + paddingLength
+ MAC_LENGTH);
frameNumber++;
}
public void flush() throws IOException {
// Write the tag if required
if(writeTag) {
out.write(tag, 0, tag.length);
writeTag = false;
}
out.flush();
}
}