mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 04:39:54 +01:00
Final crypto changes for BTPv2. #111
Use BLAKE2s to generate tags. KDF arguments for key rotation. Frame IV format.
This commit is contained in:
@@ -8,12 +8,12 @@ public interface TransportConstants {
|
|||||||
/** The length of the pseudo-random tag in bytes. */
|
/** The length of the pseudo-random tag in bytes. */
|
||||||
int TAG_LENGTH = 16;
|
int TAG_LENGTH = 16;
|
||||||
|
|
||||||
/** The length of the stream header IV in bytes. */
|
|
||||||
int STREAM_HEADER_IV_LENGTH = 24;
|
|
||||||
|
|
||||||
/** The length of the message authentication code (MAC) in bytes. */
|
/** The length of the message authentication code (MAC) in bytes. */
|
||||||
int MAC_LENGTH = 16;
|
int MAC_LENGTH = 16;
|
||||||
|
|
||||||
|
/** The length of the stream header initialisation vector (IV) in bytes. */
|
||||||
|
int STREAM_HEADER_IV_LENGTH = 24;
|
||||||
|
|
||||||
/** The length of the stream header in bytes. */
|
/** The length of the stream header in bytes. */
|
||||||
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
|
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
|
||||||
+ MAC_LENGTH;
|
+ MAC_LENGTH;
|
||||||
@@ -21,8 +21,11 @@ public interface TransportConstants {
|
|||||||
/** The length of the frame initalisation vector (IV) in bytes. */
|
/** The length of the frame initalisation vector (IV) in bytes. */
|
||||||
int FRAME_IV_LENGTH = 24;
|
int FRAME_IV_LENGTH = 24;
|
||||||
|
|
||||||
|
/** The length of the frame header payload in bytes. */
|
||||||
|
int FRAME_HEADER_PAYLOAD_LENGTH = 4;
|
||||||
|
|
||||||
/** The length of the frame header in bytes. */
|
/** The length of the frame header in bytes. */
|
||||||
int FRAME_HEADER_LENGTH = 4 + MAC_LENGTH;
|
int FRAME_HEADER_LENGTH = FRAME_HEADER_PAYLOAD_LENGTH + MAC_LENGTH;
|
||||||
|
|
||||||
/** The maximum length of a frame in bytes, including the header and MAC. */
|
/** The maximum length of a frame in bytes, including the header and MAC. */
|
||||||
int MAX_FRAME_LENGTH = 1024;
|
int MAX_FRAME_LENGTH = 1024;
|
||||||
|
|||||||
@@ -17,12 +17,10 @@ import org.briarproject.api.transport.TransportKeys;
|
|||||||
import org.briarproject.util.ByteUtils;
|
import org.briarproject.util.ByteUtils;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
|
||||||
import org.spongycastle.crypto.BlockCipher;
|
|
||||||
import org.spongycastle.crypto.CipherParameters;
|
import org.spongycastle.crypto.CipherParameters;
|
||||||
import org.spongycastle.crypto.Digest;
|
import org.spongycastle.crypto.Digest;
|
||||||
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||||
import org.spongycastle.crypto.digests.SHA256Digest;
|
import org.spongycastle.crypto.digests.SHA256Digest;
|
||||||
import org.spongycastle.crypto.engines.AESLightEngine;
|
|
||||||
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
|
||||||
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
|
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
|
||||||
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
import org.spongycastle.crypto.params.ECKeyGenerationParameters;
|
||||||
@@ -30,6 +28,7 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
|
|||||||
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
import org.spongycastle.crypto.params.ECPublicKeyParameters;
|
||||||
import org.spongycastle.crypto.params.KeyParameter;
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -57,33 +56,31 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
private static final int PBKDF_TARGET_MILLIS = 500;
|
private static final int PBKDF_TARGET_MILLIS = 500;
|
||||||
private static final int PBKDF_SAMPLES = 30;
|
private static final int PBKDF_SAMPLES = 30;
|
||||||
|
|
||||||
|
private static byte[] ascii(String s) {
|
||||||
|
return s.getBytes(Charset.forName("US-ASCII"));
|
||||||
|
}
|
||||||
|
|
||||||
// KDF label for master key derivation
|
// KDF label for master key derivation
|
||||||
private static final byte[] MASTER = { 'M', 'A', 'S', 'T', 'E', 'R' };
|
private static final byte[] MASTER = ascii("MASTER");
|
||||||
// KDF labels for confirmation code derivation
|
// KDF labels for confirmation code derivation
|
||||||
private static final byte[] A_CONFIRM =
|
private static final byte[] A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
|
||||||
{ 'A', '_', 'C', 'O', 'N', 'F', 'I', 'R', 'M' };
|
private static final byte[] B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
|
||||||
private static final byte[] B_CONFIRM =
|
|
||||||
{ 'B', '_', 'C', 'O', 'N', 'F', 'I', 'R', 'M' };
|
|
||||||
// KDF labels for invitation stream header key derivation
|
// KDF labels for invitation stream header key derivation
|
||||||
private static final byte[] A_INVITE =
|
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
|
||||||
{ 'A', '_', 'I', 'N', 'V', 'I', 'T', 'E' };
|
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
|
||||||
private static final byte[] B_INVITE =
|
|
||||||
{ 'B', '_', 'I', 'N', 'V', 'I', 'T', 'E' };
|
|
||||||
// KDF labels for signature nonce derivation
|
// KDF labels for signature nonce derivation
|
||||||
private static final byte[] A_NONCE = { 'A', '_', 'N', 'O', 'N', 'C', 'E' };
|
private static final byte[] A_NONCE = ascii("ALICE_SIGNATURE_NONCE");
|
||||||
private static final byte[] B_NONCE = { 'B', '_', 'N', 'O', 'N', 'C', 'E' };
|
private static final byte[] B_NONCE = ascii("BOB_SIGNATURE_NONCE");
|
||||||
// KDF label for group salt derivation
|
// KDF label for group salt derivation
|
||||||
private static final byte[] SALT = { 'S', 'A', 'L', 'T' };
|
private static final byte[] SALT = ascii("SALT");
|
||||||
// KDF labels for tag key derivation
|
// KDF labels for tag key derivation
|
||||||
private static final byte[] A_TAG = { 'A', '_', 'T', 'A', 'G' };
|
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
|
||||||
private static final byte[] B_TAG = { 'B', '_', 'T', 'A', 'G' };
|
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
|
||||||
// KDF labels for header key derivation
|
// KDF labels for header key derivation
|
||||||
private static final byte[] A_HEADER =
|
private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY");
|
||||||
{ 'A', '_', 'H', 'E', 'A', 'D', 'E', 'R' };
|
private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY");
|
||||||
private static final byte[] B_HEADER =
|
|
||||||
{ 'B', '_', 'H', 'E', 'A', 'D', 'E', 'R' };
|
|
||||||
// KDF label for key rotation
|
// KDF label for key rotation
|
||||||
private static final byte[] ROTATE = { 'R', 'O', 'T', 'A', 'T', 'E' };
|
private static final byte[] ROTATE = ascii("ROTATE");
|
||||||
|
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
private final ECKeyPairGenerator agreementKeyPairGenerator;
|
||||||
@@ -290,8 +287,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
||||||
byte[] period = new byte[4];
|
byte[] period = new byte[8];
|
||||||
ByteUtils.writeUint32(rotationPeriod, period, 0);
|
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
||||||
return new SecretKey(macKdf(k, ROTATE, period));
|
return new SecretKey(macKdf(k, ROTATE, period));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,14 +308,19 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
for (int i = 0; i < TAG_LENGTH; i++) tag[i] = 0;
|
// Initialise the PRF
|
||||||
ByteUtils.writeUint32(streamNumber, tag, 0);
|
Digest prf = new Blake2sDigest(tagKey.getBytes());
|
||||||
BlockCipher cipher = new AESLightEngine();
|
// The output of the PRF must be long enough to use as a key
|
||||||
if (cipher.getBlockSize() != TAG_LENGTH)
|
int macLength = prf.getDigestSize();
|
||||||
throw new IllegalStateException();
|
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||||
KeyParameter k = new KeyParameter(tagKey.getBytes());
|
// The input is the stream number as a 64-bit integer
|
||||||
cipher.init(true, k);
|
byte[] input = new byte[8];
|
||||||
cipher.processBlock(tag, 0, tag, 0);
|
ByteUtils.writeUint64(streamNumber, input, 0);
|
||||||
|
prf.update(input, 0, input.length);
|
||||||
|
byte[] mac = new byte[macLength];
|
||||||
|
prf.doFinal(mac, 0);
|
||||||
|
// The output is the first TAG_LENGTH bytes of the MAC
|
||||||
|
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] encryptWithPassword(byte[] input, String password) {
|
public byte[] encryptWithPassword(byte[] input, String password) {
|
||||||
|
|||||||
@@ -2,26 +2,23 @@ package org.briarproject.crypto;
|
|||||||
|
|
||||||
import org.briarproject.util.ByteUtils;
|
import org.briarproject.util.ByteUtils;
|
||||||
|
|
||||||
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
class FrameEncoder {
|
class FrameEncoder {
|
||||||
|
|
||||||
static void encodeIv(byte[] iv, long frameNumber, boolean header) {
|
static void encodeIv(byte[] iv, long frameNumber, boolean header) {
|
||||||
if (iv.length < FRAME_IV_LENGTH) throw new IllegalArgumentException();
|
if (iv.length < FRAME_IV_LENGTH) throw new IllegalArgumentException();
|
||||||
if (frameNumber < 0 || frameNumber > MAX_32_BIT_UNSIGNED)
|
if (frameNumber < 0) throw new IllegalArgumentException();
|
||||||
throw new IllegalArgumentException();
|
ByteUtils.writeUint64(frameNumber, iv, 0);
|
||||||
ByteUtils.writeUint32(frameNumber, iv, 0);
|
if (header) iv[0] |= 0x80;
|
||||||
if (header) iv[4] = 1;
|
for (int i = 8; i < FRAME_IV_LENGTH; i++) iv[i] = 0;
|
||||||
else iv[4] = 0;
|
|
||||||
for (int i = 5; i < FRAME_IV_LENGTH; i++) iv[i] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encodeHeader(byte[] header, boolean finalFrame,
|
static void encodeHeader(byte[] header, boolean finalFrame,
|
||||||
int payloadLength, int paddingLength) {
|
int payloadLength, int paddingLength) {
|
||||||
if (header.length < FRAME_HEADER_LENGTH)
|
if (header.length < FRAME_HEADER_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (payloadLength < 0) throw new IllegalArgumentException();
|
if (payloadLength < 0) throw new IllegalArgumentException();
|
||||||
if (paddingLength < 0) throw new IllegalArgumentException();
|
if (paddingLength < 0) throw new IllegalArgumentException();
|
||||||
@@ -33,19 +30,19 @@ class FrameEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean isFinalFrame(byte[] header) {
|
static boolean isFinalFrame(byte[] header) {
|
||||||
if (header.length < FRAME_HEADER_LENGTH)
|
if (header.length < FRAME_HEADER_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
return (header[0] & 0x80) == 0x80;
|
return (header[0] & 0x80) == 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getPayloadLength(byte[] header) {
|
static int getPayloadLength(byte[] header) {
|
||||||
if (header.length < FRAME_HEADER_LENGTH)
|
if (header.length < FRAME_HEADER_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
return ByteUtils.readUint16(header, 0) & 0x7FFF;
|
return ByteUtils.readUint16(header, 0) & 0x7FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getPaddingLength(byte[] header) {
|
static int getPaddingLength(byte[] header) {
|
||||||
if (header.length < FRAME_HEADER_LENGTH)
|
if (header.length < FRAME_HEADER_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
return ByteUtils.readUint16(header, 2);
|
return ByteUtils.readUint16(header, 2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.io.InputStream;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||||
|
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAC_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_FRAME_LENGTH;
|
||||||
@@ -34,7 +35,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
this.cipher = cipher;
|
this.cipher = cipher;
|
||||||
this.streamHeaderKey = streamHeaderKey;
|
this.streamHeaderKey = streamHeaderKey;
|
||||||
frameIv = new byte[FRAME_IV_LENGTH];
|
frameIv = new byte[FRAME_IV_LENGTH];
|
||||||
frameHeader = new byte[FRAME_HEADER_LENGTH];
|
frameHeader = new byte[FRAME_HEADER_PAYLOAD_LENGTH];
|
||||||
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
||||||
frameKey = null;
|
frameKey = null;
|
||||||
frameNumber = 0;
|
frameNumber = 0;
|
||||||
@@ -46,6 +47,8 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
if (payload.length < MAX_PAYLOAD_LENGTH)
|
if (payload.length < MAX_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (finalFrame) return -1;
|
if (finalFrame) return -1;
|
||||||
|
// Don't allow the frame counter to wrap
|
||||||
|
if (frameNumber < 0) throw new IOException();
|
||||||
// Read the stream header if required
|
// Read the stream header if required
|
||||||
if (frameKey == null) readStreamHeader();
|
if (frameKey == null) readStreamHeader();
|
||||||
// Read the frame header
|
// Read the frame header
|
||||||
@@ -62,7 +65,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
cipher.init(false, frameKey, frameIv);
|
cipher.init(false, frameKey, frameIv);
|
||||||
int decrypted = cipher.process(frameCiphertext, 0,
|
int decrypted = cipher.process(frameCiphertext, 0,
|
||||||
FRAME_HEADER_LENGTH, frameHeader, 0);
|
FRAME_HEADER_LENGTH, frameHeader, 0);
|
||||||
if (decrypted != FRAME_HEADER_LENGTH - MAC_LENGTH)
|
if (decrypted != FRAME_HEADER_PAYLOAD_LENGTH)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import java.io.OutputStream;
|
|||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||||
|
import static org.briarproject.api.transport.TransportConstants.FRAME_HEADER_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.FRAME_IV_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAC_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_FRAME_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
||||||
import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
|
||||||
|
|
||||||
class StreamEncrypterImpl implements StreamEncrypter {
|
class StreamEncrypterImpl implements StreamEncrypter {
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private final AuthenticatedCipher cipher;
|
private final AuthenticatedCipher cipher;
|
||||||
private final SecretKey streamHeaderKey, frameKey;
|
private final SecretKey streamHeaderKey, frameKey;
|
||||||
private final byte[] tag, streamHeaderIv;
|
private final byte[] tag, streamHeaderIv;
|
||||||
private final byte[] frameIv, framePlaintext, frameCiphertext;
|
private final byte[] frameIv, frameHeader, framePlaintext, frameCiphertext;
|
||||||
|
|
||||||
private long frameNumber;
|
private long frameNumber;
|
||||||
private boolean writeTag, writeStreamHeader;
|
private boolean writeTag, writeStreamHeader;
|
||||||
@@ -37,7 +37,8 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
this.streamHeaderKey = streamHeaderKey;
|
this.streamHeaderKey = streamHeaderKey;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
frameIv = new byte[FRAME_IV_LENGTH];
|
frameIv = new byte[FRAME_IV_LENGTH];
|
||||||
framePlaintext = new byte[FRAME_HEADER_LENGTH + MAX_PAYLOAD_LENGTH];
|
frameHeader = new byte[FRAME_HEADER_PAYLOAD_LENGTH];
|
||||||
|
framePlaintext = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
frameCiphertext = new byte[MAX_FRAME_LENGTH];
|
||||||
frameNumber = 0;
|
frameNumber = 0;
|
||||||
writeTag = (tag != null);
|
writeTag = (tag != null);
|
||||||
@@ -49,34 +50,33 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
if (payloadLength + paddingLength > MAX_PAYLOAD_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
// Don't allow the frame counter to wrap
|
// Don't allow the frame counter to wrap
|
||||||
if (frameNumber > MAX_32_BIT_UNSIGNED) throw new IOException();
|
if (frameNumber < 0) throw new IOException();
|
||||||
// Write the tag if required
|
// Write the tag if required
|
||||||
if (writeTag) writeTag();
|
if (writeTag) writeTag();
|
||||||
// Write the stream header if required
|
// Write the stream header if required
|
||||||
if (writeStreamHeader) writeStreamHeader();
|
if (writeStreamHeader) writeStreamHeader();
|
||||||
// Encode the frame header
|
// Encode the frame header
|
||||||
FrameEncoder.encodeHeader(framePlaintext, finalFrame, payloadLength,
|
FrameEncoder.encodeHeader(frameHeader, finalFrame, payloadLength,
|
||||||
paddingLength);
|
paddingLength);
|
||||||
// Encrypt and authenticate the frame header
|
// Encrypt and authenticate the frame header
|
||||||
FrameEncoder.encodeIv(frameIv, frameNumber, true);
|
FrameEncoder.encodeIv(frameIv, frameNumber, true);
|
||||||
try {
|
try {
|
||||||
cipher.init(true, frameKey, frameIv);
|
cipher.init(true, frameKey, frameIv);
|
||||||
int encrypted = cipher.process(framePlaintext, 0,
|
int encrypted = cipher.process(frameHeader, 0,
|
||||||
FRAME_HEADER_LENGTH - MAC_LENGTH, frameCiphertext, 0);
|
FRAME_HEADER_PAYLOAD_LENGTH, frameCiphertext, 0);
|
||||||
if (encrypted != FRAME_HEADER_LENGTH) throw new RuntimeException();
|
if (encrypted != FRAME_HEADER_LENGTH) throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException badCipher) {
|
} catch (GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
}
|
}
|
||||||
// Combine the payload and padding
|
// Combine the payload and padding
|
||||||
System.arraycopy(payload, 0, framePlaintext, FRAME_HEADER_LENGTH,
|
System.arraycopy(payload, 0, framePlaintext, 0, payloadLength);
|
||||||
payloadLength);
|
|
||||||
for (int i = 0; i < paddingLength; i++)
|
for (int i = 0; i < paddingLength; i++)
|
||||||
framePlaintext[FRAME_HEADER_LENGTH + payloadLength + i] = 0;
|
framePlaintext[payloadLength + i] = 0;
|
||||||
// Encrypt and authenticate the payload and padding
|
// Encrypt and authenticate the payload and padding
|
||||||
FrameEncoder.encodeIv(frameIv, frameNumber, false);
|
FrameEncoder.encodeIv(frameIv, frameNumber, false);
|
||||||
try {
|
try {
|
||||||
cipher.init(true, frameKey, frameIv);
|
cipher.init(true, frameKey, frameIv);
|
||||||
int encrypted = cipher.process(framePlaintext, FRAME_HEADER_LENGTH,
|
int encrypted = cipher.process(framePlaintext, 0,
|
||||||
payloadLength + paddingLength, frameCiphertext,
|
payloadLength + paddingLength, frameCiphertext,
|
||||||
FRAME_HEADER_LENGTH);
|
FRAME_HEADER_LENGTH);
|
||||||
if (encrypted != payloadLength + paddingLength + MAC_LENGTH)
|
if (encrypted != payloadLength + paddingLength + MAC_LENGTH)
|
||||||
|
|||||||
@@ -37,6 +37,19 @@ public class ByteUtils {
|
|||||||
b[offset + 3] = (byte) (i & 0xFF);
|
b[offset + 3] = (byte) (i & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void writeUint64(long i, byte[] b, int offset) {
|
||||||
|
if (i < 0) throw new IllegalArgumentException();
|
||||||
|
if (b.length < offset + 8) throw new IllegalArgumentException();
|
||||||
|
b[offset] = (byte) (i >> 56);
|
||||||
|
b[offset + 1] = (byte) (i >> 48 & 0xFF);
|
||||||
|
b[offset + 2] = (byte) (i >> 40 & 0xFF);
|
||||||
|
b[offset + 3] = (byte) (i >> 32 & 0xFF);
|
||||||
|
b[offset + 4] = (byte) (i >> 24 & 0xFF);
|
||||||
|
b[offset + 5] = (byte) (i >> 16 & 0xFF);
|
||||||
|
b[offset + 6] = (byte) (i >> 8 & 0xFF);
|
||||||
|
b[offset + 7] = (byte) (i & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
public static int readUint16(byte[] b, int offset) {
|
public static int readUint16(byte[] b, int offset) {
|
||||||
if (b.length < offset + 2) throw new IllegalArgumentException();
|
if (b.length < offset + 2) throw new IllegalArgumentException();
|
||||||
return ((b[offset] & 0xFF) << 8) | (b[offset + 1] & 0xFF);
|
return ((b[offset] & 0xFF) << 8) | (b[offset + 1] & 0xFF);
|
||||||
|
|||||||
@@ -30,24 +30,39 @@ public class ByteUtilsTest extends BriarTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint16() {
|
public void testWriteUint16() {
|
||||||
byte[] b = new byte[3];
|
byte[] b = new byte[4];
|
||||||
ByteUtils.writeUint16(0, b, 1);
|
ByteUtils.writeUint16(0, b, 1);
|
||||||
assertEquals("000000", StringUtils.toHexString(b));
|
assertEquals("00000000", StringUtils.toHexString(b));
|
||||||
ByteUtils.writeUint16(1, b, 1);
|
ByteUtils.writeUint16(1, b, 1);
|
||||||
assertEquals("000001", StringUtils.toHexString(b));
|
assertEquals("00000100", StringUtils.toHexString(b));
|
||||||
ByteUtils.writeUint16(65535, b, 1);
|
ByteUtils.writeUint16(Short.MAX_VALUE, b, 1);
|
||||||
assertEquals("00FFFF", StringUtils.toHexString(b));
|
assertEquals("007FFF00", StringUtils.toHexString(b));
|
||||||
|
ByteUtils.writeUint16(ByteUtils.MAX_16_BIT_UNSIGNED, b, 1);
|
||||||
|
assertEquals("00FFFF00", StringUtils.toHexString(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteUint32() {
|
public void testWriteUint32() {
|
||||||
byte[] b = new byte[5];
|
byte[] b = new byte[6];
|
||||||
ByteUtils.writeUint32(0, b, 1);
|
ByteUtils.writeUint32(0, b, 1);
|
||||||
assertEquals("0000000000", StringUtils.toHexString(b));
|
assertEquals("000000000000", StringUtils.toHexString(b));
|
||||||
ByteUtils.writeUint32(1, b, 1);
|
ByteUtils.writeUint32(1, b, 1);
|
||||||
assertEquals("0000000001", StringUtils.toHexString(b));
|
assertEquals("000000000100", StringUtils.toHexString(b));
|
||||||
ByteUtils.writeUint32(4294967295L, b, 1);
|
ByteUtils.writeUint32(Integer.MAX_VALUE, b, 1);
|
||||||
assertEquals("00FFFFFFFF", StringUtils.toHexString(b));
|
assertEquals("007FFFFFFF00", StringUtils.toHexString(b));
|
||||||
|
ByteUtils.writeUint32(ByteUtils.MAX_32_BIT_UNSIGNED, b, 1);
|
||||||
|
assertEquals("00FFFFFFFF00", StringUtils.toHexString(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteUint64() {
|
||||||
|
byte[] b = new byte[10];
|
||||||
|
ByteUtils.writeUint64(0, b, 1);
|
||||||
|
assertEquals("00000000000000000000", StringUtils.toHexString(b));
|
||||||
|
ByteUtils.writeUint64(1, b, 1);
|
||||||
|
assertEquals("00000000000000000100", StringUtils.toHexString(b));
|
||||||
|
ByteUtils.writeUint64(Long.MAX_VALUE, b, 1);
|
||||||
|
assertEquals("007FFFFFFFFFFFFFFF00", StringUtils.toHexString(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user