Refactor KeyManager and TagRecogniser. #55

This commit is contained in:
akwizgran
2015-02-12 09:11:24 +00:00
parent 878a70620d
commit 9868feeb2a
60 changed files with 2123 additions and 3840 deletions

View File

@@ -1,20 +1,6 @@
package org.briarproject.crypto;
import static java.util.logging.Level.INFO;
import static org.briarproject.api.invitation.InvitationConstants.CODE_BITS;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
import org.briarproject.api.TransportId;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.KeyParser;
@@ -25,6 +11,9 @@ import org.briarproject.api.crypto.PublicKey;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.Signature;
import org.briarproject.api.system.SeedProvider;
import org.briarproject.api.transport.IncomingKeys;
import org.briarproject.api.transport.OutgoingKeys;
import org.briarproject.api.transport.TransportKeys;
import org.briarproject.util.ByteUtils;
import org.briarproject.util.StringUtils;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
@@ -43,6 +32,21 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.params.KeyParameter;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static org.briarproject.api.invitation.InvitationConstants.CODE_BITS;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent {
private static final Logger LOG =
@@ -55,22 +59,33 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int PBKDF_TARGET_MILLIS = 500;
private static final int PBKDF_SAMPLES = 30;
// Labels for secret derivation
private static final byte[] MASTER = { 'M', 'A', 'S', 'T', 'E', 'R', '\0' };
private static final byte[] SALT = { 'S', 'A', 'L', 'T', '\0' };
private static final byte[] FIRST = { 'F', 'I', 'R', 'S', 'T', '\0' };
private static final byte[] ROTATE = { 'R', 'O', 'T', 'A', 'T', 'E', '\0' };
// Label for confirmation code derivation
private static final byte[] CODE = { 'C', 'O', 'D', 'E', '\0' };
// Label for invitation nonce derivation
private static final byte[] NONCE = { 'N', 'O', 'N', 'C', 'E', '\0' };
// Labels for key derivation
private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G', '\0' };
private static final byte[] B_TAG = { 'B', '_', 'T', 'A', 'G', '\0' };
private static final byte[] A_FRAME =
{ 'A', '_', 'F', 'R', 'A', 'M', 'E', '\0' };
private static final byte[] B_FRAME =
{ 'B', '_', 'F', 'R', 'A', 'M', 'E', '\0' };
// KDF label for master key derivation
private static final byte[] MASTER = { 'M', 'A', 'S', 'T', 'E', 'R' };
// KDF labels for confirmation code derivation
private static final byte[] A_CONFIRM =
{ 'A', '_', 'C', 'O', 'N', 'F', 'I', 'R', 'M' };
private static final byte[] B_CONFIRM =
{ 'B', '_', 'C', 'O', 'N', 'F', 'I', 'R', 'M' };
// KDF labels for invitation stream header key derivation
private static final byte[] A_INVITE =
{ 'A', '_', 'I', 'N', 'V', 'I', 'T', 'E' };
private static final byte[] B_INVITE =
{ 'B', '_', 'I', 'N', 'V', 'I', 'T', 'E' };
// KDF labels for signature nonce derivation
private static final byte[] A_NONCE = { 'A', '_', 'N', 'O', 'N', 'C', 'E' };
private static final byte[] B_NONCE = { 'B', '_', 'N', 'O', 'N', 'C', 'E' };
// KDF label for group salt derivation
private static final byte[] SALT = { 'S', 'A', 'L', 'T' };
// KDF labels for tag key derivation
private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G' };
private static final byte[] B_TAG = { 'B', '_', 'T', 'A', 'G' };
// KDF labels for header key derivation
private static final byte[] A_HEADER =
{ 'A', '_', 'H', 'E', 'A', 'D', 'E', 'R' };
private static final byte[] B_HEADER =
{ 'B', '_', 'H', 'E', 'A', 'D', 'E', 'R' };
// KDF label for key rotation
private static final byte[] ROTATE = { 'R', 'O', 'T', 'A', 'T', 'E' };
private final SecureRandom secureRandom;
private final ECKeyPairGenerator agreementKeyPairGenerator;
@@ -167,26 +182,7 @@ class CryptoComponentImpl implements CryptoComponent {
return ByteUtils.readUint(random, CODE_BITS);
}
public int[] deriveConfirmationCodes(byte[] secret) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
byte[] alice = counterModeKdf(secret, CODE, 0);
byte[] bob = counterModeKdf(secret, CODE, 1);
int[] codes = new int[2];
codes[0] = ByteUtils.readUint(alice, CODE_BITS);
codes[1] = ByteUtils.readUint(bob, CODE_BITS);
return codes;
}
public byte[][] deriveInvitationNonces(byte[] secret) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
byte[] alice = counterModeKdf(secret, NONCE, 0);
byte[] bob = counterModeKdf(secret, NONCE, 1);
return new byte[][] { alice, bob };
}
public byte[] deriveMasterSecret(byte[] theirPublicKey,
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
MessageDigest messageDigest = getMessageDigest();
byte[] ourPublicKey = ourKeyPair.getPublic().getEncoded();
@@ -204,9 +200,8 @@ class CryptoComponentImpl implements CryptoComponent {
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
// The raw secret comes from the key agreement algorithm
byte[] raw = deriveSharedSecret(ourPriv, theirPub);
// Derive the cooked secret from the raw secret using the
// concatenation KDF
return concatenationKdf(raw, MASTER, aliceInfo, bobInfo);
// Derive the master secret from the raw secret using the hash KDF
return new SecretKey(hashKdf(raw, MASTER, aliceInfo, bobInfo));
}
// Package access for testing
@@ -228,46 +223,90 @@ class CryptoComponentImpl implements CryptoComponent {
return secret;
}
public byte[] deriveGroupSalt(byte[] secret) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
return counterModeKdf(secret, SALT, 0);
public int deriveConfirmationCode(SecretKey master, boolean alice) {
byte[] b = macKdf(master, alice ? A_CONFIRM : B_CONFIRM);
return ByteUtils.readUint(b, CODE_BITS);
}
public byte[] deriveInitialSecret(byte[] secret, int transportIndex) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
if (transportIndex < 0) throw new IllegalArgumentException();
return counterModeKdf(secret, FIRST, transportIndex);
public SecretKey deriveInvitationKey(SecretKey master, boolean alice) {
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
}
public byte[] deriveNextSecret(byte[] secret, long period) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
if (period < 0 || period > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
return counterModeKdf(secret, ROTATE, period);
public byte[] deriveSignatureNonce(SecretKey master, boolean alice) {
return macKdf(master, alice ? A_NONCE : B_NONCE);
}
public SecretKey deriveTagKey(byte[] secret, boolean alice) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
if (alice) return deriveKey(secret, A_TAG, 0);
else return deriveKey(secret, B_TAG, 0);
public byte[] deriveGroupSalt(SecretKey master) {
return macKdf(master, SALT);
}
public SecretKey deriveFrameKey(byte[] secret, long streamNumber,
public TransportKeys deriveTransportKeys(TransportId t,
SecretKey master, long rotationPeriod, boolean alice) {
// Keys for the previous period are derived from the master secret
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
SecretKey outTagPrev = deriveTagKey(master, t, alice);
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
// Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
// Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
rotationPeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
rotationPeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
rotationPeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
rotationPeriod);
// Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
}
public TransportKeys rotateTransportKeys(TransportKeys k,
long rotationPeriod) {
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getRotationPeriod();
// Rotate the keys
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
inPrev = inCurr;
inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
inNext = new IncomingKeys(inNextTag, inNextHeader, p);
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p);
}
// Collect and return the keys
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
outCurr);
}
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
byte[] period = new byte[4];
ByteUtils.writeUint32(rotationPeriod, period, 0);
return new SecretKey(macKdf(k, ROTATE, period));
}
private SecretKey deriveTagKey(SecretKey master, TransportId t,
boolean alice) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (alice) return deriveKey(secret, A_FRAME, streamNumber);
else return deriveKey(secret, B_FRAME, streamNumber);
byte[] id = StringUtils.toUtf8(t.getString());
return new SecretKey(macKdf(master, alice ? A_TAG : B_TAG, id));
}
private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
return new SecretKey(counterModeKdf(secret, label, context));
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
boolean alice) {
byte[] id = StringUtils.toUtf8(t.getString());
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
}
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
@@ -277,7 +316,8 @@ class CryptoComponentImpl implements CryptoComponent {
for (int i = 0; i < TAG_LENGTH; i++) tag[i] = 0;
ByteUtils.writeUint32(streamNumber, tag, 0);
BlockCipher cipher = new AESLightEngine();
assert cipher.getBlockSize() == TAG_LENGTH;
if (cipher.getBlockSize() != TAG_LENGTH)
throw new IllegalStateException();
KeyParameter k = new KeyParameter(tagKey.getBytes());
cipher.init(true, k);
cipher.processBlock(tag, 0, tag, 0);
@@ -348,16 +388,16 @@ class CryptoComponentImpl implements CryptoComponent {
// Key derivation function based on a hash function - see NIST SP 800-56A,
// section 5.8
private byte[] concatenationKdf(byte[]... inputs) {
private byte[] hashKdf(byte[]... inputs) {
// The output of the hash function must be long enough to use as a key
MessageDigest messageDigest = getMessageDigest();
if (messageDigest.getDigestLength() < SecretKey.LENGTH)
throw new RuntimeException();
// Each input is length-prefixed - the length must fit in an
// unsigned 8-bit integer
throw new IllegalStateException();
// Calculate the hash over the concatenated length-prefixed inputs
byte[] length = new byte[4];
for (byte[] input : inputs) {
if (input.length > 255) throw new IllegalArgumentException();
messageDigest.update((byte) input.length);
ByteUtils.writeUint32(input.length, length, 0);
messageDigest.update(length);
messageDigest.update(input);
}
byte[] hash = messageDigest.digest();
@@ -368,28 +408,24 @@ class CryptoComponentImpl implements CryptoComponent {
return truncated;
}
// Key derivation function based on a PRF in counter mode - see
// Key derivation function based on a pseudo-random function - see
// NIST SP 800-108, section 5.1
private byte[] counterModeKdf(byte[] secret, byte[] label, long context) {
if (secret.length != SecretKey.LENGTH)
throw new IllegalArgumentException();
// The label must be null-terminated
if (label[label.length - 1] != '\0')
throw new IllegalArgumentException();
private byte[] macKdf(SecretKey key, byte[]... inputs) {
// Initialise the PRF
Mac prf = new HMac(new SHA256Digest());
KeyParameter k = new KeyParameter(secret);
prf.init(k);
int macLength = prf.getMacSize();
prf.init(new KeyParameter(key.getBytes()));
// The output of the PRF must be long enough to use as a key
if (macLength < SecretKey.LENGTH) throw new RuntimeException();
int macLength = prf.getMacSize();
if (macLength < SecretKey.LENGTH)
throw new IllegalStateException();
// Calculate the PRF over the concatenated length-prefixed inputs
byte[] length = new byte[4];
for (byte[] input : inputs) {
ByteUtils.writeUint32(input.length, length, 0);
prf.update(length, 0, length.length);
prf.update(input, 0, input.length);
}
byte[] mac = new byte[macLength];
prf.update((byte) 0); // Counter
prf.update(label, 0, label.length); // Null-terminated
byte[] contextBytes = new byte[4];
ByteUtils.writeUint32(context, contextBytes, 0);
prf.update(contextBytes, 0, contextBytes.length);
prf.update((byte) SecretKey.LENGTH); // Output length
prf.doFinal(mac, 0);
// The output is the first SecretKey.LENGTH bytes of the MAC
if (mac.length == SecretKey.LENGTH) return mac;

View File

@@ -5,7 +5,6 @@ import java.io.InputStream;
import javax.inject.Inject;
import javax.inject.Provider;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.StreamDecrypter;
import org.briarproject.api.crypto.StreamDecrypterFactory;
@@ -13,34 +12,21 @@ import org.briarproject.api.transport.StreamContext;
class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
private final CryptoComponent crypto;
private final Provider<AuthenticatedCipher> cipherProvider;
@Inject
StreamDecrypterFactoryImpl(CryptoComponent crypto,
Provider<AuthenticatedCipher> cipherProvider) {
this.crypto = crypto;
StreamDecrypterFactoryImpl(Provider<AuthenticatedCipher> cipherProvider) {
this.cipherProvider = cipherProvider;
}
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
AuthenticatedCipher cipher = cipherProvider.get();
return new StreamDecrypterImpl(in, cipher, frameKey);
return new StreamDecrypterImpl(in, cipher, ctx.getHeaderKey());
}
public StreamDecrypter createInvitationStreamDecrypter(InputStream in,
byte[] secret, boolean alice) {
// Derive the frame key
SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice);
// Create the decrypter
AuthenticatedCipher cipher = cipherProvider.get();
return new StreamDecrypterImpl(in, cipher, frameKey);
SecretKey headerKey) {
return new StreamDecrypterImpl(in, cipherProvider.get(), headerKey);
}
}

View File

@@ -1,75 +1,77 @@
package org.briarproject.crypto;
import org.briarproject.api.FormatException;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.StreamDecrypter;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
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.SecretKey;
import org.briarproject.api.crypto.StreamDecrypter;
// FIXME: Implementation is incomplete, doesn't read the stream header
class StreamDecrypterImpl implements StreamDecrypter {
private final InputStream in;
private final AuthenticatedCipher frameCipher;
private final SecretKey frameKey;
private final byte[] iv, header, ciphertext;
private final byte[] iv, frameHeader, frameCiphertext;
private long frameNumber;
private boolean finalFrame;
StreamDecrypterImpl(InputStream in, AuthenticatedCipher frameCipher,
SecretKey frameKey) {
SecretKey headerKey) {
this.in = in;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
this.frameKey = headerKey; // FIXME
iv = new byte[IV_LENGTH];
header = new byte[HEADER_LENGTH];
ciphertext = new byte[MAX_FRAME_LENGTH];
frameHeader = new byte[HEADER_LENGTH];
frameCiphertext = new byte[MAX_FRAME_LENGTH];
frameNumber = 0;
finalFrame = false;
}
public int readFrame(byte[] payload) throws IOException {
// The buffer must be big enough for a full-size frame
if (payload.length < MAX_PAYLOAD_LENGTH)
throw new IllegalArgumentException();
if (finalFrame) return -1;
// Read the header
// Read the frame header
int offset = 0;
while (offset < HEADER_LENGTH) {
int read = in.read(ciphertext, offset, HEADER_LENGTH - offset);
int read = in.read(frameCiphertext, offset, HEADER_LENGTH - offset);
if (read == -1) throw new EOFException();
offset += read;
}
// Decrypt and authenticate the header
// Decrypt and authenticate the frame header
FrameEncoder.encodeIv(iv, frameNumber, true);
try {
frameCipher.init(false, frameKey, iv);
int decrypted = frameCipher.process(ciphertext, 0, HEADER_LENGTH,
header, 0);
int decrypted = frameCipher.process(frameCiphertext, 0,
HEADER_LENGTH, frameHeader, 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);
// Decode and validate the frame header
finalFrame = FrameEncoder.isFinalFrame(frameHeader);
int payloadLength = FrameEncoder.getPayloadLength(frameHeader);
int paddingLength = FrameEncoder.getPaddingLength(frameHeader);
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);
int read = in.read(frameCiphertext, offset, frameLength - offset);
if (read == -1) throw new EOFException();
offset += read;
}
@@ -77,7 +79,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
FrameEncoder.encodeIv(iv, frameNumber, false);
try {
frameCipher.init(false, frameKey, iv);
int decrypted = frameCipher.process(ciphertext, HEADER_LENGTH,
int decrypted = frameCipher.process(frameCiphertext, HEADER_LENGTH,
payloadLength + paddingLength + MAC_LENGTH, payload, 0);
if (decrypted != payloadLength + paddingLength)
throw new RuntimeException();

View File

@@ -27,26 +27,15 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
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
crypto.encodeTag(tag, ctx.getTagKey(), ctx.getStreamNumber());
AuthenticatedCipher cipher = cipherProvider.get();
return new StreamEncrypterImpl(out, cipher, frameKey, tag);
return new StreamEncrypterImpl(out, cipher, ctx.getHeaderKey(), tag);
}
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
byte[] secret, boolean alice) {
// Derive the frame key
SecretKey frameKey = crypto.deriveFrameKey(secret, 0, alice);
// Create the encrypter
SecretKey headerKey) {
AuthenticatedCipher cipher = cipherProvider.get();
return new StreamEncrypterImpl(out, cipher, frameKey, null);
return new StreamEncrypterImpl(out, cipher, headerKey, null);
}
}

View File

@@ -1,5 +1,12 @@
package org.briarproject.crypto;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.crypto.StreamEncrypter;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
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;
@@ -7,32 +14,26 @@ 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.SecretKey;
import org.briarproject.api.crypto.StreamEncrypter;
// FIXME: Implementation is incomplete, doesn't write the stream header
class StreamEncrypterImpl implements StreamEncrypter {
private final OutputStream out;
private final AuthenticatedCipher frameCipher;
private final SecretKey frameKey;
private final byte[] tag, iv, plaintext, ciphertext;
private final byte[] tag, iv, framePlaintext, frameCiphertext;
private long frameNumber;
private boolean writeTag;
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher frameCipher,
SecretKey frameKey, byte[] tag) {
SecretKey headerKey, byte[] tag) {
this.out = out;
this.frameCipher = frameCipher;
this.frameKey = frameKey;
this.frameKey = headerKey; // FIXME
this.tag = tag;
iv = new byte[IV_LENGTH];
plaintext = new byte[HEADER_LENGTH + MAX_PAYLOAD_LENGTH];
ciphertext = new byte[MAX_FRAME_LENGTH];
framePlaintext = new byte[HEADER_LENGTH + MAX_PAYLOAD_LENGTH];
frameCiphertext = new byte[MAX_FRAME_LENGTH];
frameNumber = 0;
writeTag = (tag != null);
}
@@ -48,37 +49,39 @@ class StreamEncrypterImpl implements StreamEncrypter {
out.write(tag, 0, tag.length);
writeTag = false;
}
// Encode the header
FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength,
// Encode the frame header
FrameEncoder.encodeHeader(framePlaintext, finalFrame, payloadLength,
paddingLength);
// Encrypt and authenticate the header
// Encrypt and authenticate the frame header
FrameEncoder.encodeIv(iv, frameNumber, true);
try {
frameCipher.init(true, frameKey, iv);
int encrypted = frameCipher.process(plaintext, 0,
HEADER_LENGTH - MAC_LENGTH, ciphertext, 0);
int encrypted = frameCipher.process(framePlaintext, 0,
HEADER_LENGTH - MAC_LENGTH, frameCiphertext, 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);
System.arraycopy(payload, 0, framePlaintext, HEADER_LENGTH,
payloadLength);
for (int i = 0; i < paddingLength; i++)
plaintext[HEADER_LENGTH + payloadLength + i] = 0;
framePlaintext[HEADER_LENGTH + payloadLength + i] = 0;
// Encrypt and authenticate the payload and padding
FrameEncoder.encodeIv(iv, frameNumber, false);
try {
frameCipher.init(true, frameKey, iv);
int encrypted = frameCipher.process(plaintext, HEADER_LENGTH,
payloadLength + paddingLength, ciphertext, HEADER_LENGTH);
int encrypted = frameCipher.process(framePlaintext, HEADER_LENGTH,
payloadLength + paddingLength, frameCiphertext,
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);
out.write(frameCiphertext, 0, HEADER_LENGTH + payloadLength
+ paddingLength + MAC_LENGTH);
frameNumber++;
}