package com.google.bitcoin.bouncycastle.crypto.tls; import com.google.bitcoin.bouncycastle.crypto.BlockCipher; import com.google.bitcoin.bouncycastle.crypto.Digest; import com.google.bitcoin.bouncycastle.crypto.params.KeyParameter; import com.google.bitcoin.bouncycastle.crypto.params.ParametersWithIV; import java.io.IOException; /** * A generic TLS 1.0 block cipher suite. This can be used for AES or 3DES for * example. */ public class TlsBlockCipherCipherSuite extends TlsCipherSuite { private BlockCipher encryptCipher; private BlockCipher decryptCipher; private Digest writeDigest; private Digest readDigest; private int cipherKeySize; private short keyExchange; private TlsMac writeMac; private TlsMac readMac; protected TlsBlockCipherCipherSuite(BlockCipher encrypt, BlockCipher decrypt, Digest writeDigest, Digest readDigest, int cipherKeySize, short keyExchange) { this.encryptCipher = encrypt; this.decryptCipher = decrypt; this.writeDigest = writeDigest; this.readDigest = readDigest; this.cipherKeySize = cipherKeySize; this.keyExchange = keyExchange; } protected void init(byte[] ms, byte[] cr, byte[] sr) { int prfSize = (2 * cipherKeySize) + (2 * writeDigest.getDigestSize()) + (2 * encryptCipher.getBlockSize()); byte[] key_block = new byte[prfSize]; byte[] random = new byte[cr.length + sr.length]; System.arraycopy(cr, 0, random, sr.length, cr.length); System.arraycopy(sr, 0, random, 0, sr.length); TlsUtils.PRF(ms, TlsUtils.toByteArray("key expansion"), random, key_block); int offset = 0; // Init MACs writeMac = new TlsMac(writeDigest, key_block, offset, writeDigest .getDigestSize()); offset += writeDigest.getDigestSize(); readMac = new TlsMac(readDigest, key_block, offset, readDigest .getDigestSize()); offset += readDigest.getDigestSize(); // Init Ciphers this.initCipher(true, encryptCipher, key_block, cipherKeySize, offset, offset + (cipherKeySize * 2)); offset += cipherKeySize; this.initCipher(false, decryptCipher, key_block, cipherKeySize, offset, offset + cipherKeySize + decryptCipher.getBlockSize()); } private void initCipher(boolean forEncryption, BlockCipher cipher, byte[] key_block, int key_size, int key_offset, int iv_offset) { KeyParameter key_parameter = new KeyParameter(key_block, key_offset, key_size); ParametersWithIV parameters_with_iv = new ParametersWithIV( key_parameter, key_block, iv_offset, cipher.getBlockSize()); cipher.init(forEncryption, parameters_with_iv); } protected byte[] encodePlaintext(short type, byte[] plaintext, int offset, int len) { int blocksize = encryptCipher.getBlockSize(); int paddingsize = blocksize - ((len + writeMac.getSize() + 1) % blocksize); int totalsize = len + writeMac.getSize() + paddingsize + 1; byte[] outbuf = new byte[totalsize]; System.arraycopy(plaintext, offset, outbuf, 0, len); byte[] mac = writeMac.calculateMac(type, plaintext, offset, len); System.arraycopy(mac, 0, outbuf, len, mac.length); int paddoffset = len + mac.length; for (int i = 0; i <= paddingsize; i++) { outbuf[i + paddoffset] = (byte)paddingsize; } for (int i = 0; i < totalsize; i += blocksize) { encryptCipher.processBlock(outbuf, i, outbuf, i); } return outbuf; } protected byte[] decodeCiphertext(short type, byte[] ciphertext, int offset, int len, TlsProtocolHandler handler) throws IOException { int blocksize = decryptCipher.getBlockSize(); boolean decrypterror = false; /* * Decrypt all the ciphertext using the blockcipher */ for (int i = 0; i < len; i += blocksize) { decryptCipher.processBlock(ciphertext, i + offset, ciphertext, i + offset); } /* * Check if padding is correct */ int paddingsize = ciphertext[offset + len - 1]; if (offset + len - 1 - paddingsize < 0) { /* * This would lead to a negative array index, so this padding * must be incorrect! */ decrypterror = true; paddingsize = 0; } else { /* * Now, check all the padding-bytes. */ for (int i = 0; i <= paddingsize; i++) { if (ciphertext[offset + len - 1 - i] != paddingsize) { /* Wrong padding */ decrypterror = true; } } } /* * We now don't care if padding verification has failed or not, * we will calculate the mac to give an attacker no kind of timing * profile he can use to find out if mac verification failed or * padding verification failed. */ int plaintextlength = len - readMac.getSize() - paddingsize - 1; byte[] calculatedMac = readMac.calculateMac(type, ciphertext, offset, plaintextlength); /* * Check all bytes in the mac. */ for (int i = 0; i < calculatedMac.length; i++) { if (ciphertext[offset + plaintextlength + i] != calculatedMac[i]) { decrypterror = true; } } /* * Now, it is safe to fail. */ if (decrypterror) { handler.failWithError(TlsProtocolHandler.AL_fatal, TlsProtocolHandler.AP_bad_record_mac); } byte[] plaintext = new byte[plaintextlength]; System.arraycopy(ciphertext, offset, plaintext, 0, plaintextlength); return plaintext; } protected short getKeyExchangeAlgorithm() { return this.keyExchange; } }