aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-09-07 14:52:40 +0000
committerlloyd <[email protected]>2012-09-07 14:52:40 +0000
commit11556009cefa8246ab9f5aaba667290d3d7632e9 (patch)
tree204dc1b0884866d31245ebbc8bbb623872e5d1df /src
parent3f877fe296c1959fefa696094314c96f78bb9f7e (diff)
Fixes for server record handling, where we don't know the version
initially. Partially read the header, figure out the record version, then read the rest of it if needed. Also fix a bug, off by one in CBC decrypt.
Diffstat (limited to 'src')
-rw-r--r--src/tls/tls_record.cpp74
1 files changed, 43 insertions, 31 deletions
diff --git a/src/tls/tls_record.cpp b/src/tls/tls_record.cpp
index 5ecd226a4..db74b7268 100644
--- a/src/tls/tls_record.cpp
+++ b/src/tls/tls_record.cpp
@@ -272,32 +272,26 @@ size_t read_record(std::vector<byte>& readbuf,
byte& msg_type,
std::vector<byte>& msg,
u64bit msg_sequence,
- Protocol_Version version,
+ Protocol_Version negotiated_version,
Connection_Cipher_State* cipherstate)
{
consumed = 0;
- BOTAN_ASSERT(version.valid(),
- "We know what version we are using");
-
- const size_t header_size =
- (version.is_datagram_protocol()) ? DTLS_HEADER_SIZE : TLS_HEADER_SIZE;
-
- if(readbuf_pos < header_size) // header incomplete?
+ if(readbuf_pos < TLS_HEADER_SIZE) // header incomplete?
{
if(size_t needed = fill_buffer_to(readbuf, readbuf_pos,
input, input_sz, consumed,
- header_size))
+ TLS_HEADER_SIZE))
return needed;
- BOTAN_ASSERT_EQUAL(readbuf_pos, header_size,
+ 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(version.is_datagram_protocol())
+ if(negotiated_version.is_datagram_protocol())
throw TLS_Exception(Alert::PROTOCOL_VERSION,
"Client sent SSLv2-style DTLS hello");
@@ -346,21 +340,35 @@ size_t read_record(std::vector<byte>& readbuf,
Protocol_Version record_version(readbuf[1], readbuf[2]);
- if(record_version.is_datagram_protocol())
- msg_sequence = load_be<u64bit>(&readbuf[3], 0);
-
- const size_t record_len = make_u16bit(readbuf[header_size-2],
- readbuf[header_size-1]);
-
- if(version.valid() && record_version != version)
+ if(negotiated_version.valid() && record_version != negotiated_version)
{
throw TLS_Exception(Alert::PROTOCOL_VERSION,
"Got record with version " +
record_version.to_string() +
" expected " +
- version.to_string());
+ negotiated_version.to_string());
}
+ if(record_version.is_datagram_protocol() && readbuf_pos < DTLS_HEADER_SIZE)
+ {
+ if(size_t needed = fill_buffer_to(readbuf, readbuf_pos,
+ input, input_sz, consumed,
+ DTLS_HEADER_SIZE))
+ return needed;
+
+ BOTAN_ASSERT_EQUAL(readbuf_pos, DTLS_HEADER_SIZE,
+ "Have an entire header");
+ }
+
+ const size_t header_size =
+ (record_version.is_datagram_protocol()) ? DTLS_HEADER_SIZE : TLS_HEADER_SIZE;
+
+ if(record_version.is_datagram_protocol())
+ msg_sequence = load_be<u64bit>(&readbuf[3], 0);
+
+ const size_t record_len = make_u16bit(readbuf[header_size-2],
+ readbuf[header_size-1]);
+
if(record_len > MAX_CIPHERTEXT_SIZE)
throw TLS_Exception(Alert::RECORD_OVERFLOW,
"Got message that exceeds maximum size");
@@ -376,7 +384,7 @@ size_t read_record(std::vector<byte>& readbuf,
byte* record_contents = &readbuf[header_size];
- if(!cipherstate) // Only handshake messages allowed here
+ if(!cipherstate) // Only handshake messages allowed during initial handshake
{
if(readbuf[0] != CHANGE_CIPHER_SPEC &&
readbuf[0] != ALERT &&
@@ -392,6 +400,9 @@ size_t read_record(std::vector<byte>& readbuf,
return 0; // got a full record
}
+ BOTAN_ASSERT(negotiated_version.valid(),
+ "We know what version we are using");
+
// Otherwise, decrypt, check MAC, return plaintext
const size_t block_size = cipherstate->block_size();
const size_t iv_size = cipherstate->iv_size();
@@ -403,31 +414,32 @@ size_t read_record(std::vector<byte>& readbuf,
}
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 = record_contents;
-
const size_t blocks = record_len / block_size;
+ BOTAN_ASSERT(blocks > 0, "At least one ciphertext block");
+
+ byte* buf = record_contents;
+
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);
+ xor_buf(&buf[0], &cipherstate->cbc_state()[0], block_size);
secure_vector<byte> last_ciphertext2;
- for(size_t i = 1; i <= blocks; ++i)
+ for(size_t i = 1; i < blocks; ++i)
{
last_ciphertext2.assign(&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;
+
+ cipherstate->cbc_state() = last_ciphertext;
}
else
throw Internal_Error("NULL cipher not supported");
@@ -437,7 +449,7 @@ size_t read_record(std::vector<byte>& readbuf,
* padding_length fields are padding from our perspective.
*/
const size_t pad_size =
- tls_padding_check(version, block_size,
+ tls_padding_check(negotiated_version, block_size,
record_contents, record_len);
const size_t mac_pad_iv_size = mac_size + pad_size + iv_size;
@@ -448,10 +460,10 @@ size_t read_record(std::vector<byte>& readbuf,
cipherstate->mac()->update_be(msg_sequence);
cipherstate->mac()->update(readbuf[0]); // msg_type
- if(version != Protocol_Version::SSL_V3)
+ if(negotiated_version != Protocol_Version::SSL_V3)
{
- cipherstate->mac()->update(version.major_version());
- cipherstate->mac()->update(version.minor_version());
+ cipherstate->mac()->update(negotiated_version.major_version());
+ cipherstate->mac()->update(negotiated_version.minor_version());
}
const u16bit plain_length = record_len - mac_pad_iv_size;