aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls/rec_wri.cpp
diff options
context:
space:
mode:
authorlloyd <[email protected]>2011-12-23 16:17:29 +0000
committerlloyd <[email protected]>2011-12-23 16:17:29 +0000
commit67c1645ae151f5dd0f2bafce926ff8690fd97f19 (patch)
tree9af9c1c22ab58093328cdfd00dbe42292d8b5ed6 /src/tls/rec_wri.cpp
parentd363602f95f1514b4b595d9912fba2e503edcb21 (diff)
Rename ssl module to tls
Diffstat (limited to 'src/tls/rec_wri.cpp')
-rw-r--r--src/tls/rec_wri.cpp270
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();
+ }
+
+}