aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-09-06 14:33:21 +0000
committerlloyd <[email protected]>2012-09-06 14:33:21 +0000
commitbeef2ddf2347fbe04fa40cc115d0f080a3b878fd (patch)
tree8d721666a40d36dfa0a7214edd85aa42c458c664
parent94fec1bb0293cf1c5ae7ddcb8d53f21a3edb58ae (diff)
Move record reading also to a stand alone function
-rw-r--r--src/tls/rec_read.cpp274
-rw-r--r--src/tls/tls_record.cpp275
-rw-r--r--src/tls/tls_record.h34
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;