diff options
author | lloyd <[email protected]> | 2011-12-23 16:17:29 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2011-12-23 16:17:29 +0000 |
commit | 67c1645ae151f5dd0f2bafce926ff8690fd97f19 (patch) | |
tree | 9af9c1c22ab58093328cdfd00dbe42292d8b5ed6 /src/tls/rec_wri.cpp | |
parent | d363602f95f1514b4b595d9912fba2e503edcb21 (diff) |
Rename ssl module to tls
Diffstat (limited to 'src/tls/rec_wri.cpp')
-rw-r--r-- | src/tls/rec_wri.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/tls/rec_wri.cpp b/src/tls/rec_wri.cpp new file mode 100644 index 000000000..d3a5c13f7 --- /dev/null +++ b/src/tls/rec_wri.cpp @@ -0,0 +1,270 @@ +/* +* TLS Record Writing +* (C) 2004-2010 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_record.h> +#include <botan/internal/tls_handshake_hash.h> +#include <botan/lookup.h> +#include <botan/loadstor.h> +#include <botan/libstate.h> + +namespace Botan { + +/** +* Record_Writer Constructor +*/ +Record_Writer::Record_Writer(std::tr1::function<void (const byte[], size_t)> out) : + output_fn(out), + buffer(DEFAULT_BUFFERSIZE) + { + mac = 0; + reset(); + } + +/** +* Reset the state +*/ +void Record_Writer::reset() + { + cipher.reset(); + + delete mac; + mac = 0; + + zeroise(buffer); + buf_pos = 0; + + major = minor = buf_type = 0; + block_size = 0; + mac_size = 0; + iv_size = 0; + + seq_no = 0; + } + +/** +* Set the version to use +*/ +void Record_Writer::set_version(Version_Code version) + { + if(version != SSL_V3 && version != TLS_V10 && version != TLS_V11) + throw Invalid_Argument("Record_Writer: Invalid protocol version"); + + major = (version >> 8) & 0xFF; + minor = (version & 0xFF); + } + +/** +* Set the keys for writing +*/ +void Record_Writer::set_keys(const CipherSuite& suite, const SessionKeys& keys, + Connection_Side side) + { + cipher.reset(); + delete mac; + mac = 0; + + SymmetricKey mac_key, cipher_key; + InitializationVector iv; + + if(side == CLIENT) + { + cipher_key = keys.client_cipher_key(); + iv = keys.client_iv(); + mac_key = keys.client_mac_key(); + } + else + { + cipher_key = keys.server_cipher_key(); + iv = keys.server_iv(); + mac_key = keys.server_mac_key(); + } + + const std::string cipher_algo = suite.cipher_algo(); + const std::string mac_algo = suite.mac_algo(); + + if(have_block_cipher(cipher_algo)) + { + cipher.append(get_cipher( + cipher_algo + "/CBC/NoPadding", + cipher_key, iv, ENCRYPTION) + ); + block_size = block_size_of(cipher_algo); + + if(major > 3 || (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, ENCRYPTION)); + block_size = 0; + iv_size = 0; + } + else + throw Invalid_Argument("Record_Writer: Unknown cipher " + cipher_algo); + + if(have_hash(mac_algo)) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + + if(major == 3 && minor == 0) + mac = af.make_mac("SSL3-MAC(" + mac_algo + ")"); + else + mac = af.make_mac("HMAC(" + mac_algo + ")"); + + mac->set_key(mac_key); + mac_size = mac->output_length(); + } + else + throw Invalid_Argument("Record_Writer: Unknown hash " + mac_algo); + } + +/** +* Send one or more records to the other side +*/ +void Record_Writer::send(byte type, const byte input[], size_t length) + { + if(type != buf_type) + flush(); + + const size_t BUFFER_SIZE = buffer.size(); + buf_type = type; + + // FIXME: compression right here + + buffer.copy(buf_pos, input, length); + if(buf_pos + length >= BUFFER_SIZE) + { + send_record(buf_type, &buffer[0], length); + input += (BUFFER_SIZE - buf_pos); + length -= (BUFFER_SIZE - buf_pos); + while(length >= BUFFER_SIZE) + { + send_record(buf_type, input, BUFFER_SIZE); + input += BUFFER_SIZE; + length -= BUFFER_SIZE; + } + buffer.copy(input, length); + buf_pos = 0; + } + buf_pos += length; + } + +/** +* Split buffer into records, and send them all +*/ +void Record_Writer::flush() + { + const byte* buf_ptr = &buffer[0]; + size_t offset = 0; + + while(offset != buf_pos) + { + size_t record_size = buf_pos - offset; + if(record_size > MAX_PLAINTEXT_SIZE) + record_size = MAX_PLAINTEXT_SIZE; + + send_record(buf_type, buf_ptr + offset, record_size); + offset += record_size; + } + buf_type = 0; + buf_pos = 0; + } + +/** +* Encrypt and send the record +*/ +void Record_Writer::send_record(byte type, const byte buf[], size_t length) + { + if(length >= MAX_COMPRESSED_SIZE) + throw TLS_Exception(INTERNAL_ERROR, + "Record_Writer: Compressed packet is too big"); + + if(mac_size == 0) + send_record(type, major, minor, buf, length); + else + { + mac->update_be(seq_no); + mac->update(type); + + if(major > 3 || (major == 3 && minor != 0)) + { + mac->update(major); + mac->update(minor); + } + + mac->update(get_byte<u16bit>(0, length)); + mac->update(get_byte<u16bit>(1, length)); + mac->update(buf, length); + + SecureVector<byte> buf_mac = mac->final(); + + // TODO: This could all use a single buffer + cipher.start_msg(); + + if(iv_size) + { + RandomNumberGenerator& rng = global_state().global_rng(); + + SecureVector<byte> random_iv(iv_size); + + rng.randomize(&random_iv[0], random_iv.size()); + + cipher.write(random_iv); + } + + cipher.write(buf, length); + cipher.write(buf_mac); + + if(block_size) + { + const size_t pad_val = + (block_size - (1 + length + buf_mac.size())) % block_size; + + for(size_t i = 0; i != pad_val + 1; ++i) + cipher.write(pad_val); + } + cipher.end_msg(); + + SecureVector<byte> output = cipher.read_all(Pipe::LAST_MESSAGE); + + send_record(type, major, minor, &output[0], output.size()); + + seq_no++; + } + } + +/** +* Send a final record packet +*/ +void Record_Writer::send_record(byte type, byte major, byte minor, + const byte out[], size_t length) + { + if(length >= MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(INTERNAL_ERROR, + "Record_Writer: Record is too big"); + + byte header[5] = { type, major, minor, 0 }; + for(size_t i = 0; i != 2; ++i) + header[i+3] = get_byte<u16bit>(i, length); + + output_fn(header, 5); + output_fn(out, length); + } + +/** +* Send an alert +*/ +void Record_Writer::alert(Alert_Level level, Alert_Type type) + { + byte alert[2] = { level, type }; + send(ALERT, alert, sizeof(alert)); + flush(); + } + +} |