Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.bouncycastle.tls.crypto.impl.TlsBlockCipher Maven / Gradle / Ivy
package org.bouncycastle.tls.crypto.impl;
import java.io.IOException;
import java.security.SecureRandom;
import org.bouncycastle.tls.AlertDescription;
import org.bouncycastle.tls.ProtocolVersion;
import org.bouncycastle.tls.TlsFatalAlert;
import org.bouncycastle.tls.crypto.TlsCipher;
import org.bouncycastle.tls.crypto.TlsCrypto;
import org.bouncycastle.tls.crypto.TlsCryptoParameters;
import org.bouncycastle.tls.crypto.TlsHMAC;
import org.bouncycastle.util.Arrays;
/**
* A generic TLS 1.0-1.2 / SSLv3 block cipher. This can be used for AES or 3DES for example.
*/
public class TlsBlockCipher
implements TlsCipher
{
protected TlsCryptoParameters cryptoParams;
private final TlsCrypto crypto;
protected byte[] randomData;
protected boolean useExplicitIV;
protected boolean encryptThenMAC;
protected TlsBlockCipherImpl encryptCipher;
protected TlsBlockCipherImpl decryptCipher;
protected TlsSuiteMac writeMac;
protected TlsSuiteMac readMac;
public TlsBlockCipher(TlsCrypto crypto, TlsCryptoParameters cryptoParams, TlsBlockCipherImpl encryptCipher, TlsBlockCipherImpl decryptCipher,
TlsHMAC writeMac, TlsHMAC readMac, int cipherKeySize)
throws IOException
{
this.cryptoParams = cryptoParams;
this.crypto = crypto;
this.randomData = crypto.createNonce(256);
this.useExplicitIV = TlsImplUtils.isTLSv11(cryptoParams);
this.encryptThenMAC = cryptoParams.getSecurityParameters().isEncryptThenMAC();
int key_block_size = (2 * cipherKeySize) + writeMac.getMacLength() + readMac.getMacLength();
// From TLS 1.1 onwards, block ciphers don't need client_write_IV
if (!useExplicitIV)
{
key_block_size += encryptCipher.getBlockSize() + decryptCipher.getBlockSize();
}
byte[] key_block = TlsImplUtils.calculateKeyBlock(cryptoParams, key_block_size);
int offset = 0;
byte[] clientMacKey = Arrays.copyOfRange(key_block, offset, offset + writeMac.getMacLength());
offset += clientMacKey.length;
byte[] serverMacKey = Arrays.copyOfRange(key_block, offset, offset + writeMac.getMacLength());
offset += serverMacKey.length;
byte[] client_write_key = Arrays.copyOfRange(key_block, offset, offset + cipherKeySize);
offset += cipherKeySize;
byte[] server_write_key = Arrays.copyOfRange(key_block, offset, offset + cipherKeySize);
offset += cipherKeySize;
byte[] server_IV, client_IV;
if (useExplicitIV)
{
client_IV = new byte[encryptCipher.getBlockSize()];
server_IV = new byte[encryptCipher.getBlockSize()];
}
else
{
client_IV = Arrays.copyOfRange(key_block, offset, offset + encryptCipher.getBlockSize());
offset += encryptCipher.getBlockSize();
server_IV = Arrays.copyOfRange(key_block, offset, offset + encryptCipher.getBlockSize());
offset += encryptCipher.getBlockSize();
}
if (offset != key_block_size)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
this.readMac = new TlsSuiteHMac(cryptoParams, readMac);
this.writeMac = new TlsSuiteHMac(cryptoParams, writeMac);
this.encryptCipher = encryptCipher;
this.decryptCipher = decryptCipher;
if (cryptoParams.isServer())
{
this.writeMac.setKey(serverMacKey);
this.readMac.setKey(clientMacKey);
this.encryptCipher.setKey(server_write_key);
this.decryptCipher.setKey(client_write_key);
this.encryptCipher.init(server_IV);
this.decryptCipher.init(client_IV);
}
else
{
this.writeMac.setKey(clientMacKey);
this.readMac.setKey(serverMacKey);
this.encryptCipher.setKey(client_write_key);
this.decryptCipher.setKey(server_write_key);
this.encryptCipher.init(client_IV);
this.decryptCipher.init(server_IV);
}
}
public int getPlaintextLimit(int ciphertextLimit)
{
int blockSize = encryptCipher.getBlockSize();
int macSize = writeMac.getSize();
int plaintextLimit = ciphertextLimit;
// An explicit IV consumes 1 block
if (useExplicitIV)
{
plaintextLimit -= blockSize;
}
// Leave room for the MAC, and require block-alignment
if (encryptThenMAC)
{
plaintextLimit -= macSize;
plaintextLimit -= plaintextLimit % blockSize;
}
else
{
plaintextLimit -= plaintextLimit % blockSize;
plaintextLimit -= macSize;
}
// Minimum 1 byte of padding
--plaintextLimit;
return plaintextLimit;
}
public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
throws IOException
{
int blockSize = encryptCipher.getBlockSize();
int macSize = writeMac.getSize();
ProtocolVersion version = cryptoParams.getServerVersion();
int enc_input_length = len;
if (!encryptThenMAC)
{
enc_input_length += macSize;
}
int padding_length = blockSize - 1 - (enc_input_length % blockSize);
// TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
if (!version.isDTLS() && !version.isSSL())
{
// Add a random number of extra blocks worth of padding
int maxExtraPadBlocks = (255 - padding_length) / blockSize;
int actualExtraPadBlocks = chooseExtraPadBlocks(crypto.getSecureRandom(), maxExtraPadBlocks);
padding_length += actualExtraPadBlocks * blockSize;
}
int totalSize = len + macSize + padding_length + 1;
if (useExplicitIV)
{
totalSize += blockSize;
}
byte[] outBuf = new byte[totalSize];
int outOff = 0;
if (useExplicitIV)
{
byte[] explicitIV = crypto.createNonce(blockSize);
encryptCipher.init(explicitIV);
System.arraycopy(explicitIV, 0, outBuf, outOff, blockSize);
outOff += blockSize;
}
int blocks_start = outOff;
System.arraycopy(plaintext, offset, outBuf, outOff, len);
outOff += len;
if (!encryptThenMAC)
{
byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
System.arraycopy(mac, 0, outBuf, outOff, mac.length);
outOff += mac.length;
}
for (int i = 0; i <= padding_length; i++)
{
outBuf[outOff++] = (byte)padding_length;
}
encryptCipher.doFinal(outBuf, blocks_start, outOff - blocks_start, outBuf, blocks_start);
if (encryptThenMAC)
{
byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, outOff);
System.arraycopy(mac, 0, outBuf, outOff, mac.length);
outOff += mac.length;
}
// assert outBuf.length == outOff;
return outBuf;
}
public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
throws IOException
{
int blockSize = decryptCipher.getBlockSize();
int macSize = readMac.getSize();
int minLen = blockSize;
if (encryptThenMAC)
{
minLen += macSize;
}
else
{
minLen = Math.max(minLen, macSize + 1);
}
if (useExplicitIV)
{
minLen += blockSize;
}
if (len < minLen)
{
throw new TlsFatalAlert(AlertDescription.decode_error);
}
int blocks_length = len;
if (encryptThenMAC)
{
blocks_length -= macSize;
}
if (blocks_length % blockSize != 0)
{
throw new TlsFatalAlert(AlertDescription.decryption_failed);
}
if (encryptThenMAC)
{
int end = offset + len;
byte[] receivedMac = Arrays.copyOfRange(ciphertext, end - macSize, end);
byte[] calculatedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, len - macSize);
boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
if (badMac)
{
/*
* RFC 7366 3. The MAC SHALL be evaluated before any further processing such as
* decryption is performed, and if the MAC verification fails, then processing SHALL
* terminate immediately. For TLS, a fatal bad_record_mac MUST be generated [2]. For
* DTLS, the record MUST be discarded, and a fatal bad_record_mac MAY be generated
* [4]. This immediate response to a bad MAC eliminates any timing channels that may
* be available through the use of manipulated packet data.
*/
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
}
if (useExplicitIV)
{
decryptCipher.init(Arrays.copyOfRange(ciphertext, offset, offset + blockSize));
offset += blockSize;
blocks_length -= blockSize;
}
decryptCipher.doFinal(ciphertext, offset, blocks_length, ciphertext, offset);
// If there's anything wrong with the padding, this will return zero
int totalPad = checkPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMAC ? 0 : macSize);
boolean badMac = (totalPad == 0);
int dec_output_length = blocks_length - totalPad;
if (!encryptThenMAC)
{
dec_output_length -= macSize;
int macInputLen = dec_output_length;
int macOff = offset + macInputLen;
byte[] receivedMac = Arrays.copyOfRange(ciphertext, macOff, macOff + macSize);
byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen,
blocks_length - macSize, randomData);
badMac |= !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
}
if (badMac)
{
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
return Arrays.copyOfRange(ciphertext, offset, offset + dec_output_length);
}
protected int checkPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
{
int end = off + len;
byte lastByte = buf[end - 1];
int padlen = lastByte & 0xff;
int totalPad = padlen + 1;
int dummyIndex = 0;
byte padDiff = 0;
if ((TlsImplUtils.isSSL(cryptoParams) && totalPad > blockSize) || (macSize + totalPad > len))
{
totalPad = 0;
}
else
{
int padPos = end - totalPad;
do
{
padDiff |= (buf[padPos++] ^ lastByte);
}
while (padPos < end);
dummyIndex = totalPad;
if (padDiff != 0)
{
totalPad = 0;
}
}
// Run some extra dummy checks so the number of checks is always constant
{
byte[] dummyPad = randomData;
while (dummyIndex < 256)
{
padDiff |= (dummyPad[dummyIndex++] ^ lastByte);
}
// Ensure the above loop is not eliminated
dummyPad[0] ^= padDiff;
}
return totalPad;
}
protected int chooseExtraPadBlocks(SecureRandom r, int max)
{
// return r.nextInt(max + 1);
int x = r.nextInt();
int n = lowestBitSet(x);
return Math.min(n, max);
}
protected int lowestBitSet(int x)
{
if (x == 0)
{
return 32;
}
int n = 0;
while ((x & 1) == 0)
{
++n;
x >>= 1;
}
return n;
}
}