/* * TLS Record Writing * (C) 2004-2010 Jack Lloyd * * Released under the terms of the Botan license */ #include #include #include #include #include namespace Botan { /** * Record_Writer Constructor */ Record_Writer::Record_Writer(std::function 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(0, length)); mac->update(get_byte(1, length)); mac->update(buf, length); SecureVector 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 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) { 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 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(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] = { (byte)level, (byte)type }; send(ALERT, alert, sizeof(alert)); flush(); } }