diff options
Diffstat (limited to 'src/ssl/rec_read.cpp')
-rw-r--r-- | src/ssl/rec_read.cpp | 120 |
1 files changed, 69 insertions, 51 deletions
diff --git a/src/ssl/rec_read.cpp b/src/ssl/rec_read.cpp index 95059dbf2..8f8e5dc1e 100644 --- a/src/ssl/rec_read.cpp +++ b/src/ssl/rec_read.cpp @@ -1,6 +1,6 @@ /** -* TLS Record Reading -* (C) 2004-2006 Jack Lloyd +* TLS Record Reading +* (C) 2004-2010 Jack Lloyd * * Released under the terms of the Botan license */ @@ -13,23 +13,15 @@ namespace Botan { /** -* Record_Reader Constructor -*/ -Record_Reader::Record_Reader(Socket& sock) : socket(sock) - { - reset(); - } - -/** * Reset the state */ void Record_Reader::reset() { - compress.reset(); cipher.reset(); mac.reset(); - do_compress = false; - mac_size = pad_amount = 0; + mac_size = 0; + block_size = 0; + iv_size = 0; major = minor = 0; seq_no = 0; } @@ -39,7 +31,7 @@ void Record_Reader::reset() */ void Record_Reader::set_version(Version_Code version) { - if(version != SSL_V3 && version != TLS_V10) + if(version != SSL_V3 && version != TLS_V10 && version != TLS_V11) throw Invalid_Argument("Record_Reader: Invalid protocol version"); major = (version >> 8) & 0xFF; @@ -47,15 +39,6 @@ void Record_Reader::set_version(Version_Code version) } /** -* Set the compression algorithm -*/ -void Record_Reader::set_compressor(Filter* compressor) - { - compress.append(compressor); - do_compress = true; - } - -/** * Set the keys for reading */ void Record_Reader::set_keys(const CipherSuite& suite, const SessionKeys& keys, @@ -89,12 +72,18 @@ void Record_Reader::set_keys(const CipherSuite& suite, const SessionKeys& keys, cipher_algo + "/CBC/NoPadding", cipher_key, iv, DECRYPTION) ); - pad_amount = block_size_of(cipher_algo); + block_size = block_size_of(cipher_algo); + + if(major == 3 && minor >= 2) + iv_size = block_size; + else + iv_size = 0; } else if(have_stream_cipher(cipher_algo)) { cipher.append(get_cipher(cipher_algo, cipher_key, DECRYPTION)); - pad_amount = 0; + block_size = 0; + iv_size = 0; } else throw Invalid_Argument("Record_Reader: Unknown cipher " + cipher_algo); @@ -112,75 +101,102 @@ void Record_Reader::set_keys(const CipherSuite& suite, const SessionKeys& keys, throw Invalid_Argument("Record_Reader: Unknown hash " + mac_algo); } +void Record_Reader::add_input(const byte input[], u32bit input_size) + { + input_queue.write(input, input_size); + } + /** * Retrieve the next record */ -SecureVector<byte> Record_Reader::get_record(byte& msg_type) +u32bit Record_Reader::get_record(byte& msg_type, + MemoryRegion<byte>& output) { byte header[5] = { 0 }; - u32bit got = socket.read(header, sizeof(header)); + const u32bit have_in_queue = input_queue.size(); - if(got == 0) - { - msg_type = CONNECTION_CLOSED; - return SecureVector<byte>(); - } - else if(got != sizeof(header)) - throw Decoding_Error("Record_Reader: Record truncated"); + if(have_in_queue < sizeof(header)) + return (sizeof(header) - have_in_queue); - msg_type = header[0]; + /* + * We peek first to make sure we have the full record + */ + input_queue.peek(header, sizeof(header)); - const u16bit version = make_u16bit(header[1], header[2]); + const u16bit version = make_u16bit(header[1], header[2]); + const u16bit record_len = make_u16bit(header[3], header[4]); if(major && (header[1] != major || header[2] != minor)) throw TLS_Exception(PROTOCOL_VERSION, "Record_Reader: Got unexpected version"); - SecureVector<byte> buffer(make_u16bit(header[3], header[4])); - if(socket.read(buffer, buffer.size()) != buffer.size()) - throw Decoding_Error("Record_Reader: Record truncated"); + // If insufficient data, return without doing anything + if(have_in_queue < (sizeof(header) + record_len)) + return (sizeof(header) + record_len - have_in_queue); + + SecureVector<byte> buffer(record_len); + input_queue.read(header, sizeof(header)); // pull off the header + input_queue.read(buffer, buffer.size()); + + /* + * We are handshaking, no crypto to do so return as-is + * TODO: Check msg_type to confirm a handshake? + */ if(mac_size == 0) - return buffer; + { + msg_type = header[0]; + output = buffer; + return 0; // got a full record + } + + // Otherwise, decrypt, check MAC, return plaintext cipher.process_msg(buffer); SecureVector<byte> plaintext = cipher.read_all(Pipe::LAST_MESSAGE); u32bit pad_size = 0; - if(pad_amount) + + if(block_size) { byte pad_value = plaintext[plaintext.size()-1]; pad_size = pad_value + 1; + /* + * Check the padding; if it is wrong, then say we have 0 bytes of + * padding, which should ensure that the MAC check below does not + * suceed. This hides a timing channel. + * + * This particular countermeasure is recommended in the TLS 1.2 + * spec (RFC 5246) in section 6.2.3.2 + */ if(version == SSL_V3) { - if(pad_value > pad_amount) - throw TLS_Exception(BAD_RECORD_MAC, - "Record_Reader: Bad padding"); + if(pad_value > block_size) + pad_size = 0; } else { for(u32bit j = 0; j != pad_size; j++) if(plaintext[plaintext.size()-j-1] != pad_value) - throw TLS_Exception(BAD_RECORD_MAC, - "Record_Reader: Bad padding"); + pad_size = 0; } } - if(plaintext.size() < mac_size + pad_size) + if(plaintext.size() < mac_size + pad_size + iv_size) throw Decoding_Error("Record_Reader: Record truncated"); const u32bit mac_offset = plaintext.size() - (mac_size + pad_size); SecureVector<byte> recieved_mac(plaintext.begin() + mac_offset, mac_size); - const u16bit plain_length = plaintext.size() - (mac_size + pad_size); + const u16bit plain_length = plaintext.size() - (mac_size + pad_size + iv_size); mac.start_msg(); for(u32bit j = 0; j != 8; j++) mac.write(get_byte(j, seq_no)); - mac.write(msg_type); + mac.write(header[0]); // msg_type if(version != SSL_V3) for(u32bit j = 0; j != 2; j++) @@ -188,7 +204,7 @@ SecureVector<byte> Record_Reader::get_record(byte& msg_type) for(u32bit j = 0; j != 2; j++) mac.write(get_byte(j, plain_length)); - mac.write(plaintext, plain_length); + mac.write(&plaintext[iv_size], plain_length); mac.end_msg(); ++seq_no; @@ -198,7 +214,9 @@ SecureVector<byte> Record_Reader::get_record(byte& msg_type) if(recieved_mac != computed_mac) throw TLS_Exception(BAD_RECORD_MAC, "Record_Reader: MAC failure"); - return SecureVector<byte>(plaintext, mac_offset); + msg_type = header[0]; + output.set(&plaintext[iv_size], plain_length); + return 0; } } |