diff options
-rw-r--r-- | src/tls/rec_read.cpp | 274 | ||||
-rw-r--r-- | src/tls/tls_record.cpp | 275 | ||||
-rw-r--r-- | src/tls/tls_record.h | 34 |
3 files changed, 315 insertions, 268 deletions
diff --git a/src/tls/rec_read.cpp b/src/tls/rec_read.cpp index 38c98a349..e640cd5eb 100644 --- a/src/tls/rec_read.cpp +++ b/src/tls/rec_read.cpp @@ -6,12 +6,8 @@ */ #include <botan/tls_record.h> -#include <botan/lookup.h> -#include <botan/loadstor.h> -#include <botan/internal/tls_session_key.h> +#include <botan/tls_exceptn.h> #include <botan/internal/rounding.h> -#include <botan/internal/assert.h> -#include <botan/internal/xor_buf.h> namespace Botan { @@ -86,82 +82,6 @@ void Record_Reader::change_cipher_spec(Connection_Side side, m_macbuf.resize(m_read_cipherstate->mac_size()); } -size_t Record_Reader::fill_buffer_to(const byte*& input, - size_t& input_size, - size_t& input_consumed, - size_t desired) - { - if(desired <= m_readbuf_pos) - return 0; // already have it - - const size_t space_available = (m_readbuf.size() - m_readbuf_pos); - const size_t taken = std::min(input_size, desired - m_readbuf_pos); - - if(taken > space_available) - throw TLS_Exception(Alert::RECORD_OVERFLOW, - "Record is larger than allowed maximum size"); - - copy_mem(&m_readbuf[m_readbuf_pos], input, taken); - m_readbuf_pos += taken; - input_consumed += taken; - input_size -= taken; - input += taken; - - return (desired - m_readbuf_pos); // how many bytes do we still need? - } - -namespace { - -/* -* Checks the TLS padding. Returns 0 if the padding is invalid (we -* count the padding_length field as part of the padding size so a -* valid padding will always be at least one byte long), or the -* length of the padding otherwise. -* -* Returning 0 in the error case should ensure the MAC check will fail. -* This approach is suggested in section 6.2.3.2 of RFC 5246. -* -* Also returns 0 if block_size == 0, so can be safely called with a -* stream cipher in use. -*/ -size_t tls_padding_check(Protocol_Version version, - size_t block_size, - const byte record[], - size_t record_len) - { - if(block_size == 0 || record_len == 0 || record_len % block_size != 0) - return 0; - - const size_t padding_length = record[(record_len-1)]; - - if(padding_length >= record_len) - return 0; - - /* - * SSL v3 requires that the padding be less than the block size - * but not does specify the value of the padding bytes. - */ - if(version == Protocol_Version::SSL_V3) - { - if(padding_length > 0 && padding_length < block_size) - return (padding_length + 1); - else - return 0; - } - - /* - * TLS v1.0 and up require all the padding bytes be the same value - * and allows up to 255 bytes. - */ - for(size_t i = 0; i != padding_length; ++i) - if(record[(record_len-i-1)] != padding_length) - return 0; - - return padding_length + 1; - } - -} - /* * Retrieve the next record */ @@ -171,187 +91,27 @@ size_t Record_Reader::add_input(const byte input_array[], size_t input_sz, std::vector<byte>& msg, u64bit& msg_sequence) { - const byte* input = &input_array[0]; - - consumed = 0; - - if(m_readbuf_pos < TLS_HEADER_SIZE) // header incomplete? - { - if(size_t needed = fill_buffer_to(input, input_sz, consumed, TLS_HEADER_SIZE)) - return needed; - - BOTAN_ASSERT_EQUAL(m_readbuf_pos, TLS_HEADER_SIZE, - "Have an entire header"); - } - - // Possible SSLv2 format client hello - if((!m_read_cipherstate) && (m_readbuf[0] & 0x80) && (m_readbuf[2] == 1)) - { - if(m_readbuf[3] == 0 && m_readbuf[4] == 2) - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Client claims to only support SSLv2, rejecting"); - - if(m_readbuf[3] >= 3) // SSLv2 mapped TLS hello, then? - { - const size_t record_len = make_u16bit(m_readbuf[0], m_readbuf[1]) & 0x7FFF; - - if(size_t needed = fill_buffer_to(input, input_sz, consumed, record_len + 2)) - return needed; - - BOTAN_ASSERT_EQUAL(m_readbuf_pos, (record_len + 2), - "Have the entire SSLv2 hello"); - - msg_type = HANDSHAKE; - - msg.resize(record_len + 4); - - // Fake v3-style handshake message wrapper - msg[0] = CLIENT_HELLO_SSLV2; - msg[1] = 0; - msg[2] = m_readbuf[0] & 0x7F; - msg[3] = m_readbuf[1]; - - copy_mem(&msg[4], &m_readbuf[2], m_readbuf_pos - 2); - m_readbuf_pos = 0; - msg_sequence = m_read_seq_no++; - return 0; - } - } - - if(m_readbuf[0] != CHANGE_CIPHER_SPEC && - m_readbuf[0] != ALERT && - m_readbuf[0] != HANDSHAKE && - m_readbuf[0] != APPLICATION_DATA && - m_readbuf[0] != HEARTBEAT) - { - throw Unexpected_Message( - "Unknown record type " + std::to_string(m_readbuf[0]) + - " from counterparty"); - } - - const size_t record_len = make_u16bit(m_readbuf[3], m_readbuf[4]); - - if(m_version.major_version()) - { - if(m_readbuf[1] != m_version.major_version() || - m_readbuf[2] != m_version.minor_version()) - { - throw TLS_Exception(Alert::PROTOCOL_VERSION, - "Got unexpected version from counterparty"); - } - } - - if(record_len > MAX_CIPHERTEXT_SIZE) - throw TLS_Exception(Alert::RECORD_OVERFLOW, - "Got message that exceeds maximum size"); - - if(size_t needed = fill_buffer_to(input, input_sz, consumed, - TLS_HEADER_SIZE + record_len)) + const size_t needed = read_record(m_readbuf, + m_readbuf_pos, + input_array, + input_sz, + consumed, + msg_type, + msg, + m_read_seq_no, + m_version, + m_read_cipherstate.get()); + + if(needed) return needed; - BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_len, - m_readbuf_pos, - "Have the full record"); - - if(!m_read_cipherstate) // Only handshake messages allowed here - { - if(m_readbuf[0] != CHANGE_CIPHER_SPEC && - m_readbuf[0] != ALERT && - m_readbuf[0] != HANDSHAKE) - { - throw Decoding_Error("Invalid msg type received during handshake"); - } - - msg_type = m_readbuf[0]; - msg.resize(record_len); - copy_mem(&msg[0], &m_readbuf[TLS_HEADER_SIZE], record_len); - - m_readbuf_pos = 0; - msg_sequence = m_read_seq_no++; - return 0; // got a full record - } - - // Otherwise, decrypt, check MAC, return plaintext - const size_t block_size = m_read_cipherstate->block_size(); - const size_t iv_size = m_read_cipherstate->iv_size(); - const size_t mac_size = m_read_cipherstate->mac_size(); - - if(StreamCipher* sc = m_read_cipherstate->stream_cipher()) - { - sc->cipher1(&m_readbuf[TLS_HEADER_SIZE], record_len); - } - else if(BlockCipher* bc = m_read_cipherstate->block_cipher()) - { - secure_vector<byte>& cbc_state = m_read_cipherstate->cbc_state(); - - BOTAN_ASSERT(record_len % block_size == 0, - "Buffer is an even multiple of block size"); - - byte* buf = &m_readbuf[TLS_HEADER_SIZE]; - - const size_t blocks = record_len / block_size; - - secure_vector<byte> last_ciphertext(block_size); - copy_mem(&last_ciphertext[0], &buf[0], block_size); - - bc->decrypt(&buf[0]); - xor_buf(&buf[0], &cbc_state[0], block_size); - - for(size_t i = 1; i <= blocks; ++i) - { - secure_vector<byte> last_ciphertext2(&buf[block_size*i], &buf[block_size*(i+1)]); - bc->decrypt(&buf[block_size*i]); - xor_buf(&buf[block_size*i], &last_ciphertext[0], block_size); - std::swap(last_ciphertext, last_ciphertext2); - } - cbc_state = last_ciphertext; - } - else - throw Internal_Error("NULL cipher not supported"); - - /* - * This is actually padding_length + 1 because both the padding and - * padding_length fields are padding from our perspective. - */ - const size_t pad_size = - tls_padding_check(m_version, block_size, - &m_readbuf[TLS_HEADER_SIZE], record_len); - - const size_t mac_pad_iv_size = m_macbuf.size() + pad_size + iv_size; - - if(record_len < mac_pad_iv_size) - throw Decoding_Error("Record sent with invalid length"); - - m_read_cipherstate->mac()->update_be(m_read_seq_no); - m_read_cipherstate->mac()->update(m_readbuf[0]); // msg_type - - if(m_version != Protocol_Version::SSL_V3) - { - m_read_cipherstate->mac()->update(m_version.major_version()); - m_read_cipherstate->mac()->update(m_version.minor_version()); - } - - const u16bit plain_length = record_len - mac_pad_iv_size; - - m_read_cipherstate->mac()->update_be(plain_length); - m_read_cipherstate->mac()->update(&m_readbuf[TLS_HEADER_SIZE + iv_size], plain_length); - - m_read_cipherstate->mac()->final(&m_macbuf[0]); - - const size_t mac_offset = record_len - (m_macbuf.size() + pad_size); - - if(!same_mem(&m_readbuf[TLS_HEADER_SIZE + mac_offset], &m_macbuf[0], m_macbuf.size())) - throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); - - if(plain_length > m_max_fragment) + // full message decoded + if(msg.size() > m_max_fragment) throw TLS_Exception(Alert::RECORD_OVERFLOW, "Plaintext record is too large"); - msg_type = m_readbuf[0]; - msg_sequence = m_read_seq_no++; - msg.assign(&m_readbuf[TLS_HEADER_SIZE + iv_size], - &m_readbuf[TLS_HEADER_SIZE + iv_size + plain_length]); + msg_sequence = m_read_seq_no; + m_read_seq_no += 1; - m_readbuf_pos = 0; return 0; } diff --git a/src/tls/tls_record.cpp b/src/tls/tls_record.cpp index 58aa5bfc9..8609b65eb 100644 --- a/src/tls/tls_record.cpp +++ b/src/tls/tls_record.cpp @@ -6,8 +6,9 @@ */ #include <botan/tls_record.h> -#include <botan/internal/tls_session_key.h> #include <botan/libstate.h> +#include <botan/loadstor.h> +#include <botan/internal/tls_session_key.h> #include <botan/internal/rounding.h> #include <botan/internal/assert.h> #include <botan/internal/xor_buf.h> @@ -189,6 +190,278 @@ size_t write_record(std::vector<byte>& output, return (TLS_HEADER_SIZE + buf_size); } +namespace { + +size_t fill_buffer_to(std::vector<byte>& readbuf, + size_t& readbuf_pos, + const byte*& input, + size_t& input_size, + size_t& input_consumed, + size_t desired) + { + if(desired <= readbuf_pos) + return 0; // already have it + + const size_t space_available = (readbuf.size() - readbuf_pos); + const size_t taken = std::min(input_size, desired - readbuf_pos); + + if(taken > space_available) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Record is larger than allowed maximum size"); + + copy_mem(&readbuf[readbuf_pos], input, taken); + readbuf_pos += taken; + input_consumed += taken; + input_size -= taken; + input += taken; + + return (desired - readbuf_pos); // how many bytes do we still need? + } + +/* +* Checks the TLS padding. Returns 0 if the padding is invalid (we +* count the padding_length field as part of the padding size so a +* valid padding will always be at least one byte long), or the +* length of the padding otherwise. +* +* Returning 0 in the error case should ensure the MAC check will fail. +* This approach is suggested in section 6.2.3.2 of RFC 5246. +* +* Also returns 0 if block_size == 0, so can be safely called with a +* stream cipher in use. +*/ +size_t tls_padding_check(Protocol_Version version, + size_t block_size, + const byte record[], + size_t record_len) + { + if(block_size == 0 || record_len == 0 || record_len % block_size != 0) + return 0; + + const size_t padding_length = record[(record_len-1)]; + + if(padding_length >= record_len) + return 0; + + /* + * SSL v3 requires that the padding be less than the block size + * but not does specify the value of the padding bytes. + */ + if(version == Protocol_Version::SSL_V3) + { + if(padding_length > 0 && padding_length < block_size) + return (padding_length + 1); + else + return 0; + } + + /* + * TLS v1.0 and up require all the padding bytes be the same value + * and allows up to 255 bytes. + */ + for(size_t i = 0; i != padding_length; ++i) + if(record[(record_len-i-1)] != padding_length) + return 0; + + return padding_length + 1; + } + +} + +size_t read_record(std::vector<byte>& readbuf, + size_t& readbuf_pos, + const byte input[], + size_t input_sz, + size_t& consumed, + byte& msg_type, + std::vector<byte>& msg, + u64bit msg_sequence, + Protocol_Version version, + Connection_Cipher_State* cipherstate) + { + consumed = 0; + + if(readbuf_pos < TLS_HEADER_SIZE) // header incomplete? + { + if(size_t needed = fill_buffer_to(readbuf, readbuf_pos, + input, input_sz, consumed, + TLS_HEADER_SIZE)) + return needed; + + BOTAN_ASSERT_EQUAL(readbuf_pos, TLS_HEADER_SIZE, + "Have an entire header"); + } + + // Possible SSLv2 format client hello + if((!cipherstate) && (readbuf[0] & 0x80) && (readbuf[2] == 1)) + { + if(readbuf[3] == 0 && readbuf[4] == 2) + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client claims to only support SSLv2, rejecting"); + + if(readbuf[3] >= 3) // SSLv2 mapped TLS hello, then? + { + const size_t record_len = make_u16bit(readbuf[0], readbuf[1]) & 0x7FFF; + + if(size_t needed = fill_buffer_to(readbuf, readbuf_pos, + input, input_sz, consumed, + record_len + 2)) + return needed; + + BOTAN_ASSERT_EQUAL(readbuf_pos, (record_len + 2), + "Have the entire SSLv2 hello"); + + msg_type = HANDSHAKE; + + msg.resize(record_len + 4); + + // Fake v3-style handshake message wrapper + msg[0] = CLIENT_HELLO_SSLV2; + msg[1] = 0; + msg[2] = readbuf[0] & 0x7F; + msg[3] = readbuf[1]; + + copy_mem(&msg[4], &readbuf[2], readbuf_pos - 2); + readbuf_pos = 0; + return 0; + } + } + + if(readbuf[0] != CHANGE_CIPHER_SPEC && + readbuf[0] != ALERT && + readbuf[0] != HANDSHAKE && + readbuf[0] != APPLICATION_DATA && + readbuf[0] != HEARTBEAT) + { + throw Unexpected_Message( + "Unknown record type " + std::to_string(readbuf[0]) + + " from counterparty"); + } + + const size_t record_len = make_u16bit(readbuf[3], readbuf[4]); + + if(version.major_version()) + { + if(readbuf[1] != version.major_version() || + readbuf[2] != version.minor_version()) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Got unexpected version from counterparty"); + } + } + + if(record_len > MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Got message that exceeds maximum size"); + + if(size_t needed = fill_buffer_to(readbuf, readbuf_pos, + input, input_sz, consumed, + TLS_HEADER_SIZE + record_len)) + return needed; + + BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_len, + readbuf_pos, + "Have the full record"); + + if(!cipherstate) // Only handshake messages allowed here + { + if(readbuf[0] != CHANGE_CIPHER_SPEC && + readbuf[0] != ALERT && + readbuf[0] != HANDSHAKE) + { + throw Decoding_Error("Invalid msg type received during handshake"); + } + + msg_type = readbuf[0]; + msg.resize(record_len); + copy_mem(&msg[0], &readbuf[TLS_HEADER_SIZE], record_len); + + readbuf_pos = 0; + return 0; // got a full record + } + + // Otherwise, decrypt, check MAC, return plaintext + const size_t block_size = cipherstate->block_size(); + const size_t iv_size = cipherstate->iv_size(); + const size_t mac_size = cipherstate->mac_size(); + + if(StreamCipher* sc = cipherstate->stream_cipher()) + { + sc->cipher1(&readbuf[TLS_HEADER_SIZE], record_len); + } + else if(BlockCipher* bc = cipherstate->block_cipher()) + { + secure_vector<byte>& cbc_state = cipherstate->cbc_state(); + + BOTAN_ASSERT(record_len % block_size == 0, + "Buffer is an even multiple of block size"); + + byte* buf = &readbuf[TLS_HEADER_SIZE]; + + const size_t blocks = record_len / block_size; + + secure_vector<byte> last_ciphertext(block_size); + copy_mem(&last_ciphertext[0], &buf[0], block_size); + + bc->decrypt(&buf[0]); + xor_buf(&buf[0], &cbc_state[0], block_size); + + for(size_t i = 1; i <= blocks; ++i) + { + secure_vector<byte> last_ciphertext2(&buf[block_size*i], &buf[block_size*(i+1)]); + bc->decrypt(&buf[block_size*i]); + xor_buf(&buf[block_size*i], &last_ciphertext[0], block_size); + std::swap(last_ciphertext, last_ciphertext2); + } + cbc_state = last_ciphertext; + } + else + throw Internal_Error("NULL cipher not supported"); + + /* + * This is actually padding_length + 1 because both the padding and + * padding_length fields are padding from our perspective. + */ + const size_t pad_size = + tls_padding_check(version, block_size, + &readbuf[TLS_HEADER_SIZE], record_len); + + const size_t mac_pad_iv_size = mac_size + pad_size + iv_size; + + if(record_len < mac_pad_iv_size) + throw Decoding_Error("Record sent with invalid length"); + + printf("%016llX\n", msg_sequence); + cipherstate->mac()->update_be(msg_sequence); + cipherstate->mac()->update(readbuf[0]); // msg_type + + if(version != Protocol_Version::SSL_V3) + { + cipherstate->mac()->update(version.major_version()); + cipherstate->mac()->update(version.minor_version()); + } + + const u16bit plain_length = record_len - mac_pad_iv_size; + + cipherstate->mac()->update_be(plain_length); + cipherstate->mac()->update(&readbuf[TLS_HEADER_SIZE + iv_size], plain_length); + + std::vector<byte> mac_buf(mac_size); + cipherstate->mac()->final(&mac_buf[0]); + + const size_t mac_offset = record_len - (mac_size + pad_size); + + if(!same_mem(&readbuf[TLS_HEADER_SIZE + mac_offset], &mac_buf[0], mac_size)) + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + + msg_type = readbuf[0]; + msg.assign(&readbuf[TLS_HEADER_SIZE + iv_size], + &readbuf[TLS_HEADER_SIZE + iv_size + plain_length]); + + readbuf_pos = 0; + return 0; + } + } } diff --git a/src/tls/tls_record.h b/src/tls/tls_record.h index 90b03531d..9a27ba567 100644 --- a/src/tls/tls_record.h +++ b/src/tls/tls_record.h @@ -32,11 +32,6 @@ class Connection_Cipher_State { public: /** - * Create an empty (NULL_WITH_NULL_NULL) cipher state - */ - Connection_Cipher_State() {} - - /** * Initialize a new cipher state */ Connection_Cipher_State(Protocol_Version version, @@ -68,6 +63,15 @@ class Connection_Cipher_State /** * Create a TLS record +* @param write_buffer the output record is placed here +* @param msg_type is the type of the message (handshake, alert, ...) +* @param msg is the plaintext message +* @param msg_length is the length of msg +* @param msg_sequence_number is the sequence number +* @param version is the protocol version +* @param cipherstate is the writing cipher state +* @param rng is a random number generator +* @return number of bytes written to write_buffer */ size_t write_record(std::vector<byte>& write_buffer, byte msg_type, const byte msg[], size_t msg_length, @@ -77,6 +81,21 @@ size_t write_record(std::vector<byte>& write_buffer, RandomNumberGenerator& rng); /** +* Decode a TLS record +* @return zero if full message, else number of bytes still needed +*/ +size_t read_record(std::vector<byte>& read_buffer, + size_t& read_buffer_position, + const byte input[], + size_t input_length, + size_t& input_consumed, + byte& msg_type, + std::vector<byte>& msg, + u64bit msg_sequence, + Protocol_Version version, + Connection_Cipher_State* cipherstate); + +/** * TLS Record Writer */ class BOTAN_DLL Record_Writer @@ -164,11 +183,6 @@ class BOTAN_DLL Record_Reader Record_Reader(const Record_Reader&) = delete; Record_Reader& operator=(const Record_Reader&) = delete; private: - size_t fill_buffer_to(const byte*& input, - size_t& input_size, - size_t& input_consumed, - size_t desired); - std::vector<byte> m_readbuf; std::vector<byte> m_macbuf; size_t m_readbuf_pos = 0; |