aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-09-26 20:41:41 -0400
committerJack Lloyd <[email protected]>2016-09-26 20:41:41 -0400
commitc7b66e0e2d617cf23fd3a2a9b2aad417de172e8d (patch)
tree9d78c4883af4c7e5f4786773188d9307ad658576 /src
parent16131507a198c1b0ac608e4313bf02d41ab2f3b5 (diff)
parent0a1e8467ea361a0cbd4abfff0302cef2a8cabfa6 (diff)
Merge GH #633 Cleanup TLS CBC encryption code
Diffstat (limited to 'src')
-rw-r--r--src/lib/tls/tls_record.cpp228
1 files changed, 88 insertions, 140 deletions
diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp
index 4a52aa4a9..88609ca26 100644
--- a/src/lib/tls/tls_record.cpp
+++ b/src/lib/tls/tls_record.cpp
@@ -155,6 +155,41 @@ Connection_Cipher_State::format_ad(u64bit msg_sequence,
return ad;
}
+namespace {
+
+void cbc_encrypt_record(const BlockCipher& bc,
+ secure_vector<byte>& cbc_state,
+ byte buf[],
+ size_t buf_size)
+ {
+ const size_t block_size = bc.block_size();
+ const size_t blocks = buf_size / block_size;
+ BOTAN_ASSERT(buf_size % block_size == 0, "CBC input");
+ BOTAN_ASSERT(blocks > 0, "Expected at least 1 block");
+
+ xor_buf(buf, cbc_state.data(), block_size);
+ bc.encrypt(buf);
+
+ for(size_t i = 1; i < blocks; ++i)
+ {
+ xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size);
+ bc.encrypt(&buf[block_size*i]);
+ }
+
+ cbc_state.assign(&buf[block_size*(blocks-1)],
+ &buf[block_size*blocks]);
+ }
+
+inline void append_u16_len(secure_vector<byte>& output, size_t len_field)
+ {
+ const uint16_t len16 = len_field;
+ BOTAN_ASSERT_EQUAL(len_field, len16, "No truncation");
+ output.push_back(get_byte(0, len16));
+ output.push_back(get_byte(1, len16));
+ }
+
+}
+
void write_record(secure_vector<byte>& output,
Record_Message msg,
Protocol_Version version,
@@ -176,14 +211,14 @@ void write_record(secure_vector<byte>& output,
if(!cs) // initial unencrypted handshake records
{
- output.push_back(get_byte<u16bit>(0, static_cast<u16bit>(msg.get_size())));
- output.push_back(get_byte<u16bit>(1, static_cast<u16bit>(msg.get_size())));
-
+ append_u16_len(output, msg.get_size());
output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size());
return;
}
+ std::vector<byte> aad = cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size()));
+
if(AEAD_Mode* aead = cs->aead())
{
const size_t ctext_size = aead->output_length(msg.get_size());
@@ -193,10 +228,9 @@ void write_record(secure_vector<byte>& output,
const size_t rec_size = ctext_size + cs->nonce_bytes_from_record();
BOTAN_ASSERT(rec_size <= 0xFFFF, "Ciphertext length fits in field");
- output.push_back(get_byte(0, static_cast<u16bit>(rec_size)));
- output.push_back(get_byte(1, static_cast<u16bit>(rec_size)));
+ append_u16_len(output, rec_size);
- aead->set_ad(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size())));
+ aead->set_ad(aad);
if(cs->nonce_bytes_from_record() > 0)
{
@@ -217,147 +251,61 @@ void write_record(secure_vector<byte>& output,
const size_t iv_size = cs->iv_size();
const size_t mac_size = cs->mac_size();
- if(!cs->uses_encrypt_then_mac())
- {
- cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, static_cast<u16bit>(msg.get_size())));
- cs->mac()->update(msg.get_data(), msg.get_size());
-
- const size_t buf_size = round_up(
- iv_size + msg.get_size() + mac_size + (block_size ? 1 : 0),
- block_size);
-
- if(buf_size > MAX_CIPHERTEXT_SIZE)
- throw Internal_Error("Output record is larger than allowed by protocol");
-
- output.push_back(get_byte(0, static_cast<u16bit>(buf_size)));
- output.push_back(get_byte(1, static_cast<u16bit>(buf_size)));
-
- const size_t header_size = output.size();
-
- if(iv_size)
- {
- output.resize(output.size() + iv_size);
- rng.randomize(&output[output.size() - iv_size], iv_size);
- }
-
- output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size());
-
- output.resize(output.size() + mac_size);
- cs->mac()->final(&output[output.size() - mac_size]);
-
- if(block_size)
- {
- const size_t pad_val =
- buf_size - (iv_size + msg.get_size() + mac_size + 1);
-
- for(size_t i = 0; i != pad_val + 1; ++i)
- output.push_back(static_cast<byte>(pad_val));
- }
-
- if(buf_size > MAX_CIPHERTEXT_SIZE)
- throw Internal_Error("Produced ciphertext larger than protocol allows");
-
- BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(),
- "Output buffer is sized properly");
-
- if(BlockCipher* bc = cs->block_cipher())
- {
- secure_vector<byte>& cbc_state = cs->cbc_state();
-
- BOTAN_ASSERT(buf_size % block_size == 0,
- "Buffer is an even multiple of block size");
+ const size_t input_size =
+ iv_size + msg.get_size() + 1 + (cs->uses_encrypt_then_mac() ? 0 : mac_size);
+ const size_t enc_size = round_up(input_size, block_size);
+ const size_t pad_val = enc_size - input_size;
+ const size_t buf_size = enc_size + (cs->uses_encrypt_then_mac() ? mac_size : 0);
- byte* buf = &output[header_size];
-
- const size_t blocks = buf_size / block_size;
-
- xor_buf(buf, cbc_state.data(), block_size);
- bc->encrypt(buf);
-
- for(size_t i = 1; i < blocks; ++i)
- {
- xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size);
- bc->encrypt(&buf[block_size*i]);
- }
-
- cbc_state.assign(&buf[block_size*(blocks-1)],
- &buf[block_size*blocks]);
- }
- else
- {
- throw Internal_Error("NULL cipher not supported");
- }
- }
- else
+ if(cs->uses_encrypt_then_mac())
{
- const size_t enc_size = round_up(
- iv_size + msg.get_size() + (block_size ? 1 : 0),
- block_size);
+ aad[11] = get_byte<uint16_t>(0, enc_size);
+ aad[12] = get_byte<uint16_t>(1, enc_size);
+ }
- const size_t buf_size = enc_size + mac_size;
+ BOTAN_ASSERT(enc_size % block_size == 0,
+ "Buffer is an even multiple of block size");
- if(buf_size > MAX_CIPHERTEXT_SIZE)
- throw Internal_Error("Output record is larger than allowed by protocol");
+ append_u16_len(output, buf_size);
- output.push_back(get_byte<u16bit>(0, buf_size));
- output.push_back(get_byte<u16bit>(1, buf_size));
-
- const size_t header_size = output.size();
+ const size_t header_size = output.size();
- if(iv_size)
- {
- output.resize(output.size() + iv_size);
- rng.randomize(&output[output.size() - iv_size], iv_size);
- }
-
- output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size());
-
- if(block_size)
- {
- const size_t pad_val =
- enc_size - (iv_size + msg.get_size() + 1);
+ if(iv_size)
+ {
+ output.resize(output.size() + iv_size);
+ rng.randomize(&output[output.size() - iv_size], iv_size);
+ }
- for(size_t i = 0; i != pad_val + 1; ++i)
- output.push_back(pad_val);
- }
+ output.insert(output.end(), msg.get_data(), msg.get_data() + msg.get_size());
- if(BlockCipher* bc = cs->block_cipher())
- {
- secure_vector<byte>& cbc_state = cs->cbc_state();
+ // EtM also uses ciphertext size instead of plaintext size for AEAD input
+ const byte* mac_input = (cs->uses_encrypt_then_mac() ? &output[header_size] : msg.get_data());
+ const size_t mac_input_len = (cs->uses_encrypt_then_mac() ? enc_size : msg.get_size());
- BOTAN_ASSERT( enc_size % block_size == 0,
- "Buffer is an even multiple of block size");
+ if(cs->uses_encrypt_then_mac())
+ {
+ for(size_t i = 0; i != pad_val + 1; ++i)
+ output.push_back(static_cast<byte>(pad_val));
+ cbc_encrypt_record(*cs->block_cipher(), cs->cbc_state(), &output[header_size], enc_size);
+ }
- byte* buf = &output[header_size];
-
- const size_t blocks = enc_size / block_size;
+ output.resize(output.size() + mac_size);
+ cs->mac()->update(aad);
+ cs->mac()->update(mac_input, mac_input_len);
+ cs->mac()->final(&output[output.size() - mac_size]);
- xor_buf(buf, cbc_state.data(), block_size);
- bc->encrypt(buf);
-
- for(size_t i = 1; i < blocks; ++i)
- {
- xor_buf(&buf[block_size*i], &buf[block_size*(i-1)], block_size);
- bc->encrypt(&buf[block_size*i]);
- }
+ if(cs->uses_encrypt_then_mac() == false)
+ {
+ for(size_t i = 0; i != pad_val + 1; ++i)
+ output.push_back(static_cast<byte>(pad_val));
+ cbc_encrypt_record(*cs->block_cipher(), cs->cbc_state(), &output[header_size], buf_size);
+ }
- cbc_state.assign(&buf[block_size*(blocks-1)],
- &buf[block_size*blocks]);
-
- cs->mac()->update(cs->format_ad(seq, msg.get_type(), version, enc_size));
- cs->mac()->update(buf, enc_size);
-
- output.resize(output.size() + mac_size);
- cs->mac()->final(&output[output.size() - mac_size]);
+ if(buf_size > MAX_CIPHERTEXT_SIZE)
+ throw Internal_Error("Output record is larger than allowed by protocol");
- BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(),
+ BOTAN_ASSERT_EQUAL(buf_size + header_size, output.size(),
"Output buffer is sized properly");
- }
- else
- {
- throw Internal_Error("NULL cipher not supported");
- }
- }
}
namespace {
@@ -497,7 +445,7 @@ void decrypt_record(secure_vector<byte>& output,
u16bit pad_size = tls_padding_check(record_contents, record_len);
// This mask is zero if there is not enough room in the packet to get
- // a valid MAC. We have to accept empty packets, since otherwise we
+ // a valid MAC. We have to accept empty packets, since otherwise we
// are not compatible with the BEAST countermeasure (thus record_len+1).
const u16bit size_ok_mask = CT::is_lte<u16bit>(static_cast<u16bit>(mac_size + pad_size + iv_size), static_cast<u16bit>(record_len + 1));
pad_size &= size_ok_mask;
@@ -542,7 +490,7 @@ void decrypt_record(secure_vector<byte>& output,
// This early exit does not leak info because all the values are public
if((record_len < mac_size + iv_size) || ( enc_size % cs.block_size() != 0))
throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
-
+
cs.mac()->update(cs.format_ad(record_sequence, record_type, record_version, enc_size));
cs.mac()->update(record_contents, enc_size);
@@ -550,14 +498,14 @@ void decrypt_record(secure_vector<byte>& output,
cs.mac()->final(mac_buf.data());
const size_t mac_offset = enc_size;
-
+
const bool mac_ok = same_mem(&record_contents[mac_offset], mac_buf.data(), mac_size);
-
+
if(!mac_ok)
{
- throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
+ throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure");
}
-
+
cbc_decrypt_record(record_contents, enc_size, cs, *bc);
// 0 if padding was invalid, otherwise 1 + padding_bytes
@@ -565,7 +513,7 @@ void decrypt_record(secure_vector<byte>& output,
const byte* plaintext_block = &record_contents[iv_size];
const u16bit plaintext_length = enc_size - iv_size - pad_size;
-
+
output.assign(plaintext_block, plaintext_block + plaintext_length);
}
}