aboutsummaryrefslogtreecommitdiffstats
path: root/src/tls
diff options
context:
space:
mode:
Diffstat (limited to 'src/tls')
-rw-r--r--src/tls/c_kex.cpp165
-rw-r--r--src/tls/cert_req.cpp151
-rw-r--r--src/tls/cert_ver.cpp98
-rw-r--r--src/tls/finished.cpp100
-rw-r--r--src/tls/hello.cpp331
-rw-r--r--src/tls/info.txt66
-rw-r--r--src/tls/rec_read.cpp255
-rw-r--r--src/tls/rec_wri.cpp270
-rw-r--r--src/tls/s_kex.cpp180
-rw-r--r--src/tls/tls_alerts.h54
-rw-r--r--src/tls/tls_client.cpp499
-rw-r--r--src/tls/tls_client.h87
-rw-r--r--src/tls/tls_exceptn.h43
-rw-r--r--src/tls/tls_handshake_hash.cpp70
-rw-r--r--src/tls/tls_handshake_hash.h40
-rw-r--r--src/tls/tls_magic.h192
-rw-r--r--src/tls/tls_messages.h297
-rw-r--r--src/tls/tls_policy.cpp118
-rw-r--r--src/tls/tls_policy.h63
-rw-r--r--src/tls/tls_reader.h186
-rw-r--r--src/tls/tls_record.h119
-rw-r--r--src/tls/tls_server.cpp494
-rw-r--r--src/tls/tls_server.h76
-rw-r--r--src/tls/tls_session_key.cpp170
-rw-r--r--src/tls/tls_session_key.h52
-rw-r--r--src/tls/tls_state.cpp59
-rw-r--r--src/tls/tls_state.h53
-rw-r--r--src/tls/tls_suites.cpp281
-rw-r--r--src/tls/tls_suites.h42
29 files changed, 4611 insertions, 0 deletions
diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp
new file mode 100644
index 000000000..0f20b819c
--- /dev/null
+++ b/src/tls/c_kex.cpp
@@ -0,0 +1,165 @@
+/*
+* Client Key Exchange Message
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/pubkey.h>
+#include <botan/dh.h>
+#include <botan/rsa.h>
+#include <botan/rng.h>
+#include <botan/loadstor.h>
+#include <memory>
+
+namespace Botan {
+
+/**
+* Create a new Client Key Exchange message
+*/
+Client_Key_Exchange::Client_Key_Exchange(RandomNumberGenerator& rng,
+ Record_Writer& writer,
+ HandshakeHash& hash,
+ const Public_Key* pub_key,
+ Version_Code using_version,
+ Version_Code pref_version)
+ {
+ include_length = true;
+
+ if(const DH_PublicKey* dh_pub = dynamic_cast<const DH_PublicKey*>(pub_key))
+ {
+ DH_PrivateKey priv_key(rng, dh_pub->get_domain());
+
+ PK_Key_Agreement ka(priv_key, "Raw");
+
+ pre_master = ka.derive_key(0, dh_pub->public_value()).bits_of();
+
+ key_material = priv_key.public_value();
+ }
+ else if(const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(pub_key))
+ {
+ pre_master = rng.random_vec(48);
+ pre_master[0] = (pref_version >> 8) & 0xFF;
+ pre_master[1] = (pref_version ) & 0xFF;
+
+ PK_Encryptor_EME encryptor(*rsa_pub, "PKCS1v15");
+
+ key_material = encryptor.encrypt(pre_master, rng);
+
+ if(using_version == SSL_V3)
+ include_length = false;
+ }
+ else
+ throw Invalid_Argument("Client_Key_Exchange: Key not RSA or DH");
+
+ send(writer, hash);
+ }
+
+/**
+* Read a Client Key Exchange message
+*/
+Client_Key_Exchange::Client_Key_Exchange(const MemoryRegion<byte>& contents,
+ const CipherSuite& suite,
+ Version_Code using_version)
+ {
+ include_length = true;
+
+ if(using_version == SSL_V3 && (suite.kex_type() == TLS_ALGO_KEYEXCH_RSA))
+ include_length = false;
+
+ deserialize(contents);
+ }
+
+/**
+* Serialize a Client Key Exchange message
+*/
+SecureVector<byte> Client_Key_Exchange::serialize() const
+ {
+ if(include_length)
+ {
+ SecureVector<byte> buf;
+ append_tls_length_value(buf, key_material, 2);
+ return buf;
+ }
+ else
+ return key_material;
+ }
+
+/**
+* Deserialize a Client Key Exchange message
+*/
+void Client_Key_Exchange::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(include_length)
+ {
+ TLS_Data_Reader reader(buf);
+ key_material = reader.get_range<byte>(2, 0, 65535);
+ }
+ else
+ key_material = buf;
+ }
+
+/**
+* Return the pre_master_secret
+*/
+SecureVector<byte>
+Client_Key_Exchange::pre_master_secret(RandomNumberGenerator& rng,
+ const Private_Key* priv_key,
+ Version_Code version)
+ {
+
+ if(const DH_PrivateKey* dh_priv = dynamic_cast<const DH_PrivateKey*>(priv_key))
+ {
+ try {
+ PK_Key_Agreement ka(*dh_priv, "Raw");
+
+ pre_master = ka.derive_key(0, key_material).bits_of();
+ }
+ catch(...)
+ {
+ /*
+ * Something failed in the DH computation. To avoid possible
+ * timing attacks, randomize the pre-master output and carry
+ * on, allowing the protocol to fail later in the finished
+ * checks.
+ */
+ pre_master = rng.random_vec(dh_priv->public_value().size());
+ }
+
+ return pre_master;
+ }
+ else if(const RSA_PrivateKey* rsa_priv = dynamic_cast<const RSA_PrivateKey*>(priv_key))
+ {
+ PK_Decryptor_EME decryptor(*rsa_priv, "PKCS1v15");
+
+ try {
+ pre_master = decryptor.decrypt(key_material);
+
+ if(pre_master.size() != 48 ||
+ make_u16bit(pre_master[0], pre_master[1]) != version)
+ throw Decoding_Error("Client_Key_Exchange: Secret corrupted");
+ }
+ catch(...)
+ {
+ pre_master = rng.random_vec(48);
+ pre_master[0] = (version >> 8) & 0xFF;
+ pre_master[1] = (version ) & 0xFF;
+ }
+
+ return pre_master;
+ }
+ else
+ throw Invalid_Argument("Client_Key_Exchange: Bad key for decrypt");
+ }
+
+/**
+* Return the pre_master_secret
+*/
+SecureVector<byte> Client_Key_Exchange::pre_master_secret() const
+ {
+ return pre_master;
+ }
+
+}
diff --git a/src/tls/cert_req.cpp b/src/tls/cert_req.cpp
new file mode 100644
index 000000000..b8b2624bf
--- /dev/null
+++ b/src/tls/cert_req.cpp
@@ -0,0 +1,151 @@
+/*
+* Certificate Request Message
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/loadstor.h>
+#include <botan/secqueue.h>
+
+namespace Botan {
+
+/**
+* Create a new Certificate Request message
+*/
+Certificate_Req::Certificate_Req(Record_Writer& writer,
+ HandshakeHash& hash,
+ const std::vector<X509_Certificate>& certs)
+ {
+ for(size_t i = 0; i != certs.size(); ++i)
+ names.push_back(certs[i].subject_dn());
+
+ // FIXME: should be able to choose what to ask for
+ types.push_back(RSA_CERT);
+ types.push_back(DSS_CERT);
+
+ send(writer, hash);
+ }
+
+/**
+* Serialize a Certificate Request message
+*/
+SecureVector<byte> Certificate_Req::serialize() const
+ {
+ SecureVector<byte> buf;
+
+ append_tls_length_value(buf, types, 1);
+
+ DER_Encoder encoder;
+ for(size_t i = 0; i != names.size(); ++i)
+ encoder.encode(names[i]);
+
+ append_tls_length_value(buf, encoder.get_contents(), 2);
+
+ return buf;
+ }
+
+/**
+* Deserialize a Certificate Request message
+*/
+void Certificate_Req::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() < 4)
+ throw Decoding_Error("Certificate_Req: Bad certificate request");
+
+ size_t types_size = buf[0];
+
+ if(buf.size() < types_size + 3)
+ throw Decoding_Error("Certificate_Req: Bad certificate request");
+
+ for(size_t i = 0; i != types_size; ++i)
+ types.push_back(static_cast<Certificate_Type>(buf[i+1]));
+
+ size_t names_size = make_u16bit(buf[types_size+2], buf[types_size+3]);
+
+ if(buf.size() != names_size + types_size + 3)
+ throw Decoding_Error("Certificate_Req: Bad certificate request");
+
+ BER_Decoder decoder(&buf[types_size + 3], names_size);
+
+ while(decoder.more_items())
+ {
+ X509_DN name;
+ decoder.decode(name);
+ names.push_back(name);
+ }
+ }
+
+/**
+* Create a new Certificate message
+*/
+Certificate::Certificate(Record_Writer& writer,
+ const std::vector<X509_Certificate>& cert_list,
+ HandshakeHash& hash)
+ {
+ certs = cert_list;
+ send(writer, hash);
+ }
+
+/**
+* Serialize a Certificate message
+*/
+SecureVector<byte> Certificate::serialize() const
+ {
+ SecureVector<byte> buf(3);
+
+ for(size_t i = 0; i != certs.size(); ++i)
+ {
+ SecureVector<byte> raw_cert = certs[i].BER_encode();
+ const size_t cert_size = raw_cert.size();
+ for(size_t i = 0; i != 3; ++i)
+ buf.push_back(get_byte<u32bit>(i+1, cert_size));
+ buf += raw_cert;
+ }
+
+ const size_t buf_size = buf.size() - 3;
+ for(size_t i = 0; i != 3; ++i)
+ buf[i] = get_byte<u32bit>(i+1, buf_size);
+
+ return buf;
+ }
+
+/**
+* Deserialize a Certificate message
+*/
+void Certificate::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() < 3)
+ throw Decoding_Error("Certificate: Message malformed");
+
+ const size_t total_size = make_u32bit(0, buf[0], buf[1], buf[2]);
+
+ SecureQueue queue;
+ queue.write(&buf[3], buf.size() - 3);
+
+ if(queue.size() != total_size)
+ throw Decoding_Error("Certificate: Message malformed");
+
+ while(queue.size())
+ {
+ if(queue.size() < 3)
+ throw Decoding_Error("Certificate: Message malformed");
+
+ byte len[3];
+ queue.read(len, 3);
+
+ const size_t cert_size = make_u32bit(0, len[0], len[1], len[2]);
+ const size_t original_size = queue.size();
+
+ X509_Certificate cert(queue);
+ if(queue.size() + cert_size != original_size)
+ throw Decoding_Error("Certificate: Message malformed");
+ certs.push_back(cert);
+ }
+ }
+
+}
diff --git a/src/tls/cert_ver.cpp b/src/tls/cert_ver.cpp
new file mode 100644
index 000000000..3220a8c9e
--- /dev/null
+++ b/src/tls/cert_ver.cpp
@@ -0,0 +1,98 @@
+/*
+* Certificate Verify Message
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/pubkey.h>
+#include <botan/rsa.h>
+#include <botan/dsa.h>
+#include <botan/loadstor.h>
+#include <memory>
+
+namespace Botan {
+
+/**
+* Create a new Certificate Verify message
+*/
+Certificate_Verify::Certificate_Verify(RandomNumberGenerator& rng,
+ Record_Writer& writer,
+ HandshakeHash& hash,
+ const Private_Key* priv_key)
+ {
+ std::string padding = "";
+ Signature_Format format = IEEE_1363;
+
+ if(priv_key->algo_name() == "RSA")
+ padding = "EMSA3(TLS.Digest.0)";
+ else if(priv_key->algo_name() == "DSA")
+ {
+ padding == "EMSA1(SHA-1)";
+ format = DER_SEQUENCE;
+ }
+ else
+ throw Invalid_Argument(priv_key->algo_name() +
+ " is invalid/unknown for TLS signatures");
+
+ PK_Signer signer(*priv_key, padding, format);
+
+ signature = signer.sign_message(hash.final(), rng);
+ send(writer, hash);
+ }
+
+/**
+* Serialize a Certificate Verify message
+*/
+SecureVector<byte> Certificate_Verify::serialize() const
+ {
+ SecureVector<byte> buf;
+
+ const u16bit sig_len = signature.size();
+ buf.push_back(get_byte(0, sig_len));
+ buf.push_back(get_byte(1, sig_len));
+ buf += signature;
+
+ return buf;
+ }
+
+/**
+* Deserialize a Certificate Verify message
+*/
+void Certificate_Verify::deserialize(const MemoryRegion<byte>& buf)
+ {
+ TLS_Data_Reader reader(buf);
+ signature = reader.get_range<byte>(2, 0, 65535);
+ }
+
+/**
+* Verify a Certificate Verify message
+*/
+bool Certificate_Verify::verify(const X509_Certificate& cert,
+ HandshakeHash& hash)
+ {
+ // FIXME: duplicate of Server_Key_Exchange::verify
+
+ std::auto_ptr<Public_Key> key(cert.subject_public_key());
+
+ std::string padding = "";
+ Signature_Format format = IEEE_1363;
+
+ if(key->algo_name() == "RSA")
+ padding = "EMSA3(TLS.Digest.0)";
+ else if(key->algo_name() == "DSA")
+ {
+ padding == "EMSA1(SHA-1)";
+ format = DER_SEQUENCE;
+ }
+ else
+ throw Invalid_Argument(key->algo_name() +
+ " is invalid/unknown for TLS signatures");
+
+ PK_Verifier verifier(*key, padding, format);
+ return verifier.verify_message(hash.final(), signature);
+ }
+
+}
diff --git a/src/tls/finished.cpp b/src/tls/finished.cpp
new file mode 100644
index 000000000..d76fbd884
--- /dev/null
+++ b/src/tls/finished.cpp
@@ -0,0 +1,100 @@
+/*
+* Finished Message
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/prf_tls.h>
+
+namespace Botan {
+
+/**
+* Create a new Finished message
+*/
+Finished::Finished(Record_Writer& writer,
+ Version_Code version, Connection_Side side,
+ const MemoryRegion<byte>& master_secret,
+ HandshakeHash& hash)
+ {
+ verification_data = compute_verify(master_secret, hash, side, version);
+ send(writer, hash);
+ }
+
+/**
+* Serialize a Finished message
+*/
+SecureVector<byte> Finished::serialize() const
+ {
+ return verification_data;
+ }
+
+/**
+* Deserialize a Finished message
+*/
+void Finished::deserialize(const MemoryRegion<byte>& buf)
+ {
+ verification_data = buf;
+ }
+
+/**
+* Verify a Finished message
+*/
+bool Finished::verify(const MemoryRegion<byte>& secret, Version_Code version,
+ const HandshakeHash& hash, Connection_Side side)
+ {
+ SecureVector<byte> computed = compute_verify(secret, hash, side, version);
+ if(computed == verification_data)
+ return true;
+ return false;
+ }
+
+/**
+* Compute the verify_data
+*/
+SecureVector<byte> Finished::compute_verify(const MemoryRegion<byte>& secret,
+ HandshakeHash hash,
+ Connection_Side side,
+ Version_Code version)
+ {
+ if(version == SSL_V3)
+ {
+ const byte SSL_CLIENT_LABEL[] = { 0x43, 0x4C, 0x4E, 0x54 };
+ const byte SSL_SERVER_LABEL[] = { 0x53, 0x52, 0x56, 0x52 };
+
+ SecureVector<byte> ssl3_finished;
+
+ if(side == CLIENT)
+ hash.update(SSL_CLIENT_LABEL, sizeof(SSL_CLIENT_LABEL));
+ else
+ hash.update(SSL_SERVER_LABEL, sizeof(SSL_SERVER_LABEL));
+
+ return hash.final_ssl3(secret);
+ }
+ else if(version == TLS_V10 || version == TLS_V11)
+ {
+ const byte TLS_CLIENT_LABEL[] = {
+ 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x66, 0x69, 0x6E, 0x69,
+ 0x73, 0x68, 0x65, 0x64 };
+
+ const byte TLS_SERVER_LABEL[] = {
+ 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x66, 0x69, 0x6E, 0x69,
+ 0x73, 0x68, 0x65, 0x64 };
+
+ TLS_PRF prf;
+
+ SecureVector<byte> input;
+ if(side == CLIENT)
+ input += std::make_pair(TLS_CLIENT_LABEL, sizeof(TLS_CLIENT_LABEL));
+ else
+ input += std::make_pair(TLS_SERVER_LABEL, sizeof(TLS_SERVER_LABEL));
+ input += hash.final();
+
+ return prf.derive_key(12, secret, input);
+ }
+ else
+ throw Invalid_Argument("Finished message: Unknown protocol version");
+ }
+
+}
diff --git a/src/tls/hello.cpp b/src/tls/hello.cpp
new file mode 100644
index 000000000..ae0d9607b
--- /dev/null
+++ b/src/tls/hello.cpp
@@ -0,0 +1,331 @@
+/*
+* TLS Hello Messages
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+
+namespace Botan {
+
+/*
+* Encode and send a Handshake message
+*/
+void HandshakeMessage::send(Record_Writer& writer, HandshakeHash& hash) const
+ {
+ SecureVector<byte> buf = serialize();
+ SecureVector<byte> send_buf(4);
+
+ const size_t buf_size = buf.size();
+
+ send_buf[0] = type();
+
+ for(size_t i = 1; i != 4; ++i)
+ send_buf[i] = get_byte<u32bit>(i, buf_size);
+
+ send_buf += buf;
+
+ hash.update(send_buf);
+
+ writer.send(HANDSHAKE, &send_buf[0], send_buf.size());
+ writer.flush();
+ }
+
+/*
+* Create a new Hello Request message
+*/
+Hello_Request::Hello_Request(Record_Writer& writer)
+ {
+ HandshakeHash dummy; // FIXME: *UGLY*
+ send(writer, dummy);
+ }
+
+/*
+* Serialize a Hello Request message
+*/
+SecureVector<byte> Hello_Request::serialize() const
+ {
+ return SecureVector<byte>();
+ }
+
+/*
+* Deserialize a Hello Request message
+*/
+void Hello_Request::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size())
+ throw Decoding_Error("Hello_Request: Must be empty, and is not");
+ }
+
+/*
+* Create a new Client Hello message
+*/
+Client_Hello::Client_Hello(RandomNumberGenerator& rng,
+ Record_Writer& writer,
+ const TLS_Policy& policy,
+ HandshakeHash& hash)
+ {
+ c_random = rng.random_vec(32);
+
+ suites = policy.ciphersuites();
+ comp_algos = policy.compression();
+ c_version = policy.pref_version();
+
+ send(writer, hash);
+ }
+
+/*
+* Serialize a Client Hello message
+*/
+SecureVector<byte> Client_Hello::serialize() const
+ {
+ SecureVector<byte> buf;
+
+ buf.push_back(static_cast<byte>(c_version >> 8));
+ buf.push_back(static_cast<byte>(c_version ));
+ buf += c_random;
+
+ append_tls_length_value(buf, sess_id, 1);
+ append_tls_length_value(buf, suites, 2);
+ append_tls_length_value(buf, comp_algos, 1);
+
+ return buf;
+ }
+
+void Client_Hello::deserialize_sslv2(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() < 12 || buf[0] != 1)
+ throw Decoding_Error("Client_Hello: SSLv2 hello corrupted");
+
+ const size_t cipher_spec_len = make_u16bit(buf[3], buf[4]);
+ const size_t sess_id_len = make_u16bit(buf[5], buf[6]);
+ const size_t challenge_len = make_u16bit(buf[7], buf[8]);
+
+ const size_t expected_size =
+ (9 + sess_id_len + cipher_spec_len + challenge_len);
+
+ if(buf.size() != expected_size)
+ throw Decoding_Error("Client_Hello: SSLv2 hello corrupted");
+
+ if(sess_id_len != 0 || cipher_spec_len % 3 != 0 ||
+ (challenge_len < 16 || challenge_len > 32))
+ {
+ throw Decoding_Error("Client_Hello: SSLv2 hello corrupted");
+ }
+
+ for(size_t i = 9; i != 9 + cipher_spec_len; i += 3)
+ {
+ if(buf[i] != 0) // a SSLv2 cipherspec; ignore it
+ continue;
+
+ suites.push_back(make_u16bit(buf[i+1], buf[i+2]));
+ }
+
+ c_version = static_cast<Version_Code>(make_u16bit(buf[1], buf[2]));
+
+ c_random.resize(challenge_len);
+ copy_mem(&c_random[0], &buf[9+cipher_spec_len+sess_id_len], challenge_len);
+ }
+
+/*
+* Deserialize a Client Hello message
+*/
+void Client_Hello::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() == 0)
+ throw Decoding_Error("Client_Hello: Packet corrupted");
+
+ if(buf.size() < 41)
+ throw Decoding_Error("Client_Hello: Packet corrupted");
+
+ TLS_Data_Reader reader(buf);
+
+ c_version = static_cast<Version_Code>(reader.get_u16bit());
+ c_random = reader.get_fixed<byte>(32);
+
+ sess_id = reader.get_range<byte>(1, 0, 32);
+
+ suites = reader.get_range_vector<u16bit>(2, 1, 32767);
+
+ comp_algos = reader.get_range_vector<byte>(1, 1, 255);
+
+ if(reader.has_remaining())
+ {
+ const u16bit all_extn_size = reader.get_u16bit();
+
+ if(reader.remaining_bytes() != all_extn_size)
+ throw Decoding_Error("Client_Hello: Bad extension size");
+
+ while(reader.has_remaining())
+ {
+ const u16bit extension_code = reader.get_u16bit();
+ const u16bit extension_size = reader.get_u16bit();
+
+ if(extension_code == TLSEXT_SERVER_NAME_INDICATION)
+ {
+ u16bit name_bytes = reader.get_u16bit();
+
+ while(name_bytes)
+ {
+ byte name_type = reader.get_byte();
+ name_bytes--;
+
+ if(name_type == 0) // DNS
+ {
+ std::vector<byte> name =
+ reader.get_range_vector<byte>(2, 1, 65535);
+
+ requested_hostname.assign(
+ reinterpret_cast<const char*>(&name[0]),
+ name.size());
+
+ name_bytes -= (2 + name.size());
+ }
+ else
+ {
+ reader.discard_next(name_bytes);
+ name_bytes = 0;
+ }
+ }
+ }
+ else if(extension_code == TLSEXT_SRP_IDENTIFIER)
+ {
+ std::vector<byte> name = reader.get_range_vector<byte>(1, 1, 255);
+
+ requested_srp_id.assign(
+ reinterpret_cast<char*>(&name[0]),
+ name.size());
+ }
+ else
+ {
+ reader.discard_next(extension_size);
+ }
+ }
+ }
+ }
+
+/*
+* Check if we offered this ciphersuite
+*/
+bool Client_Hello::offered_suite(u16bit ciphersuite) const
+ {
+ for(size_t i = 0; i != suites.size(); ++i)
+ if(suites[i] == ciphersuite)
+ return true;
+ return false;
+ }
+
+/*
+* Create a new Server Hello message
+*/
+Server_Hello::Server_Hello(RandomNumberGenerator& rng,
+ Record_Writer& writer,
+ const TLS_Policy& policy,
+ const std::vector<X509_Certificate>& certs,
+ const Client_Hello& c_hello,
+ Version_Code ver,
+ HandshakeHash& hash)
+ {
+ bool have_rsa = false, have_dsa = false;
+
+ for(size_t i = 0; i != certs.size(); ++i)
+ {
+ Public_Key* key = certs[i].subject_public_key();
+ if(key->algo_name() == "RSA")
+ have_rsa = true;
+
+ if(key->algo_name() == "DSA")
+ have_dsa = true;
+ }
+
+ suite = policy.choose_suite(c_hello.ciphersuites(), have_rsa, have_dsa);
+
+ if(suite == 0)
+ throw TLS_Exception(PROTOCOL_VERSION,
+ "Can't agree on a ciphersuite with client");
+
+ comp_algo = policy.choose_compression(c_hello.compression_algos());
+
+ s_version = ver;
+ s_random = rng.random_vec(32);
+
+ send(writer, hash);
+ }
+
+/*
+* Serialize a Server Hello message
+*/
+SecureVector<byte> Server_Hello::serialize() const
+ {
+ SecureVector<byte> buf;
+
+ buf.push_back(static_cast<byte>(s_version >> 8));
+ buf.push_back(static_cast<byte>(s_version ));
+ buf += s_random;
+
+ append_tls_length_value(buf, sess_id, 1);
+
+ buf.push_back(get_byte(0, suite));
+ buf.push_back(get_byte(1, suite));
+
+ buf.push_back(comp_algo);
+
+ return buf;
+ }
+
+/*
+* Deserialize a Server Hello message
+*/
+void Server_Hello::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() < 38)
+ throw Decoding_Error("Server_Hello: Packet corrupted");
+
+ TLS_Data_Reader reader(buf);
+
+ s_version = static_cast<Version_Code>(reader.get_u16bit());
+
+ if(s_version != SSL_V3 && s_version != TLS_V10 && s_version != TLS_V11)
+ {
+ throw TLS_Exception(PROTOCOL_VERSION,
+ "Server_Hello: Unsupported server version");
+ }
+
+ s_random = reader.get_fixed<byte>(32);
+
+ sess_id = reader.get_range<byte>(1, 0, 32);
+
+ suite = reader.get_u16bit();
+
+ comp_algo = reader.get_byte();
+ }
+
+/*
+* Create a new Server Hello Done message
+*/
+Server_Hello_Done::Server_Hello_Done(Record_Writer& writer,
+ HandshakeHash& hash)
+ {
+ send(writer, hash);
+ }
+
+/*
+* Serialize a Server Hello Done message
+*/
+SecureVector<byte> Server_Hello_Done::serialize() const
+ {
+ return SecureVector<byte>();
+ }
+
+/*
+* Deserialize a Server Hello Done message
+*/
+void Server_Hello_Done::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size())
+ throw Decoding_Error("Server_Hello_Done: Must be empty, and is not");
+ }
+
+}
diff --git a/src/tls/info.txt b/src/tls/info.txt
new file mode 100644
index 000000000..1170fef45
--- /dev/null
+++ b/src/tls/info.txt
@@ -0,0 +1,66 @@
+define SSL_TLS
+
+<comment>
+The SSL/TLS code is complex, new, and not yet reviewed, there may be
+serious bugs or security issues.
+</comment>
+
+uses_tr1 yes
+
+<header:public>
+tls_client.h
+tls_exceptn.h
+tls_magic.h
+tls_policy.h
+tls_record.h
+tls_server.h
+tls_session_key.h
+tls_suites.h
+</header:public>
+
+<header:internal>
+tls_alerts.h
+tls_handshake_hash.h
+tls_messages.h
+tls_reader.h
+tls_state.h
+</header:internal>
+
+<source>
+c_kex.cpp
+cert_req.cpp
+cert_ver.cpp
+finished.cpp
+tls_handshake_hash.cpp
+hello.cpp
+rec_read.cpp
+rec_wri.cpp
+s_kex.cpp
+tls_client.cpp
+tls_policy.cpp
+tls_server.cpp
+tls_session_key.cpp
+tls_state.cpp
+tls_suites.cpp
+</source>
+
+<requires>
+aes
+arc4
+asn1
+des
+dh
+dsa
+eme_pkcs
+emsa3
+filters
+hmac
+md5
+prf_ssl3
+prf_tls
+rng
+rsa
+sha1
+ssl3mac
+x509cert
+</requires>
diff --git a/src/tls/rec_read.cpp b/src/tls/rec_read.cpp
new file mode 100644
index 000000000..4e5b69780
--- /dev/null
+++ b/src/tls/rec_read.cpp
@@ -0,0 +1,255 @@
+/*
+* TLS Record Reading
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/tls_record.h>
+#include <botan/lookup.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+/*
+* Reset the state
+*/
+void Record_Reader::reset()
+ {
+ cipher.reset();
+
+ delete mac;
+ mac = 0;
+
+ mac_size = 0;
+ block_size = 0;
+ iv_size = 0;
+ major = minor = 0;
+ seq_no = 0;
+ }
+
+/*
+* Set the version to use
+*/
+void Record_Reader::set_version(Version_Code version)
+ {
+ if(version != SSL_V3 && version != TLS_V10 && version != TLS_V11)
+ throw Invalid_Argument("Record_Reader: Invalid protocol version");
+
+ major = (version >> 8) & 0xFF;
+ minor = (version & 0xFF);
+ }
+
+/*
+* Set the keys for reading
+*/
+void Record_Reader::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.server_cipher_key();
+ iv = keys.server_iv();
+ mac_key = keys.server_mac_key();
+ }
+ else
+ {
+ cipher_key = keys.client_cipher_key();
+ iv = keys.client_iv();
+ mac_key = keys.client_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, DECRYPTION)
+ );
+ 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, DECRYPTION));
+ block_size = 0;
+ iv_size = 0;
+ }
+ else
+ throw Invalid_Argument("Record_Reader: 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_Reader: Unknown hash " + mac_algo);
+ }
+
+void Record_Reader::add_input(const byte input[], size_t input_size)
+ {
+ input_queue.write(input, input_size);
+ }
+
+/*
+* Retrieve the next record
+*/
+size_t Record_Reader::get_record(byte& msg_type,
+ MemoryRegion<byte>& output)
+ {
+ byte header[5] = { 0 };
+
+ const size_t have_in_queue = input_queue.size();
+
+ if(have_in_queue < sizeof(header))
+ return (sizeof(header) - have_in_queue);
+
+ /*
+ * We peek first to make sure we have the full record
+ */
+ input_queue.peek(header, sizeof(header));
+
+ // SSLv2-format client hello?
+ if(header[0] & 0x80 && header[2] == 1 && header[3] == 3)
+ {
+ size_t record_len = make_u16bit(header[0], header[1]) & 0x7FFF;
+
+ if(have_in_queue < record_len + 2)
+ return (record_len + 2 - have_in_queue);
+
+ msg_type = HANDSHAKE;
+ output.resize(record_len + 4);
+
+ input_queue.read(&output[2], record_len + 2);
+ output[0] = CLIENT_HELLO_SSLV2;
+ output[1] = 0;
+ output[2] = header[0] & 0x7F;
+ output[3] = header[1];
+
+ return 0;
+ }
+
+ if(header[0] != CHANGE_CIPHER_SPEC &&
+ header[0] != ALERT &&
+ header[0] != HANDSHAKE &&
+ header[0] != APPLICATION_DATA)
+ {
+ throw TLS_Exception(UNEXPECTED_MESSAGE,
+ "Record_Reader: Unknown record type");
+ }
+
+ 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");
+
+ // 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[0], 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)
+ {
+ 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);
+
+ size_t pad_size = 0;
+
+ 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 > block_size)
+ pad_size = 0;
+ }
+ else
+ {
+ for(size_t i = 0; i != pad_size; ++i)
+ if(plaintext[plaintext.size()-i-1] != pad_value)
+ pad_size = 0;
+ }
+ }
+
+ if(plaintext.size() < mac_size + pad_size + iv_size)
+ throw Decoding_Error("Record_Reader: Record truncated");
+
+ const size_t mac_offset = plaintext.size() - (mac_size + pad_size);
+ SecureVector<byte> received_mac(&plaintext[mac_offset],
+ mac_size);
+
+ const u16bit plain_length = plaintext.size() - (mac_size + pad_size + iv_size);
+
+ mac->update_be(seq_no);
+ mac->update(header[0]); // msg_type
+
+ if(version != SSL_V3)
+ for(size_t i = 0; i != 2; ++i)
+ mac->update(get_byte(i, version));
+
+ mac->update_be(plain_length);
+ mac->update(&plaintext[iv_size], plain_length);
+
+ ++seq_no;
+
+ SecureVector<byte> computed_mac = mac->final();
+
+ if(received_mac != computed_mac)
+ throw TLS_Exception(BAD_RECORD_MAC, "Record_Reader: MAC failure");
+
+ msg_type = header[0];
+
+ output.resize(plain_length);
+ copy_mem(&output[0], &plaintext[iv_size], plain_length);
+ return 0;
+ }
+
+}
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();
+ }
+
+}
diff --git a/src/tls/s_kex.cpp b/src/tls/s_kex.cpp
new file mode 100644
index 000000000..1e7de31d0
--- /dev/null
+++ b/src/tls/s_kex.cpp
@@ -0,0 +1,180 @@
+/*
+* Server Key Exchange Message
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_messages.h>
+#include <botan/internal/tls_reader.h>
+#include <botan/pubkey.h>
+#include <botan/dh.h>
+#include <botan/rsa.h>
+#include <botan/dsa.h>
+#include <botan/loadstor.h>
+#include <memory>
+
+namespace Botan {
+
+/**
+* Create a new Server Key Exchange message
+*/
+Server_Key_Exchange::Server_Key_Exchange(RandomNumberGenerator& rng,
+ Record_Writer& writer,
+ const Public_Key* kex_key,
+ const Private_Key* priv_key,
+ const MemoryRegion<byte>& c_random,
+ const MemoryRegion<byte>& s_random,
+ HandshakeHash& hash)
+ {
+ const DH_PublicKey* dh_pub = dynamic_cast<const DH_PublicKey*>(kex_key);
+ const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(kex_key);
+
+ if(dh_pub)
+ {
+ params.push_back(dh_pub->get_domain().get_p());
+ params.push_back(dh_pub->get_domain().get_g());
+ params.push_back(BigInt::decode(dh_pub->public_value()));
+ }
+ else if(rsa_pub)
+ {
+ params.push_back(rsa_pub->get_n());
+ params.push_back(rsa_pub->get_e());
+ }
+ else
+ throw Invalid_Argument("Bad key for TLS key exchange: not DH or RSA");
+
+
+ std::string padding = "";
+ Signature_Format format = IEEE_1363;
+
+ if(priv_key->algo_name() == "RSA")
+ padding = "EMSA3(TLS.Digest.0)";
+ else if(priv_key->algo_name() == "DSA")
+ {
+ padding = "EMSA1(SHA-1)";
+ format = DER_SEQUENCE;
+ }
+ else
+ throw Invalid_Argument(priv_key->algo_name() +
+ " is invalid/unknown for TLS signatures");
+
+ PK_Signer signer(*priv_key, padding, format);
+
+ signer.update(c_random);
+ signer.update(s_random);
+ signer.update(serialize_params());
+ signature = signer.signature(rng);
+
+ send(writer, hash);
+ }
+
+/**
+* Serialize a Server Key Exchange message
+*/
+SecureVector<byte> Server_Key_Exchange::serialize() const
+ {
+ SecureVector<byte> buf = serialize_params();
+ append_tls_length_value(buf, signature, 2);
+ return buf;
+ }
+
+/**
+* Serialize the ServerParams structure
+*/
+SecureVector<byte> Server_Key_Exchange::serialize_params() const
+ {
+ SecureVector<byte> buf;
+
+ for(size_t i = 0; i != params.size(); ++i)
+ append_tls_length_value(buf, BigInt::encode(params[i]), 2);
+
+ return buf;
+ }
+
+/**
+* Deserialize a Server Key Exchange message
+*/
+void Server_Key_Exchange::deserialize(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() < 6)
+ throw Decoding_Error("Server_Key_Exchange: Packet corrupted");
+
+ SecureVector<byte> values[4];
+ size_t so_far = 0;
+
+ for(size_t i = 0; i != 4; ++i)
+ {
+ const u16bit len = make_u16bit(buf[so_far], buf[so_far+1]);
+ so_far += 2;
+
+ if(len + so_far > buf.size())
+ throw Decoding_Error("Server_Key_Exchange: Packet corrupted");
+
+ values[i].resize(len);
+ copy_mem(&values[i][0], &buf[so_far], len);
+ so_far += len;
+
+ if(i == 2 && so_far == buf.size())
+ break;
+ }
+
+ params.push_back(BigInt::decode(values[0]));
+ params.push_back(BigInt::decode(values[1]));
+ if(values[3].size())
+ {
+ params.push_back(BigInt::decode(values[2]));
+ signature = values[3];
+ }
+ else
+ signature = values[2];
+ }
+
+/**
+* Return the public key
+*/
+Public_Key* Server_Key_Exchange::key() const
+ {
+ if(params.size() == 2)
+ return new RSA_PublicKey(params[0], params[1]);
+ else if(params.size() == 3)
+ return new DH_PublicKey(DL_Group(params[0], params[1]), params[2]);
+ else
+ throw Internal_Error("Server_Key_Exchange::key: No key set");
+ }
+
+/**
+* Verify a Server Key Exchange message
+*/
+bool Server_Key_Exchange::verify(const X509_Certificate& cert,
+ const MemoryRegion<byte>& c_random,
+ const MemoryRegion<byte>& s_random) const
+ {
+
+ std::auto_ptr<Public_Key> key(cert.subject_public_key());
+
+ std::string padding = "";
+ Signature_Format format = IEEE_1363;
+
+ if(key->algo_name() == "RSA")
+ padding = "EMSA3(TLS.Digest.0)";
+ else if(key->algo_name() == "DSA")
+ {
+ padding == "EMSA1(SHA-1)";
+ format = DER_SEQUENCE;
+ }
+ else
+ throw Invalid_Argument(key->algo_name() +
+ " is invalid/unknown for TLS signatures");
+
+ PK_Verifier verifier(*key, padding, format);
+
+ SecureVector<byte> params_got = serialize_params();
+ verifier.update(c_random);
+ verifier.update(s_random);
+ verifier.update(params_got);
+
+ return verifier.check_signature(signature);
+ }
+
+}
diff --git a/src/tls/tls_alerts.h b/src/tls/tls_alerts.h
new file mode 100644
index 000000000..241599aa8
--- /dev/null
+++ b/src/tls/tls_alerts.h
@@ -0,0 +1,54 @@
+/*
+* Alert Message
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_ALERT_H__
+#define BOTAN_TLS_ALERT_H__
+
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+/**
+* SSL/TLS Alert Message
+*/
+class Alert
+ {
+ public:
+ /**
+ * @return if this alert is a fatal one or not
+ */
+ bool is_fatal() const { return fatal; }
+
+ /**
+ * @return type of alert
+ */
+ Alert_Type type() const { return type_code; }
+
+ /**
+ * Deserialize an Alert message
+ * @param buf the serialized alert
+ */
+ Alert(const MemoryRegion<byte>& buf)
+ {
+ if(buf.size() != 2)
+ throw Decoding_Error("Alert: Bad size for alert message");
+
+ if(buf[0] == 1) fatal = false;
+ else if(buf[0] == 2) fatal = true;
+ else
+ throw Decoding_Error("Alert: Bad type code for alert level");
+
+ type_code = static_cast<Alert_Type>(buf[1]);
+ }
+ private:
+ bool fatal;
+ Alert_Type type_code;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp
new file mode 100644
index 000000000..cfa86881c
--- /dev/null
+++ b/src/tls/tls_client.cpp
@@ -0,0 +1,499 @@
+/*
+* TLS Client
+* (C) 2004-2011 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/tls_client.h>
+#include <botan/internal/tls_alerts.h>
+#include <botan/internal/tls_state.h>
+#include <botan/loadstor.h>
+#include <botan/rsa.h>
+#include <botan/dsa.h>
+#include <botan/dh.h>
+
+namespace Botan {
+
+namespace {
+
+/**
+* Verify the state transition is allowed
+* FIXME: checks are wrong for session reuse (add a flag for that)
+*/
+void client_check_state(Handshake_Type new_msg, Handshake_State* state)
+ {
+ class State_Transition_Error : public Unexpected_Message
+ {
+ public:
+ State_Transition_Error(const std::string& err) :
+ Unexpected_Message("State transition error from " + err) {}
+ };
+
+ if(new_msg == HELLO_REQUEST)
+ {
+ if(state->client_hello)
+ throw State_Transition_Error("HelloRequest");
+ }
+ else if(new_msg == SERVER_HELLO)
+ {
+ if(!state->client_hello || state->server_hello)
+ throw State_Transition_Error("ServerHello");
+ }
+ else if(new_msg == CERTIFICATE)
+ {
+ if(!state->server_hello || state->server_kex ||
+ state->cert_req || state->server_hello_done)
+ throw State_Transition_Error("ServerCertificate");
+ }
+ else if(new_msg == SERVER_KEX)
+ {
+ if(!state->server_hello || state->server_kex ||
+ state->cert_req || state->server_hello_done)
+ throw State_Transition_Error("ServerKeyExchange");
+ }
+ else if(new_msg == CERTIFICATE_REQUEST)
+ {
+ if(!state->server_certs || state->cert_req || state->server_hello_done)
+ throw State_Transition_Error("CertificateRequest");
+ }
+ else if(new_msg == SERVER_HELLO_DONE)
+ {
+ if(!state->server_hello || state->server_hello_done)
+ throw State_Transition_Error("ServerHelloDone");
+ }
+ else if(new_msg == HANDSHAKE_CCS)
+ {
+ if(!state->client_finished || state->server_finished)
+ throw State_Transition_Error("ServerChangeCipherSpec");
+ }
+ else if(new_msg == FINISHED)
+ {
+ if(!state->got_server_ccs)
+ throw State_Transition_Error("ServerFinished");
+ }
+ else
+ throw Unexpected_Message("Unexpected message in handshake");
+ }
+
+}
+
+/**
+* TLS Client Constructor
+*/
+TLS_Client::TLS_Client(std::tr1::function<void (const byte[], size_t)> socket_output_fn,
+ std::tr1::function<void (const byte[], size_t, u16bit)> process_fn,
+ const TLS_Policy& policy,
+ RandomNumberGenerator& rng) :
+ policy(policy),
+ rng(rng),
+ proc_fn(process_fn),
+ writer(socket_output_fn),
+ state(0),
+ active(false)
+ {
+ writer.set_version(policy.pref_version());
+
+ state = new Handshake_State;
+ state->client_hello = new Client_Hello(rng, writer, policy, state->hash);
+ }
+
+void TLS_Client::add_client_cert(const X509_Certificate& cert,
+ Private_Key* cert_key)
+ {
+ certs.push_back(std::make_pair(cert, cert_key));
+ }
+
+/**
+* TLS Client Destructor
+*/
+TLS_Client::~TLS_Client()
+ {
+ close();
+ for(size_t i = 0; i != certs.size(); i++)
+ delete certs[i].second;
+ delete state;
+ }
+
+size_t TLS_Client::received_data(const byte buf[], size_t buf_size)
+ {
+ try
+ {
+ reader.add_input(buf, buf_size);
+
+ byte rec_type = CONNECTION_CLOSED;
+ SecureVector<byte> record;
+
+ while(!reader.currently_empty())
+ {
+ const size_t bytes_needed = reader.get_record(rec_type, record);
+
+ if(bytes_needed > 0)
+ return bytes_needed;
+
+ if(rec_type == APPLICATION_DATA)
+ {
+ if(active)
+ {
+ proc_fn(&record[0], record.size(), NO_ALERT_TYPE);
+ }
+ else
+ {
+ throw Unexpected_Message("Application data before handshake done");
+ }
+ }
+ else if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC)
+ {
+ read_handshake(rec_type, record);
+ }
+ else if(rec_type == ALERT)
+ {
+ Alert alert(record);
+
+ proc_fn(0, 0, alert.type());
+
+ if(alert.is_fatal() || alert.type() == CLOSE_NOTIFY)
+ {
+ if(alert.type() == CLOSE_NOTIFY)
+ {
+ writer.alert(WARNING, CLOSE_NOTIFY);
+ }
+
+ close(FATAL, NO_ALERT_TYPE);
+ }
+ }
+ else
+ throw Unexpected_Message("Unknown message type received");
+ }
+
+ return 0; // on a record boundary
+ }
+ catch(TLS_Exception& e)
+ {
+ close(FATAL, e.type());
+ throw;
+ }
+ catch(std::exception& e)
+ {
+ close(FATAL, INTERNAL_ERROR);
+ throw;
+ }
+ }
+
+void TLS_Client::queue_for_sending(const byte buf[], size_t buf_size)
+ {
+ if(active)
+ {
+ while(!pre_handshake_write_queue.end_of_data())
+ {
+ SecureVector<byte> q_buf(1024);
+ const size_t got = pre_handshake_write_queue.read(&q_buf[0], q_buf.size());
+ writer.send(APPLICATION_DATA, &q_buf[0], got);
+ }
+
+ writer.send(APPLICATION_DATA, buf, buf_size);
+ writer.flush();
+ }
+ else
+ pre_handshake_write_queue.write(buf, buf_size);
+ }
+
+/**
+* Close a TLS connection
+*/
+void TLS_Client::close()
+ {
+ close(WARNING, CLOSE_NOTIFY);
+ }
+
+/**
+* Close a TLS connection
+*/
+void TLS_Client::close(Alert_Level level, Alert_Type alert_code)
+ {
+ if(active)
+ {
+ active = false;
+
+ if(alert_code != NO_ALERT_TYPE)
+ {
+ try
+ {
+ writer.alert(level, alert_code);
+ writer.flush();
+ }
+ catch(...) { /* swallow it */ }
+ }
+
+ reader.reset();
+ writer.reset();
+ }
+ }
+
+/**
+* Split up and process handshake messages
+*/
+void TLS_Client::read_handshake(byte rec_type,
+ const MemoryRegion<byte>& rec_buf)
+ {
+ if(rec_type == HANDSHAKE)
+ state->queue.write(&rec_buf[0], rec_buf.size());
+
+ while(true)
+ {
+ Handshake_Type type = HANDSHAKE_NONE;
+ SecureVector<byte> contents;
+
+ if(rec_type == HANDSHAKE)
+ {
+ if(state->queue.size() >= 4)
+ {
+ byte head[4] = { 0 };
+ state->queue.peek(head, 4);
+
+ const size_t length = make_u32bit(0, head[1], head[2], head[3]);
+
+ if(state->queue.size() >= length + 4)
+ {
+ type = static_cast<Handshake_Type>(head[0]);
+ contents.resize(length);
+ state->queue.read(head, 4);
+ state->queue.read(&contents[0], contents.size());
+ }
+ }
+ }
+ else if(rec_type == CHANGE_CIPHER_SPEC)
+ {
+ if(state->queue.size() == 0 && rec_buf.size() == 1 && rec_buf[0] == 1)
+ type = HANDSHAKE_CCS;
+ else
+ throw Decoding_Error("Malformed ChangeCipherSpec message");
+ }
+ else
+ throw Decoding_Error("Unknown message type in handshake processing");
+
+ if(type == HANDSHAKE_NONE)
+ break;
+
+ process_handshake_msg(type, contents);
+
+ if(type == HANDSHAKE_CCS || !state)
+ break;
+ }
+ }
+
+/**
+* Process a handshake message
+*/
+void TLS_Client::process_handshake_msg(Handshake_Type type,
+ const MemoryRegion<byte>& contents)
+ {
+ rng.add_entropy(&contents[0], contents.size());
+
+ if(type == HELLO_REQUEST)
+ {
+ if(state == 0)
+ state = new Handshake_State();
+ else
+ return;
+ }
+
+ if(state == 0)
+ throw Unexpected_Message("Unexpected handshake message");
+
+ if(type != HANDSHAKE_CCS && type != HELLO_REQUEST && type != FINISHED)
+ {
+ state->hash.update(static_cast<byte>(type));
+ const size_t record_length = contents.size();
+ for(size_t i = 0; i != 3; i++)
+ state->hash.update(get_byte<u32bit>(i+1, record_length));
+ state->hash.update(contents);
+ }
+
+ if(type == HELLO_REQUEST)
+ {
+ client_check_state(type, state);
+
+ Hello_Request hello_request(contents);
+ state->client_hello = new Client_Hello(rng, writer, policy, state->hash);
+ }
+ else if(type == SERVER_HELLO)
+ {
+ client_check_state(type, state);
+
+ state->server_hello = new Server_Hello(contents);
+
+ if(!state->client_hello->offered_suite(
+ state->server_hello->ciphersuite()
+ )
+ )
+ throw TLS_Exception(HANDSHAKE_FAILURE,
+ "TLS_Client: Server replied with bad ciphersuite");
+
+ state->version = state->server_hello->version();
+
+ if(state->version > state->client_hello->version())
+ throw TLS_Exception(HANDSHAKE_FAILURE,
+ "TLS_Client: Server replied with bad version");
+
+ if(state->version < policy.min_version())
+ throw TLS_Exception(PROTOCOL_VERSION,
+ "TLS_Client: Server is too old for specified policy");
+
+ writer.set_version(state->version);
+ reader.set_version(state->version);
+
+ state->suite = CipherSuite(state->server_hello->ciphersuite());
+ }
+ else if(type == CERTIFICATE)
+ {
+ client_check_state(type, state);
+
+ if(state->suite.sig_type() == TLS_ALGO_SIGNER_ANON)
+ throw Unexpected_Message("Recived certificate from anonymous server");
+
+ state->server_certs = new Certificate(contents);
+
+ peer_certs = state->server_certs->cert_chain();
+ if(peer_certs.size() == 0)
+ throw TLS_Exception(HANDSHAKE_FAILURE,
+ "TLS_Client: No certificates sent by server");
+
+ if(!policy.check_cert(peer_certs))
+ throw TLS_Exception(BAD_CERTIFICATE,
+ "TLS_Client: Server certificate is not valid");
+
+ state->kex_pub = peer_certs[0].subject_public_key();
+
+ bool is_dsa = false, is_rsa = false;
+
+ if(dynamic_cast<DSA_PublicKey*>(state->kex_pub))
+ is_dsa = true;
+ else if(dynamic_cast<RSA_PublicKey*>(state->kex_pub))
+ is_rsa = true;
+ else
+ throw TLS_Exception(UNSUPPORTED_CERTIFICATE,
+ "Unknown key type received in server kex");
+
+ if((is_dsa && state->suite.sig_type() != TLS_ALGO_SIGNER_DSA) ||
+ (is_rsa && state->suite.sig_type() != TLS_ALGO_SIGNER_RSA))
+ throw TLS_Exception(ILLEGAL_PARAMETER,
+ "Certificate key type did not match ciphersuite");
+ }
+ else if(type == SERVER_KEX)
+ {
+ client_check_state(type, state);
+
+ if(state->suite.kex_type() == TLS_ALGO_KEYEXCH_NOKEX)
+ throw Unexpected_Message("Unexpected key exchange from server");
+
+ state->server_kex = new Server_Key_Exchange(contents);
+
+ if(state->kex_pub)
+ delete state->kex_pub;
+
+ state->kex_pub = state->server_kex->key();
+
+ bool is_dh = false, is_rsa = false;
+
+ if(dynamic_cast<DH_PublicKey*>(state->kex_pub))
+ is_dh = true;
+ else if(dynamic_cast<RSA_PublicKey*>(state->kex_pub))
+ is_rsa = true;
+ else
+ throw TLS_Exception(HANDSHAKE_FAILURE,
+ "Unknown key type received in server kex");
+
+ if((is_dh && state->suite.kex_type() != TLS_ALGO_KEYEXCH_DH) ||
+ (is_rsa && state->suite.kex_type() != TLS_ALGO_KEYEXCH_RSA))
+ throw TLS_Exception(ILLEGAL_PARAMETER,
+ "Certificate key type did not match ciphersuite");
+
+ if(state->suite.sig_type() != TLS_ALGO_SIGNER_ANON)
+ {
+ if(!state->server_kex->verify(peer_certs[0],
+ state->client_hello->random(),
+ state->server_hello->random()))
+ throw TLS_Exception(DECRYPT_ERROR,
+ "Bad signature on server key exchange");
+ }
+ }
+ else if(type == CERTIFICATE_REQUEST)
+ {
+ client_check_state(type, state);
+
+ state->cert_req = new Certificate_Req(contents);
+ state->do_client_auth = true;
+ }
+ else if(type == SERVER_HELLO_DONE)
+ {
+ client_check_state(type, state);
+
+ state->server_hello_done = new Server_Hello_Done(contents);
+
+ if(state->do_client_auth)
+ {
+ std::vector<X509_Certificate> send_certs;
+
+ std::vector<Certificate_Type> types =
+ state->cert_req->acceptable_types();
+
+ // FIXME: Fill in useful certs here, if any
+ state->client_certs = new Certificate(writer, send_certs,
+ state->hash);
+ }
+
+ state->client_kex =
+ new Client_Key_Exchange(rng, writer, state->hash,
+ state->kex_pub, state->version,
+ state->client_hello->version());
+
+ if(state->do_client_auth)
+ {
+ Private_Key* key_matching_cert = 0; // FIXME
+ state->client_verify = new Certificate_Verify(rng,
+ writer, state->hash,
+ key_matching_cert);
+ }
+
+ state->keys = SessionKeys(state->suite, state->version,
+ state->client_kex->pre_master_secret(),
+ state->client_hello->random(),
+ state->server_hello->random());
+
+ writer.send(CHANGE_CIPHER_SPEC, 1);
+ writer.flush();
+
+ writer.set_keys(state->suite, state->keys, CLIENT);
+
+ state->client_finished = new Finished(writer, state->version, CLIENT,
+ state->keys.master_secret(),
+ state->hash);
+ }
+ else if(type == HANDSHAKE_CCS)
+ {
+ client_check_state(type, state);
+
+ reader.set_keys(state->suite, state->keys, CLIENT);
+ state->got_server_ccs = true;
+ }
+ else if(type == FINISHED)
+ {
+ client_check_state(type, state);
+
+ state->server_finished = new Finished(contents);
+
+ if(!state->server_finished->verify(state->keys.master_secret(),
+ state->version, state->hash, SERVER))
+ throw TLS_Exception(DECRYPT_ERROR,
+ "Finished message didn't verify");
+
+ delete state;
+ state = 0;
+ active = true;
+ }
+ else
+ throw Unexpected_Message("Unknown handshake message received");
+ }
+
+}
diff --git a/src/tls/tls_client.h b/src/tls/tls_client.h
new file mode 100644
index 000000000..6d613be33
--- /dev/null
+++ b/src/tls/tls_client.h
@@ -0,0 +1,87 @@
+/*
+* TLS Client
+* (C) 2004-2011 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_CLIENT_H__
+#define BOTAN_TLS_CLIENT_H__
+
+#include <botan/tls_policy.h>
+#include <botan/tls_record.h>
+#include <vector>
+#include <string>
+
+namespace Botan {
+
+/**
+* SSL/TLS Client
+*/
+class BOTAN_DLL TLS_Client
+ {
+ public:
+ /**
+ * Set up a new TLS client session
+ */
+ TLS_Client(std::tr1::function<void (const byte[], size_t)> socket_output_fn,
+ std::tr1::function<void (const byte[], size_t, u16bit)> proc_fn,
+ const TLS_Policy& policy,
+ RandomNumberGenerator& rng);
+
+ /**
+ * Inject TLS traffic received from counterparty
+
+ * @return a hint as the how many more bytes we need to process the
+ current record (this may be 0 if on a record boundary)
+ */
+ size_t received_data(const byte buf[], size_t buf_size);
+
+ /**
+ * Inject plaintext intended for counterparty
+ */
+ void queue_for_sending(const byte buf[], size_t buf_size);
+
+ void close();
+
+ bool handshake_complete() const { return active; }
+
+ std::vector<X509_Certificate> peer_cert_chain() const { return peer_certs; }
+
+ void add_client_cert(const X509_Certificate& cert,
+ Private_Key* cert_key);
+
+ ~TLS_Client();
+ private:
+ void close(Alert_Level, Alert_Type);
+
+ size_t get_pending_socket_input(byte output[], size_t length);
+
+ void initialize();
+ void do_handshake();
+
+ void state_machine();
+ void read_handshake(byte, const MemoryRegion<byte>&);
+ void process_handshake_msg(Handshake_Type, const MemoryRegion<byte>&);
+
+ const TLS_Policy& policy;
+ RandomNumberGenerator& rng;
+
+ std::tr1::function<void (const byte[], size_t, u16bit)> proc_fn;
+
+ Record_Writer writer;
+ Record_Reader reader;
+
+ SecureQueue pre_handshake_write_queue;
+
+ std::vector<X509_Certificate> peer_certs;
+ std::vector<std::pair<X509_Certificate, Private_Key*> > certs;
+
+ class Handshake_State* state;
+ //SecureVector<byte> session_id;
+ bool active;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_exceptn.h b/src/tls/tls_exceptn.h
new file mode 100644
index 000000000..37b9c0d27
--- /dev/null
+++ b/src/tls/tls_exceptn.h
@@ -0,0 +1,43 @@
+/*
+* Exceptions
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_EXCEPTION_H__
+#define BOTAN_TLS_EXCEPTION_H__
+
+#include <botan/exceptn.h>
+#include <botan/tls_magic.h>
+
+namespace Botan {
+
+/**
+* Exception Base Class
+*/
+class BOTAN_DLL TLS_Exception : public Exception
+ {
+ public:
+ Alert_Type type() const throw() { return alert_type; }
+
+ TLS_Exception(Alert_Type type,
+ const std::string& err_msg = "Unknown error") :
+ Exception(err_msg), alert_type(type) {}
+
+ private:
+ Alert_Type alert_type;
+ };
+
+/**
+* Unexpected_Message Exception
+*/
+struct BOTAN_DLL Unexpected_Message : public TLS_Exception
+ {
+ Unexpected_Message(const std::string& err) :
+ TLS_Exception(UNEXPECTED_MESSAGE, err) {}
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_handshake_hash.cpp b/src/tls/tls_handshake_hash.cpp
new file mode 100644
index 000000000..7c1e2e385
--- /dev/null
+++ b/src/tls/tls_handshake_hash.cpp
@@ -0,0 +1,70 @@
+/*
+* TLS Handshake Hash
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/md5.h>
+#include <botan/sha160.h>
+#include <memory>
+
+namespace Botan {
+
+/**
+* Return a TLS Handshake Hash
+*/
+SecureVector<byte> HandshakeHash::final()
+ {
+ MD5 md5;
+ SHA_160 sha1;
+
+ md5.update(data);
+ sha1.update(data);
+
+ SecureVector<byte> output;
+ output += md5.final();
+ output += sha1.final();
+ return output;
+ }
+
+/**
+* Return a SSLv3 Handshake Hash
+*/
+SecureVector<byte> HandshakeHash::final_ssl3(const MemoryRegion<byte>& secret)
+ {
+ const byte PAD_INNER = 0x36, PAD_OUTER = 0x5C;
+
+ MD5 md5;
+ SHA_160 sha1;
+
+ md5.update(data);
+ sha1.update(data);
+
+ md5.update(secret);
+ sha1.update(secret);
+
+ for(size_t i = 0; i != 48; ++i)
+ md5.update(PAD_INNER);
+ for(size_t i = 0; i != 40; ++i)
+ sha1.update(PAD_INNER);
+
+ SecureVector<byte> inner_md5 = md5.final(), inner_sha1 = sha1.final();
+
+ md5.update(secret);
+ sha1.update(secret);
+ for(size_t i = 0; i != 48; ++i)
+ md5.update(PAD_OUTER);
+ for(size_t i = 0; i != 40; ++i)
+ sha1.update(PAD_OUTER);
+ md5.update(inner_md5);
+ sha1.update(inner_sha1);
+
+ SecureVector<byte> output;
+ output += md5.final();
+ output += sha1.final();
+ return output;
+ }
+
+}
diff --git a/src/tls/tls_handshake_hash.h b/src/tls/tls_handshake_hash.h
new file mode 100644
index 000000000..cea612a71
--- /dev/null
+++ b/src/tls/tls_handshake_hash.h
@@ -0,0 +1,40 @@
+/*
+* TLS Handshake Hash
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_HASH_H__
+#define BOTAN_TLS_HANDSHAKE_HASH_H__
+
+#include <botan/secmem.h>
+
+namespace Botan {
+
+using namespace Botan;
+
+/**
+* TLS Handshake Hash
+*/
+class HandshakeHash
+ {
+ public:
+ void update(const byte in[], size_t length)
+ { data += std::make_pair(in, length); }
+
+ void update(const MemoryRegion<byte>& in)
+ { data += in; }
+
+ void update(byte in)
+ { data.push_back(in); }
+
+ SecureVector<byte> final();
+ SecureVector<byte> final_ssl3(const MemoryRegion<byte>&);
+ private:
+ SecureVector<byte> data;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_magic.h b/src/tls/tls_magic.h
new file mode 100644
index 000000000..00898738e
--- /dev/null
+++ b/src/tls/tls_magic.h
@@ -0,0 +1,192 @@
+/*
+* SSL/TLS Protocol Constants
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_PROTOCOL_MAGIC_H__
+#define BOTAN_TLS_PROTOCOL_MAGIC_H__
+
+namespace Botan {
+
+/**
+* Protocol Constants for SSL/TLS
+*/
+enum Size_Limits {
+ MAX_PLAINTEXT_SIZE = 16*1024,
+ MAX_COMPRESSED_SIZE = MAX_PLAINTEXT_SIZE + 1024,
+ MAX_CIPHERTEXT_SIZE = MAX_COMPRESSED_SIZE + 1024
+};
+
+enum Version_Code {
+ NO_VERSION_SET = 0x0000,
+ SSL_V3 = 0x0300,
+ TLS_V10 = 0x0301,
+ TLS_V11 = 0x0302
+};
+
+enum Connection_Side { CLIENT, SERVER };
+
+enum Record_Type {
+ CONNECTION_CLOSED = 0,
+
+ CHANGE_CIPHER_SPEC = 20,
+ ALERT = 21,
+ HANDSHAKE = 22,
+ APPLICATION_DATA = 23
+};
+
+enum Handshake_Type {
+ HELLO_REQUEST = 0,
+ CLIENT_HELLO = 1,
+ CLIENT_HELLO_SSLV2 = 255, // not a wire value
+ SERVER_HELLO = 2,
+ CERTIFICATE = 11,
+ SERVER_KEX = 12,
+ CERTIFICATE_REQUEST = 13,
+ SERVER_HELLO_DONE = 14,
+ CERTIFICATE_VERIFY = 15,
+ CLIENT_KEX = 16,
+ FINISHED = 20,
+
+ HANDSHAKE_CCS = 100,
+ HANDSHAKE_NONE = 101
+};
+
+enum Alert_Level {
+ WARNING = 1,
+ FATAL = 2
+};
+
+enum Alert_Type {
+ CLOSE_NOTIFY = 0,
+ UNEXPECTED_MESSAGE = 10,
+ BAD_RECORD_MAC = 20,
+ DECRYPTION_FAILED = 21,
+ RECORD_OVERFLOW = 22,
+ DECOMPRESSION_FAILURE = 30,
+ HANDSHAKE_FAILURE = 40,
+ BAD_CERTIFICATE = 42,
+ UNSUPPORTED_CERTIFICATE = 43,
+ CERTIFICATE_REVOKED = 44,
+ CERTIFICATE_EXPIRED = 45,
+ CERTIFICATE_UNKNOWN = 46,
+ ILLEGAL_PARAMETER = 47,
+ UNKNOWN_CA = 48,
+ ACCESS_DENIED = 49,
+ DECODE_ERROR = 50,
+ DECRYPT_ERROR = 51,
+ EXPORT_RESTRICTION = 60,
+ PROTOCOL_VERSION = 70,
+ INSUFFICIENT_SECURITY = 71,
+ INTERNAL_ERROR = 80,
+ USER_CANCELED = 90,
+ NO_RENEGOTIATION = 100,
+
+ UNKNOWN_PSK_IDENTITY = 115,
+
+ NO_ALERT_TYPE = 0xFFFF
+};
+
+enum Certificate_Type {
+ RSA_CERT = 1,
+ DSS_CERT = 2,
+ DH_RSA_CERT = 3,
+ DH_DSS_CERT = 4
+};
+
+enum Ciphersuite_Code {
+ TLS_RSA_WITH_RC4_128_MD5 = 0x0004,
+ TLS_RSA_WITH_RC4_128_SHA = 0x0005,
+
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F,
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035,
+ TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
+ TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
+ TLS_RSA_WITH_SEED_CBC_SHA = 0x0096,
+
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032,
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038,
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040,
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A,
+ TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099,
+
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
+ TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A,
+
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
+ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
+
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011,
+ TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012,
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028
+};
+
+/*
+* Form of the ciphersuites broken down by field instead of
+* being randomly assigned codepoints.
+*/
+enum TLS_Ciphersuite_Algos {
+ TLS_ALGO_SIGNER_MASK = 0xFF000000,
+ TLS_ALGO_SIGNER_ANON = 0x01000000,
+ TLS_ALGO_SIGNER_RSA = 0x02000000,
+ TLS_ALGO_SIGNER_DSA = 0x03000000,
+ TLS_ALGO_SIGNER_ECDSA = 0x04000000,
+
+ TLS_ALGO_KEYEXCH_MASK = 0x00FF0000,
+ TLS_ALGO_KEYEXCH_NOKEX = 0x00010000,
+ TLS_ALGO_KEYEXCH_RSA = 0x00020000,
+ TLS_ALGO_KEYEXCH_DH = 0x00030000,
+ TLS_ALGO_KEYEXCH_ECDH = 0x00040000,
+
+ TLS_ALGO_MAC_MASK = 0x0000FF00,
+ TLS_ALGO_MAC_MD5 = 0x00000100,
+ TLS_ALGO_MAC_SHA1 = 0x00000200,
+ TLS_ALGO_MAC_SHA256 = 0x00000300,
+ TLS_ALGO_MAC_SHA384 = 0x00000400,
+
+ TLS_ALGO_CIPHER_MASK = 0x000000FF,
+ TLS_ALGO_CIPHER_RC4_128 = 0x00000001,
+ TLS_ALGO_CIPHER_3DES_CBC = 0x00000002,
+ TLS_ALGO_CIPHER_AES128_CBC = 0x00000003,
+ TLS_ALGO_CIPHER_AES256_CBC = 0x00000004,
+ TLS_ALGO_CIPHER_SEED_CBC = 0x00000005
+};
+
+enum Compression_Algo {
+ NO_COMPRESSION = 0x00
+};
+
+enum TLS_Handshake_Extension_Type {
+ TLSEXT_SERVER_NAME_INDICATION = 0,
+ TLSEXT_MAX_FRAGMENT_LENGTH = 1,
+ TLSEXT_CLIENT_CERT_URL = 2,
+ TLSEXT_TRUSTED_CA_KEYS = 3,
+ TLSEXT_TRUNCATED_HMAC = 4,
+
+ TLSEXT_USABLE_ELLIPTIC_CURVES = 10,
+ TLSEXT_EC_POINT_FORMATS = 11,
+
+ TLSEXT_SRP_IDENTIFIER = 12,
+
+ TLSEXT_CERTIFICATE_TYPES = 9,
+ TLSEXT_SESSION_TICKET = 35
+};
+
+}
+
+#endif
diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h
new file mode 100644
index 000000000..e7eaa56e1
--- /dev/null
+++ b/src/tls/tls_messages.h
@@ -0,0 +1,297 @@
+/*
+* TLS Messages
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_MESSAGES_H__
+#define BOTAN_TLS_MESSAGES_H__
+
+#include <botan/tls_record.h>
+#include <botan/internal/tls_handshake_hash.h>
+#include <botan/tls_policy.h>
+#include <botan/bigint.h>
+#include <botan/pkcs8.h>
+#include <botan/x509cert.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* TLS Handshake Message Base Class
+*/
+class HandshakeMessage
+ {
+ public:
+ void send(Record_Writer&, HandshakeHash&) const;
+
+ virtual Handshake_Type type() const = 0;
+
+ virtual ~HandshakeMessage() {}
+ private:
+ HandshakeMessage& operator=(const HandshakeMessage&) { return (*this); }
+ virtual SecureVector<byte> serialize() const = 0;
+ virtual void deserialize(const MemoryRegion<byte>&) = 0;
+ };
+
+/**
+* Client Hello Message
+*/
+class Client_Hello : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return CLIENT_HELLO; }
+ Version_Code version() const { return c_version; }
+ const SecureVector<byte>& session_id() const { return sess_id; }
+ std::vector<u16bit> ciphersuites() const { return suites; }
+ std::vector<byte> compression_algos() const { return comp_algos; }
+
+ const SecureVector<byte>& random() const { return c_random; }
+
+ std::string hostname() const { return requested_hostname; }
+
+ std::string srp_identifier() const { return requested_srp_id; }
+
+ bool offered_suite(u16bit) const;
+
+ Client_Hello(RandomNumberGenerator& rng,
+ Record_Writer&, const TLS_Policy&, HandshakeHash&);
+
+ Client_Hello(const MemoryRegion<byte>& buf,
+ Handshake_Type type)
+ {
+ if(type == CLIENT_HELLO)
+ deserialize(buf);
+ else
+ deserialize_sslv2(buf);
+ }
+
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+ void deserialize_sslv2(const MemoryRegion<byte>&);
+
+ Version_Code c_version;
+ SecureVector<byte> sess_id, c_random;
+ std::vector<u16bit> suites;
+ std::vector<byte> comp_algos;
+ std::string requested_hostname;
+ std::string requested_srp_id;
+ };
+
+/**
+* Client Key Exchange Message
+*/
+class Client_Key_Exchange : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return CLIENT_KEX; }
+
+ SecureVector<byte> pre_master_secret() const;
+
+ SecureVector<byte> pre_master_secret(RandomNumberGenerator& rng,
+ const Private_Key* key,
+ Version_Code version);
+
+ Client_Key_Exchange(RandomNumberGenerator& rng,
+ Record_Writer& output,
+ HandshakeHash& hash,
+ const Public_Key* my_key,
+ Version_Code using_version,
+ Version_Code pref_version);
+
+ Client_Key_Exchange(const MemoryRegion<byte>& buf,
+ const CipherSuite& suite,
+ Version_Code using_version);
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+
+ SecureVector<byte> key_material, pre_master;
+ bool include_length;
+ };
+
+/**
+* Certificate Message
+*/
+class Certificate : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return CERTIFICATE; }
+ std::vector<X509_Certificate> cert_chain() const { return certs; }
+
+ Certificate(Record_Writer&, const std::vector<X509_Certificate>&,
+ HandshakeHash&);
+ Certificate(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+ std::vector<X509_Certificate> certs;
+ };
+
+/**
+* Certificate Request Message
+*/
+class Certificate_Req : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return CERTIFICATE_REQUEST; }
+
+ std::vector<Certificate_Type> acceptable_types() const { return types; }
+ std::vector<X509_DN> acceptable_CAs() const { return names; }
+
+ /* TODO
+ Certificate_Req(Record_Writer&, HandshakeHash&,
+ const X509_Certificate&);
+ */
+ Certificate_Req(Record_Writer&, HandshakeHash&,
+ const std::vector<X509_Certificate>&);
+
+ Certificate_Req(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+
+ std::vector<X509_DN> names;
+ std::vector<Certificate_Type> types;
+ };
+
+/**
+* Certificate Verify Message
+*/
+class Certificate_Verify : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return CERTIFICATE_VERIFY; }
+
+ bool verify(const X509_Certificate&, HandshakeHash&);
+
+ Certificate_Verify(RandomNumberGenerator& rng,
+ Record_Writer&, HandshakeHash&,
+ const Private_Key*);
+
+ Certificate_Verify(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+
+ SecureVector<byte> signature;
+ };
+
+/**
+* Finished Message
+*/
+class Finished : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return FINISHED; }
+
+ bool verify(const MemoryRegion<byte>&, Version_Code,
+ const HandshakeHash&, Connection_Side);
+
+ Finished(Record_Writer&, Version_Code, Connection_Side,
+ const MemoryRegion<byte>&, HandshakeHash&);
+ Finished(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+
+ SecureVector<byte> compute_verify(const MemoryRegion<byte>&,
+ HandshakeHash, Connection_Side,
+ Version_Code);
+
+ Connection_Side side;
+ SecureVector<byte> verification_data;
+ };
+
+/**
+* Hello Request Message
+*/
+class Hello_Request : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return HELLO_REQUEST; }
+
+ Hello_Request(Record_Writer&);
+ Hello_Request(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+ };
+
+/**
+* Server Hello Message
+*/
+class Server_Hello : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return SERVER_HELLO; }
+ Version_Code version() { return s_version; }
+ const SecureVector<byte>& session_id() const { return sess_id; }
+ u16bit ciphersuite() const { return suite; }
+ byte compression_algo() const { return comp_algo; }
+
+ const SecureVector<byte>& random() const { return s_random; }
+
+ Server_Hello(RandomNumberGenerator& rng,
+ Record_Writer&, const TLS_Policy&,
+ const std::vector<X509_Certificate>&,
+ const Client_Hello&, Version_Code, HandshakeHash&);
+
+ Server_Hello(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+
+ Version_Code s_version;
+ SecureVector<byte> sess_id, s_random;
+ u16bit suite;
+ byte comp_algo;
+ };
+
+/**
+* Server Key Exchange Message
+*/
+class Server_Key_Exchange : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return SERVER_KEX; }
+ Public_Key* key() const;
+
+ bool verify(const X509_Certificate&, const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&) const;
+
+ Server_Key_Exchange(RandomNumberGenerator& rng,
+ Record_Writer&, const Public_Key*,
+ const Private_Key*, const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&, HandshakeHash&);
+
+ Server_Key_Exchange(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ SecureVector<byte> serialize_params() const;
+ void deserialize(const MemoryRegion<byte>&);
+
+ std::vector<BigInt> params;
+ SecureVector<byte> signature;
+ };
+
+/**
+* Server Hello Done Message
+*/
+class Server_Hello_Done : public HandshakeMessage
+ {
+ public:
+ Handshake_Type type() const { return SERVER_HELLO_DONE; }
+
+ Server_Hello_Done(Record_Writer&, HandshakeHash&);
+ Server_Hello_Done(const MemoryRegion<byte>& buf) { deserialize(buf); }
+ private:
+ SecureVector<byte> serialize() const;
+ void deserialize(const MemoryRegion<byte>&);
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_policy.cpp b/src/tls/tls_policy.cpp
new file mode 100644
index 000000000..b73ff7850
--- /dev/null
+++ b/src/tls/tls_policy.cpp
@@ -0,0 +1,118 @@
+/*
+* Policies for TLS
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/tls_policy.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+/*
+* Return allowed ciphersuites
+*/
+std::vector<u16bit> TLS_Policy::ciphersuites() const
+ {
+ return suite_list(allow_static_rsa(), allow_edh_rsa(), allow_edh_dsa());
+ }
+
+/*
+* Return allowed ciphersuites
+*/
+std::vector<u16bit> TLS_Policy::suite_list(bool use_rsa,
+ bool use_edh_rsa,
+ bool use_edh_dsa) const
+ {
+ std::vector<u16bit> suites;
+
+ if(use_edh_dsa)
+ {
+ suites.push_back(TLS_DHE_DSS_WITH_AES_256_CBC_SHA);
+ suites.push_back(TLS_DHE_DSS_WITH_AES_128_CBC_SHA);
+ suites.push_back(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA);
+ suites.push_back(TLS_DHE_DSS_WITH_SEED_CBC_SHA);
+ }
+
+ if(use_edh_rsa)
+ {
+ suites.push_back(TLS_DHE_RSA_WITH_AES_256_CBC_SHA);
+ suites.push_back(TLS_DHE_RSA_WITH_AES_128_CBC_SHA);
+ suites.push_back(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA);
+ suites.push_back(TLS_DHE_RSA_WITH_SEED_CBC_SHA);
+ }
+
+ if(use_rsa)
+ {
+ suites.push_back(TLS_RSA_WITH_AES_256_CBC_SHA);
+ suites.push_back(TLS_RSA_WITH_AES_128_CBC_SHA);
+ suites.push_back(TLS_RSA_WITH_3DES_EDE_CBC_SHA);
+ suites.push_back(TLS_RSA_WITH_SEED_CBC_SHA);
+ suites.push_back(TLS_RSA_WITH_RC4_128_SHA);
+ suites.push_back(TLS_RSA_WITH_RC4_128_MD5);
+ }
+
+ if(suites.size() == 0)
+ throw TLS_Exception(INTERNAL_ERROR,
+ "TLS_Policy error: All ciphersuites disabled");
+
+ return suites;
+ }
+
+/*
+* Return allowed compression algorithms
+*/
+std::vector<byte> TLS_Policy::compression() const
+ {
+ std::vector<byte> algs;
+ algs.push_back(NO_COMPRESSION);
+ return algs;
+ }
+
+/*
+* Choose which ciphersuite to use
+*/
+u16bit TLS_Policy::choose_suite(const std::vector<u16bit>& c_suites,
+ bool have_rsa,
+ bool have_dsa) const
+ {
+ bool use_static_rsa = allow_static_rsa() && have_rsa;
+ bool use_edh_rsa = allow_edh_rsa() && have_rsa;
+ bool use_edh_dsa = allow_edh_dsa() && have_dsa;
+
+ std::vector<u16bit> s_suites = suite_list(use_static_rsa, use_edh_rsa,
+ use_edh_dsa);
+
+ for(size_t i = 0; i != s_suites.size(); ++i)
+ for(size_t j = 0; j != c_suites.size(); ++j)
+ if(s_suites[i] == c_suites[j])
+ return s_suites[i];
+
+ return 0;
+ }
+
+/*
+* Choose which compression algorithm to use
+*/
+byte TLS_Policy::choose_compression(const std::vector<byte>& c_comp) const
+ {
+ std::vector<byte> s_comp = compression();
+
+ for(size_t i = 0; i != s_comp.size(); ++i)
+ for(size_t j = 0; j != c_comp.size(); ++j)
+ if(s_comp[i] == c_comp[j])
+ return s_comp[i];
+
+ return NO_COMPRESSION;
+ }
+
+/*
+* Return the group to use for empheral DH
+*/
+DL_Group TLS_Policy::dh_group() const
+ {
+ return DL_Group("modp/ietf/1024");
+ }
+
+}
diff --git a/src/tls/tls_policy.h b/src/tls/tls_policy.h
new file mode 100644
index 000000000..461164d2f
--- /dev/null
+++ b/src/tls/tls_policy.h
@@ -0,0 +1,63 @@
+/*
+* Policies
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_POLICY_H__
+#define BOTAN_TLS_POLICY_H__
+
+#include <botan/tls_magic.h>
+#include <botan/x509cert.h>
+#include <botan/dl_group.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* TLS Policy Base Class
+* Inherit and overload as desired to suite local policy concerns
+*/
+class BOTAN_DLL TLS_Policy
+ {
+ public:
+ std::vector<u16bit> ciphersuites() const;
+ virtual std::vector<byte> compression() const;
+
+ virtual u16bit choose_suite(const std::vector<u16bit>& client_suites,
+ bool rsa_ok,
+ bool dsa_ok) const;
+
+ virtual byte choose_compression(const std::vector<byte>& client) const;
+
+ virtual bool allow_static_rsa() const { return true; }
+ virtual bool allow_edh_rsa() const { return true; }
+ virtual bool allow_edh_dsa() const { return true; }
+ virtual bool require_client_auth() const { return false; }
+
+ virtual DL_Group dh_group() const;
+ virtual size_t rsa_export_keysize() const { return 512; }
+
+ /*
+ * @return the minimum version that we will negotiate
+ */
+ virtual Version_Code min_version() const { return SSL_V3; }
+
+ /*
+ * @return the version we would prefer to negotiate
+ */
+ virtual Version_Code pref_version() const { return TLS_V11; }
+
+ virtual bool check_cert(const std::vector<X509_Certificate>& cert_chain) const = 0;
+
+ virtual ~TLS_Policy() {}
+ private:
+ virtual std::vector<u16bit> suite_list(bool use_rsa,
+ bool use_edh_rsa,
+ bool use_edh_dsa) const;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_reader.h b/src/tls/tls_reader.h
new file mode 100644
index 000000000..3a45235b5
--- /dev/null
+++ b/src/tls/tls_reader.h
@@ -0,0 +1,186 @@
+/*
+* TLS Data Reader
+* (C) 2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_READER_H__
+#define BOTAN_TLS_READER_H__
+
+#include <botan/secmem.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+/**
+* Helper class for decoding TLS protocol messages
+*/
+class TLS_Data_Reader
+ {
+ public:
+ TLS_Data_Reader(const MemoryRegion<byte>& buf_in) :
+ buf(buf_in), offset(0) {}
+
+ size_t remaining_bytes() const
+ {
+ return buf.size() - offset;
+ }
+
+ bool has_remaining() const
+ {
+ return (remaining_bytes() > 0);
+ }
+
+ void discard_next(size_t bytes)
+ {
+ assert_at_least(bytes);
+ offset += bytes;
+ }
+
+ u16bit get_u16bit()
+ {
+ assert_at_least(2);
+ u16bit result = make_u16bit(buf[offset], buf[offset+1]);
+ offset += 2;
+ return result;
+ }
+
+ byte get_byte()
+ {
+ assert_at_least(1);
+ byte result = buf[offset];
+ offset += 1;
+ return result;
+ }
+
+ template<typename T, typename Container>
+ Container get_elem(size_t num_elems)
+ {
+ assert_at_least(num_elems * sizeof(T));
+
+ Container result(num_elems);
+
+ for(size_t i = 0; i != num_elems; ++i)
+ result[i] = load_be<T>(&buf[offset], i);
+
+ offset += num_elems * sizeof(T);
+
+ return result;
+ }
+
+ template<typename T>
+ SecureVector<T> get_range(size_t len_bytes,
+ size_t min_elems,
+ size_t max_elems)
+ {
+ const size_t num_elems =
+ get_num_elems(len_bytes, sizeof(T), min_elems, max_elems);
+
+ return get_elem<T, SecureVector<T> >(num_elems);
+ }
+
+ template<typename T>
+ std::vector<T> get_range_vector(size_t len_bytes,
+ size_t min_elems,
+ size_t max_elems)
+ {
+ const size_t num_elems =
+ get_num_elems(len_bytes, sizeof(T), min_elems, max_elems);
+
+ return get_elem<T, std::vector<T> >(num_elems);
+ }
+
+ template<typename T>
+ SecureVector<T> get_fixed(size_t size)
+ {
+ return get_elem<T, SecureVector<T> >(size);
+ }
+
+ private:
+ size_t get_length_field(size_t len_bytes)
+ {
+ assert_at_least(len_bytes);
+
+ if(len_bytes == 1)
+ return get_byte();
+ else if(len_bytes == 2)
+ return get_u16bit();
+
+ throw Decoding_Error("TLS_Data_Reader: Bad length size");
+ }
+
+ size_t get_num_elems(size_t len_bytes,
+ size_t T_size,
+ size_t min_elems,
+ size_t max_elems)
+ {
+ const size_t byte_length = get_length_field(len_bytes);
+
+ if(byte_length % T_size != 0)
+ throw Decoding_Error("TLS_Data_Reader: Size isn't multiple of T");
+
+ const size_t num_elems = byte_length / T_size;
+
+ if(num_elems < min_elems || num_elems > max_elems)
+ throw Decoding_Error("TLS_Data_Reader: Range outside paramaters");
+
+ return num_elems;
+ }
+
+ void assert_at_least(size_t n) const
+ {
+ if(buf.size() - offset < n)
+ throw Decoding_Error("TLS_Data_Reader: Corrupt packet");
+ }
+
+ const MemoryRegion<byte>& buf;
+ size_t offset;
+ };
+
+/**
+* Helper function for encoding length-tagged vectors
+*/
+template<typename T>
+void append_tls_length_value(MemoryRegion<byte>& buf,
+ const T* vals,
+ size_t vals_size,
+ size_t tag_size)
+ {
+ const size_t T_size = sizeof(T);
+ const size_t val_bytes = T_size * vals_size;
+
+ if(tag_size != 1 && tag_size != 2)
+ throw std::invalid_argument("append_tls_length_value: invalid tag size");
+
+ if((tag_size == 1 && val_bytes > 255) ||
+ (tag_size == 2 && val_bytes > 65535))
+ throw std::invalid_argument("append_tls_length_value: value too large");
+
+ for(size_t i = 0; i != tag_size; ++i)
+ buf.push_back(get_byte(sizeof(val_bytes)-tag_size+i, val_bytes));
+
+ for(size_t i = 0; i != vals_size; ++i)
+ for(size_t j = 0; j != T_size; ++j)
+ buf.push_back(get_byte(j, vals[i]));
+ }
+
+template<typename T>
+void append_tls_length_value(MemoryRegion<byte>& buf,
+ const MemoryRegion<T>& vals,
+ size_t tag_size)
+ {
+ append_tls_length_value(buf, &vals[0], vals.size(), tag_size);
+ }
+
+template<typename T>
+void append_tls_length_value(MemoryRegion<byte>& buf,
+ const std::vector<T>& vals,
+ size_t tag_size)
+ {
+ append_tls_length_value(buf, &vals[0], vals.size(), tag_size);
+ }
+
+}
+
+#endif
diff --git a/src/tls/tls_record.h b/src/tls/tls_record.h
new file mode 100644
index 000000000..6d5dd057d
--- /dev/null
+++ b/src/tls/tls_record.h
@@ -0,0 +1,119 @@
+/*
+* TLS Record Handling
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_RECORDS_H__
+#define BOTAN_TLS_RECORDS_H__
+
+#include <botan/tls_session_key.h>
+#include <botan/tls_suites.h>
+#include <botan/pipe.h>
+#include <botan/mac.h>
+#include <botan/secqueue.h>
+#include <vector>
+
+#if defined(BOTAN_USE_STD_TR1)
+
+#if defined(BOTAN_BUILD_COMPILER_IS_MSVC)
+ #include <functional>
+#else
+ #include <tr1/functional>
+#endif
+
+#elif defined(BOTAN_USE_BOOST_TR1)
+ #include <boost/tr1/functional.hpp>
+#else
+ #error "No TR1 library defined for use"
+#endif
+
+namespace Botan {
+
+using namespace std::tr1::placeholders;
+
+/**
+* TLS Record Writer
+*/
+class BOTAN_DLL Record_Writer
+ {
+ public:
+ void send(byte type, const byte input[], size_t length);
+ void send(byte type, byte val) { send(type, &val, 1); }
+
+ void flush();
+
+ void alert(Alert_Level, Alert_Type);
+
+ void set_keys(const CipherSuite&, const SessionKeys&, Connection_Side);
+
+ void set_version(Version_Code);
+
+ void reset();
+
+ Record_Writer(std::tr1::function<void (const byte[], size_t)> output_fn);
+
+ ~Record_Writer() { delete mac; }
+ private:
+ void send_record(byte type, const byte input[], size_t length);
+ void send_record(byte type, byte major, byte minor,
+ const byte input[], size_t length);
+
+ std::tr1::function<void (const byte[], size_t)> output_fn;
+ Pipe cipher;
+ MessageAuthenticationCode* mac;
+
+ SecureVector<byte> buffer;
+ size_t buf_pos;
+
+ size_t block_size, mac_size, iv_size;
+
+ u64bit seq_no;
+ byte major, minor, buf_type;
+ };
+
+/**
+* TLS Record Reader
+*/
+class BOTAN_DLL Record_Reader
+ {
+ public:
+ void add_input(const byte input[], size_t input_size);
+
+ /**
+ * @param msg_type (output variable)
+ * @param buffer (output variable)
+ * @return Number of bytes still needed (minimum), or 0 if success
+ */
+ size_t get_record(byte& msg_type,
+ MemoryRegion<byte>& buffer);
+
+ SecureVector<byte> get_record(byte& msg_type);
+
+ void set_keys(const CipherSuite& suite,
+ const SessionKeys& keys,
+ Connection_Side side);
+
+ void set_version(Version_Code version);
+
+ void reset();
+
+ bool currently_empty() const { return input_queue.size() == 0; }
+
+ Record_Reader() { mac = 0; reset(); }
+
+ ~Record_Reader() { delete mac; }
+ private:
+ SecureQueue input_queue;
+
+ Pipe cipher;
+ MessageAuthenticationCode* mac;
+ size_t block_size, mac_size, iv_size;
+ u64bit seq_no;
+ byte major, minor;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp
new file mode 100644
index 000000000..8964be3d7
--- /dev/null
+++ b/src/tls/tls_server.cpp
@@ -0,0 +1,494 @@
+/*
+* TLS Server
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/tls_server.h>
+#include <botan/internal/tls_alerts.h>
+#include <botan/internal/tls_state.h>
+#include <botan/loadstor.h>
+#include <botan/rsa.h>
+#include <botan/dh.h>
+
+namespace Botan {
+
+namespace {
+
+/*
+* Choose what version to respond with
+*/
+Version_Code choose_version(Version_Code client, Version_Code minimum)
+ {
+ if(client < minimum)
+ throw TLS_Exception(PROTOCOL_VERSION,
+ "Client version is unacceptable by policy");
+
+ if(client == SSL_V3 || client == TLS_V10 || client == TLS_V11)
+ return client;
+ return TLS_V11;
+ }
+
+// FIXME: checks are wrong for session reuse (add a flag for that)
+/*
+* Verify the state transition is allowed
+*/
+void server_check_state(Handshake_Type new_msg, Handshake_State* state)
+ {
+ class State_Transition_Error : public Unexpected_Message
+ {
+ public:
+ State_Transition_Error(const std::string& err) :
+ Unexpected_Message("State transition error from " + err) {}
+ };
+
+ if(new_msg == CLIENT_HELLO || new_msg == CLIENT_HELLO_SSLV2)
+ {
+ if(state->server_hello)
+ throw State_Transition_Error("ClientHello");
+ }
+ else if(new_msg == CERTIFICATE)
+ {
+ if(!state->do_client_auth || !state->cert_req ||
+ !state->server_hello_done || state->client_kex)
+ throw State_Transition_Error("ClientCertificate");
+ }
+ else if(new_msg == CLIENT_KEX)
+ {
+ if(!state->server_hello_done || state->client_verify ||
+ state->got_client_ccs)
+ throw State_Transition_Error("ClientKeyExchange");
+ }
+ else if(new_msg == CERTIFICATE_VERIFY)
+ {
+ if(!state->cert_req || !state->client_certs || !state->client_kex ||
+ state->got_client_ccs)
+ throw State_Transition_Error("CertificateVerify");
+ }
+ else if(new_msg == HANDSHAKE_CCS)
+ {
+ if(!state->client_kex || state->client_finished)
+ throw State_Transition_Error("ClientChangeCipherSpec");
+ }
+ else if(new_msg == FINISHED)
+ {
+ if(!state->got_client_ccs)
+ throw State_Transition_Error("ClientFinished");
+ }
+ else
+ throw Unexpected_Message("Unexpected message in handshake");
+ }
+
+}
+
+/*
+* TLS Server Constructor
+*/
+TLS_Server::TLS_Server(std::tr1::function<size_t (byte[], size_t)> input_fn,
+ std::tr1::function<void (const byte[], size_t)> output_fn,
+ const TLS_Policy& policy,
+ RandomNumberGenerator& rng,
+ const X509_Certificate& cert,
+ const Private_Key& cert_key) :
+ input_fn(input_fn),
+ policy(policy),
+ rng(rng),
+ writer(output_fn)
+ {
+ state = 0;
+
+ cert_chain.push_back(cert);
+ private_key = PKCS8::copy_key(cert_key, rng);
+
+ try {
+ active = false;
+ writer.set_version(TLS_V10);
+ do_handshake();
+ active = true;
+ }
+ catch(std::exception& e)
+ {
+ if(state)
+ {
+ delete state;
+ state = 0;
+ }
+
+ writer.alert(FATAL, HANDSHAKE_FAILURE);
+ throw Stream_IO_Error(std::string("TLS_Server: Handshake failed: ") +
+ e.what());
+ }
+ }
+
+/*
+* TLS Server Destructor
+*/
+TLS_Server::~TLS_Server()
+ {
+ close();
+ delete private_key;
+ delete state;
+ }
+
+/*
+* Return the peer's certificate chain
+*/
+std::vector<X509_Certificate> TLS_Server::peer_cert_chain() const
+ {
+ return peer_certs;
+ }
+
+/*
+* Write to a TLS connection
+*/
+void TLS_Server::write(const byte buf[], size_t length)
+ {
+ if(!active)
+ throw Internal_Error("TLS_Server::write called while closed");
+
+ writer.send(APPLICATION_DATA, buf, length);
+ }
+
+/*
+* Read from a TLS connection
+*/
+size_t TLS_Server::read(byte out[], size_t length)
+ {
+ if(!active)
+ throw Internal_Error("TLS_Server::read called while closed");
+
+ writer.flush();
+
+ while(read_buf.size() == 0)
+ {
+ state_machine();
+ if(active == false)
+ break;
+ }
+
+ size_t got = std::min<size_t>(read_buf.size(), length);
+ read_buf.read(out, got);
+ return got;
+ }
+
+/*
+* Check connection status
+*/
+bool TLS_Server::is_closed() const
+ {
+ if(!active)
+ return true;
+ return false;
+ }
+
+/*
+* Close a TLS connection
+*/
+void TLS_Server::close()
+ {
+ close(WARNING, CLOSE_NOTIFY);
+ }
+
+/*
+* Close a TLS connection
+*/
+void TLS_Server::close(Alert_Level level, Alert_Type alert_code)
+ {
+ if(active)
+ {
+ try {
+ active = false;
+ writer.alert(level, alert_code);
+ writer.flush();
+ }
+ catch(...) {}
+ }
+ }
+
+/*
+* Iterate the TLS state machine
+*/
+void TLS_Server::state_machine()
+ {
+ byte rec_type = CONNECTION_CLOSED;
+ SecureVector<byte> record(1024);
+
+ size_t bytes_needed = reader.get_record(rec_type, record);
+
+ while(bytes_needed)
+ {
+ size_t to_get = std::min<size_t>(record.size(), bytes_needed);
+ size_t got = input_fn(&record[0], to_get);
+
+ if(got == 0)
+ {
+ rec_type = CONNECTION_CLOSED;
+ break;
+ }
+
+ reader.add_input(&record[0], got);
+
+ bytes_needed = reader.get_record(rec_type, record);
+ }
+
+ if(rec_type == CONNECTION_CLOSED)
+ {
+ active = false;
+ reader.reset();
+ writer.reset();
+ }
+ else if(rec_type == APPLICATION_DATA)
+ {
+ if(active)
+ read_buf.write(&record[0], record.size());
+ else
+ throw Unexpected_Message("Application data before handshake done");
+ }
+ else if(rec_type == HANDSHAKE || rec_type == CHANGE_CIPHER_SPEC)
+ read_handshake(rec_type, record);
+ else if(rec_type == ALERT)
+ {
+ Alert alert(record);
+
+ if(alert.is_fatal() || alert.type() == CLOSE_NOTIFY)
+ {
+ if(alert.type() == CLOSE_NOTIFY)
+ writer.alert(WARNING, CLOSE_NOTIFY);
+
+ reader.reset();
+ writer.reset();
+ active = false;
+ }
+ }
+ else
+ throw Unexpected_Message("Unknown message type received");
+ }
+
+/*
+* Split up and process handshake messages
+*/
+void TLS_Server::read_handshake(byte rec_type,
+ const MemoryRegion<byte>& rec_buf)
+ {
+ if(rec_type == HANDSHAKE)
+ {
+ if(!state)
+ state = new Handshake_State;
+ state->queue.write(&rec_buf[0], rec_buf.size());
+ }
+
+ while(true)
+ {
+ Handshake_Type type = HANDSHAKE_NONE;
+ SecureVector<byte> contents;
+
+ if(rec_type == HANDSHAKE)
+ {
+ if(state->queue.size() >= 4)
+ {
+ byte head[4] = { 0 };
+ state->queue.peek(head, 4);
+
+ const size_t length = make_u32bit(0, head[1], head[2], head[3]);
+
+ if(state->queue.size() >= length + 4)
+ {
+ type = static_cast<Handshake_Type>(head[0]);
+ contents.resize(length);
+ state->queue.read(head, 4);
+ state->queue.read(&contents[0], contents.size());
+ }
+ }
+ }
+ else if(rec_type == CHANGE_CIPHER_SPEC)
+ {
+ if(state->queue.size() == 0 && rec_buf.size() == 1 && rec_buf[0] == 1)
+ type = HANDSHAKE_CCS;
+ else
+ throw Decoding_Error("Malformed ChangeCipherSpec message");
+ }
+ else
+ throw Decoding_Error("Unknown message type in handshake processing");
+
+ if(type == HANDSHAKE_NONE)
+ break;
+
+ process_handshake_msg(type, contents);
+
+ if(type == HANDSHAKE_CCS || !state)
+ break;
+ }
+ }
+
+/*
+* Process a handshake message
+*/
+void TLS_Server::process_handshake_msg(Handshake_Type type,
+ const MemoryRegion<byte>& contents)
+ {
+ rng.add_entropy(&contents[0], contents.size());
+
+ if(state == 0)
+ throw Unexpected_Message("Unexpected handshake message");
+
+ if(type != HANDSHAKE_CCS && type != FINISHED)
+ {
+ if(type != CLIENT_HELLO_SSLV2)
+ {
+ state->hash.update(static_cast<byte>(type));
+
+ const size_t record_length = contents.size();
+ for(size_t i = 0; i != 3; i++)
+ state->hash.update(get_byte<u32bit>(i+1, record_length));
+ }
+
+ state->hash.update(contents);
+ }
+
+ if(type == CLIENT_HELLO || type == CLIENT_HELLO_SSLV2)
+ {
+ server_check_state(type, state);
+
+ state->client_hello = new Client_Hello(contents, type);
+
+ client_requested_hostname = state->client_hello->hostname();
+
+ state->version = choose_version(state->client_hello->version(),
+ policy.min_version());
+
+ writer.set_version(state->version);
+ reader.set_version(state->version);
+
+ state->server_hello = new Server_Hello(rng, writer,
+ policy, cert_chain,
+ *(state->client_hello),
+ state->version, state->hash);
+
+ state->suite = CipherSuite(state->server_hello->ciphersuite());
+
+ if(state->suite.sig_type() != TLS_ALGO_SIGNER_ANON)
+ {
+ // FIXME: should choose certs based on sig type
+ state->server_certs = new Certificate(writer, cert_chain,
+ state->hash);
+ }
+
+ state->kex_priv = PKCS8::copy_key(*private_key, rng);
+ if(state->suite.kex_type() != TLS_ALGO_KEYEXCH_NOKEX)
+ {
+ if(state->suite.kex_type() == TLS_ALGO_KEYEXCH_RSA)
+ {
+ state->kex_priv = new RSA_PrivateKey(rng,
+ policy.rsa_export_keysize());
+ }
+ else if(state->suite.kex_type() == TLS_ALGO_KEYEXCH_DH)
+ {
+ state->kex_priv = new DH_PrivateKey(rng, policy.dh_group());
+ }
+ else
+ throw Internal_Error("TLS_Server: Unknown ciphersuite kex type");
+
+ state->server_kex =
+ new Server_Key_Exchange(rng, writer,
+ state->kex_priv, private_key,
+ state->client_hello->random(),
+ state->server_hello->random(),
+ state->hash);
+ }
+
+ if(policy.require_client_auth())
+ {
+ state->do_client_auth = true;
+ throw Internal_Error("Client auth not implemented");
+ // FIXME: send client auth request here
+ }
+
+ state->server_hello_done = new Server_Hello_Done(writer, state->hash);
+ }
+ else if(type == CERTIFICATE)
+ {
+ server_check_state(type, state);
+ // FIXME: process this
+ }
+ else if(type == CLIENT_KEX)
+ {
+ server_check_state(type, state);
+
+ state->client_kex = new Client_Key_Exchange(contents, state->suite,
+ state->version);
+
+ SecureVector<byte> pre_master =
+ state->client_kex->pre_master_secret(rng, state->kex_priv,
+ state->server_hello->version());
+
+ state->keys = SessionKeys(state->suite, state->version, pre_master,
+ state->client_hello->random(),
+ state->server_hello->random());
+ }
+ else if(type == CERTIFICATE_VERIFY)
+ {
+ server_check_state(type, state);
+ // FIXME: process this
+ }
+ else if(type == HANDSHAKE_CCS)
+ {
+ server_check_state(type, state);
+
+ reader.set_keys(state->suite, state->keys, SERVER);
+ state->got_client_ccs = true;
+ }
+ else if(type == FINISHED)
+ {
+ server_check_state(type, state);
+
+ state->client_finished = new Finished(contents);
+
+ if(!state->client_finished->verify(state->keys.master_secret(),
+ state->version, state->hash, CLIENT))
+ throw TLS_Exception(DECRYPT_ERROR,
+ "Finished message didn't verify");
+
+ state->hash.update(static_cast<byte>(type));
+
+ const size_t record_length = contents.size();
+ for(size_t i = 0; i != 3; i++)
+ state->hash.update(get_byte<u32bit>(i+1, record_length));
+
+ state->hash.update(contents);
+
+ writer.send(CHANGE_CIPHER_SPEC, 1);
+ writer.flush();
+
+ writer.set_keys(state->suite, state->keys, SERVER);
+
+ state->server_finished = new Finished(writer, state->version, SERVER,
+ state->keys.master_secret(),
+ state->hash);
+
+ delete state;
+ state = 0;
+ active = true;
+ }
+ else
+ throw Unexpected_Message("Unknown handshake message received");
+ }
+
+/*
+* Perform a server-side TLS handshake
+*/
+void TLS_Server::do_handshake()
+ {
+ while(true)
+ {
+ if(active && !state)
+ break;
+
+ state_machine();
+
+ if(!active && !state)
+ throw TLS_Exception(HANDSHAKE_FAILURE, "TLS_Server: Handshake failed");
+ }
+ }
+
+}
diff --git a/src/tls/tls_server.h b/src/tls/tls_server.h
new file mode 100644
index 000000000..510ad15a7
--- /dev/null
+++ b/src/tls/tls_server.h
@@ -0,0 +1,76 @@
+/*
+* TLS Server
+* (C) 2004-2011 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_SERVER_H__
+#define BOTAN_TLS_SERVER_H__
+
+#include <botan/tls_record.h>
+#include <botan/tls_policy.h>
+#include <vector>
+
+namespace Botan {
+
+/**
+* TLS Server
+*/
+class BOTAN_DLL TLS_Server
+ {
+ public:
+ size_t read(byte buf[], size_t buf_len);
+ void write(const byte buf[], size_t buf_len);
+
+ std::vector<X509_Certificate> peer_cert_chain() const;
+
+ std::string requested_hostname() const
+ { return client_requested_hostname; }
+
+ void close();
+ bool is_closed() const;
+
+ /*
+ * FIXME: support cert chains (!)
+ * FIXME: support anonymous servers
+ */
+ TLS_Server(std::tr1::function<size_t (byte[], size_t)> input_fn,
+ std::tr1::function<void (const byte[], size_t)> output_fn,
+ const TLS_Policy& policy,
+ RandomNumberGenerator& rng,
+ const X509_Certificate& cert,
+ const Private_Key& cert_key);
+
+ ~TLS_Server();
+ private:
+ void close(Alert_Level, Alert_Type);
+
+ void do_handshake();
+ void state_machine();
+ void read_handshake(byte, const MemoryRegion<byte>&);
+
+ void process_handshake_msg(Handshake_Type, const MemoryRegion<byte>&);
+
+ std::tr1::function<size_t (byte[], size_t)> input_fn;
+
+ const TLS_Policy& policy;
+ RandomNumberGenerator& rng;
+
+ Record_Writer writer;
+ Record_Reader reader;
+
+ // FIXME: rename to match TLS_Client
+ std::vector<X509_Certificate> cert_chain, peer_certs;
+ Private_Key* private_key;
+
+ class Handshake_State* state;
+ SecureVector<byte> session_id;
+ SecureQueue read_buf;
+ std::string client_requested_hostname;
+ bool active;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_session_key.cpp b/src/tls/tls_session_key.cpp
new file mode 100644
index 000000000..7c75d1758
--- /dev/null
+++ b/src/tls/tls_session_key.cpp
@@ -0,0 +1,170 @@
+/*
+* TLS Session Key
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/tls_session_key.h>
+#include <botan/prf_ssl3.h>
+#include <botan/prf_tls.h>
+#include <botan/lookup.h>
+
+namespace Botan {
+
+/**
+* Return the client cipher key
+*/
+SymmetricKey SessionKeys::client_cipher_key() const
+ {
+ return c_cipher;
+ }
+
+/**
+* Return the server cipher key
+*/
+SymmetricKey SessionKeys::server_cipher_key() const
+ {
+ return s_cipher;
+ }
+
+/**
+* Return the client MAC key
+*/
+SymmetricKey SessionKeys::client_mac_key() const
+ {
+ return c_mac;
+ }
+
+/**
+* Return the server MAC key
+*/
+SymmetricKey SessionKeys::server_mac_key() const
+ {
+ return s_mac;
+ }
+
+/**
+* Return the client cipher IV
+*/
+InitializationVector SessionKeys::client_iv() const
+ {
+ return c_iv;
+ }
+
+/**
+* Return the server cipher IV
+*/
+InitializationVector SessionKeys::server_iv() const
+ {
+ return s_iv;
+ }
+
+/**
+* Return the TLS master secret
+*/
+SecureVector<byte> SessionKeys::master_secret() const
+ {
+ return master_sec;
+ }
+
+/**
+* Generate SSLv3 session keys
+*/
+SymmetricKey SessionKeys::ssl3_keygen(size_t prf_gen,
+ const MemoryRegion<byte>& pre_master,
+ const MemoryRegion<byte>& client_random,
+ const MemoryRegion<byte>& server_random)
+ {
+ SSL3_PRF prf;
+
+ SecureVector<byte> salt;
+ salt += client_random;
+ salt += server_random;
+
+ master_sec = prf.derive_key(48, pre_master, salt);
+
+ salt.clear();
+ salt += server_random;
+ salt += client_random;
+
+ return prf.derive_key(prf_gen, master_sec, salt);
+ }
+
+/**
+* Generate TLS 1.0 session keys
+*/
+SymmetricKey SessionKeys::tls1_keygen(size_t prf_gen,
+ const MemoryRegion<byte>& pre_master,
+ const MemoryRegion<byte>& client_random,
+ const MemoryRegion<byte>& server_random)
+ {
+ const byte MASTER_SECRET_MAGIC[] = {
+ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,
+ 0x74 };
+ const byte KEY_GEN_MAGIC[] = {
+ 0x6B, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6E, 0x73, 0x69, 0x6F,
+ 0x6E };
+
+ TLS_PRF prf;
+
+ SecureVector<byte> salt;
+ salt += std::make_pair(MASTER_SECRET_MAGIC, sizeof(MASTER_SECRET_MAGIC));
+ salt += client_random;
+ salt += server_random;
+
+ master_sec = prf.derive_key(48, pre_master, salt);
+
+ salt.clear();
+ salt += std::make_pair(KEY_GEN_MAGIC, sizeof(KEY_GEN_MAGIC));
+ salt += server_random;
+ salt += client_random;
+
+ return prf.derive_key(prf_gen, master_sec, salt);
+ }
+
+/**
+* SessionKeys Constructor
+*/
+SessionKeys::SessionKeys(const CipherSuite& suite, Version_Code version,
+ const MemoryRegion<byte>& pre_master_secret,
+ const MemoryRegion<byte>& c_random,
+ const MemoryRegion<byte>& s_random)
+ {
+ if(version != SSL_V3 && version != TLS_V10 && version != TLS_V11)
+ throw Invalid_Argument("SessionKeys: Unknown version code");
+
+ const size_t mac_keylen = output_length_of(suite.mac_algo());
+ const size_t cipher_keylen = suite.cipher_keylen();
+
+ size_t cipher_ivlen = 0;
+ if(have_block_cipher(suite.cipher_algo()))
+ cipher_ivlen = block_size_of(suite.cipher_algo());
+
+ const size_t prf_gen = 2 * (mac_keylen + cipher_keylen + cipher_ivlen);
+
+ SymmetricKey keyblock = (version == SSL_V3) ?
+ ssl3_keygen(prf_gen, pre_master_secret, c_random, s_random) :
+ tls1_keygen(prf_gen, pre_master_secret, c_random, s_random);
+
+ const byte* key_data = keyblock.begin();
+
+ c_mac = SymmetricKey(key_data, mac_keylen);
+ key_data += mac_keylen;
+
+ s_mac = SymmetricKey(key_data, mac_keylen);
+ key_data += mac_keylen;
+
+ c_cipher = SymmetricKey(key_data, cipher_keylen);
+ key_data += cipher_keylen;
+
+ s_cipher = SymmetricKey(key_data, cipher_keylen);
+ key_data += cipher_keylen;
+
+ c_iv = InitializationVector(key_data, cipher_ivlen);
+ key_data += cipher_ivlen;
+
+ s_iv = InitializationVector(key_data, cipher_ivlen);
+ }
+
+}
diff --git a/src/tls/tls_session_key.h b/src/tls/tls_session_key.h
new file mode 100644
index 000000000..51397984b
--- /dev/null
+++ b/src/tls/tls_session_key.h
@@ -0,0 +1,52 @@
+/*
+* TLS Session Key
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_SESSION_KEYS_H__
+#define BOTAN_TLS_SESSION_KEYS_H__
+
+#include <botan/tls_suites.h>
+#include <botan/tls_exceptn.h>
+#include <botan/symkey.h>
+
+namespace Botan {
+
+/**
+* TLS Session Keys
+*/
+class BOTAN_DLL SessionKeys
+ {
+ public:
+ SymmetricKey client_cipher_key() const;
+ SymmetricKey server_cipher_key() const;
+
+ SymmetricKey client_mac_key() const;
+ SymmetricKey server_mac_key() const;
+
+ InitializationVector client_iv() const;
+ InitializationVector server_iv() const;
+
+ SecureVector<byte> master_secret() const;
+
+ SessionKeys() {}
+ SessionKeys(const CipherSuite&, Version_Code, const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&, const MemoryRegion<byte>&);
+ private:
+ SymmetricKey ssl3_keygen(size_t, const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&);
+ SymmetricKey tls1_keygen(size_t, const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&,
+ const MemoryRegion<byte>&);
+
+ SecureVector<byte> master_sec;
+ SymmetricKey c_cipher, s_cipher, c_mac, s_mac;
+ InitializationVector c_iv, s_iv;
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_state.cpp b/src/tls/tls_state.cpp
new file mode 100644
index 000000000..6aaf5e201
--- /dev/null
+++ b/src/tls/tls_state.cpp
@@ -0,0 +1,59 @@
+/*
+* TLS Handshaking
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/internal/tls_state.h>
+
+namespace Botan {
+
+/**
+* Initialize the SSL/TLS Handshake State
+*/
+Handshake_State::Handshake_State()
+ {
+ client_hello = 0;
+ server_hello = 0;
+ server_certs = 0;
+ server_kex = 0;
+ cert_req = 0;
+ server_hello_done = 0;
+
+ client_certs = 0;
+ client_kex = 0;
+ client_verify = 0;
+ client_finished = 0;
+ server_finished = 0;
+
+ kex_pub = 0;
+ kex_priv = 0;
+
+ do_client_auth = got_client_ccs = got_server_ccs = false;
+ version = SSL_V3;
+ }
+
+/**
+* Destroy the SSL/TLS Handshake State
+*/
+Handshake_State::~Handshake_State()
+ {
+ delete client_hello;
+ delete server_hello;
+ delete server_certs;
+ delete server_kex;
+ delete cert_req;
+ delete server_hello_done;
+
+ delete client_certs;
+ delete client_kex;
+ delete client_verify;
+ delete client_finished;
+ delete server_finished;
+
+ delete kex_pub;
+ delete kex_priv;
+ }
+
+}
diff --git a/src/tls/tls_state.h b/src/tls/tls_state.h
new file mode 100644
index 000000000..e2728198f
--- /dev/null
+++ b/src/tls/tls_state.h
@@ -0,0 +1,53 @@
+/*
+* TLS Handshake State
+* (C) 2004-2006 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_HANDSHAKE_H__
+#define BOTAN_TLS_HANDSHAKE_H__
+
+#include <botan/internal/tls_messages.h>
+#include <botan/secqueue.h>
+
+namespace Botan {
+
+/**
+* SSL/TLS Handshake State
+*/
+class Handshake_State
+ {
+ public:
+ Client_Hello* client_hello;
+ Server_Hello* server_hello;
+ Certificate* server_certs;
+ Server_Key_Exchange* server_kex;
+ Certificate_Req* cert_req;
+ Server_Hello_Done* server_hello_done;
+
+ Certificate* client_certs;
+ Client_Key_Exchange* client_kex;
+ Certificate_Verify* client_verify;
+ Finished* client_finished;
+ Finished* server_finished;
+
+ Public_Key* kex_pub;
+ Private_Key* kex_priv;
+
+ CipherSuite suite;
+ SessionKeys keys;
+ HandshakeHash hash;
+
+ SecureQueue queue;
+
+ Version_Code version;
+ bool got_client_ccs, got_server_ccs, do_client_auth;
+
+ Handshake_State();
+ ~Handshake_State();
+ };
+
+}
+
+#endif
diff --git a/src/tls/tls_suites.cpp b/src/tls/tls_suites.cpp
new file mode 100644
index 000000000..07cbec608
--- /dev/null
+++ b/src/tls/tls_suites.cpp
@@ -0,0 +1,281 @@
+/*
+* TLS Cipher Suites
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#include <botan/tls_suites.h>
+#include <botan/tls_exceptn.h>
+
+namespace Botan {
+
+/**
+* Convert an SSL/TLS ciphersuite to algorithm fields
+*/
+TLS_Ciphersuite_Algos CipherSuite::lookup_ciphersuite(u16bit suite)
+ {
+ if(suite == TLS_RSA_WITH_RC4_128_MD5)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_MD5 |
+ TLS_ALGO_CIPHER_RC4_128);
+
+ if(suite == TLS_RSA_WITH_RC4_128_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_RC4_128);
+
+ if(suite == TLS_RSA_WITH_3DES_EDE_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_3DES_CBC);
+
+ if(suite == TLS_RSA_WITH_AES_128_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_RSA_WITH_AES_256_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_RSA_WITH_SEED_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_SEED_CBC);
+
+ if(suite == TLS_RSA_WITH_AES_128_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_RSA_WITH_AES_256_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_NOKEX |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_3DES_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_SEED_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_SEED_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_DSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_3DES_CBC);
+
+ if(suite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_DHE_DSS_WITH_SEED_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_SEED_CBC);
+
+ if(suite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_DHE_RSA_WITH_AES_128_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_DHE_RSA_WITH_AES_256_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_DH |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_RC4_128);
+
+ if(suite == TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_3DES_CBC);
+
+ if(suite == TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA384 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_ECDHE_RSA_WITH_RC4_128_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_RC4_128);
+
+ if(suite == TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_3DES_CBC);
+
+ if(suite == TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_RSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA1 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ if(suite == TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA256 |
+ TLS_ALGO_CIPHER_AES128_CBC);
+
+ if(suite == TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384)
+ return TLS_Ciphersuite_Algos(TLS_ALGO_SIGNER_ECDSA |
+ TLS_ALGO_KEYEXCH_ECDH |
+ TLS_ALGO_MAC_SHA384 |
+ TLS_ALGO_CIPHER_AES256_CBC);
+
+ return TLS_Ciphersuite_Algos(0);
+ }
+
+namespace {
+
+std::pair<std::string, size_t> cipher_code_to_name(TLS_Ciphersuite_Algos algo)
+ {
+ if((algo & TLS_ALGO_CIPHER_MASK) == TLS_ALGO_CIPHER_RC4_128)
+ return std::make_pair("ARC4", 16);
+
+ if((algo & TLS_ALGO_CIPHER_MASK) == TLS_ALGO_CIPHER_3DES_CBC)
+ return std::make_pair("3DES", 24);
+
+ if((algo & TLS_ALGO_CIPHER_MASK) == TLS_ALGO_CIPHER_AES128_CBC)
+ return std::make_pair("AES-128", 16);
+
+ if((algo & TLS_ALGO_CIPHER_MASK) == TLS_ALGO_CIPHER_AES256_CBC)
+ return std::make_pair("AES-256", 32);
+
+ if((algo & TLS_ALGO_CIPHER_MASK) == TLS_ALGO_CIPHER_SEED_CBC)
+ return std::make_pair("SEED", 16);
+
+ throw TLS_Exception(INTERNAL_ERROR,
+ "CipherSuite: Unknown cipher type " + to_string(algo));
+ }
+
+std::string mac_code_to_name(TLS_Ciphersuite_Algos algo)
+ {
+ if((algo & TLS_ALGO_MAC_MASK) == TLS_ALGO_MAC_MD5)
+ return "MD5";
+
+ if((algo & TLS_ALGO_MAC_MASK) == TLS_ALGO_MAC_SHA1)
+ return "SHA-1";
+
+ if((algo & TLS_ALGO_MAC_MASK) == TLS_ALGO_MAC_SHA256)
+ return "SHA-256";
+
+ if((algo & TLS_ALGO_MAC_MASK) == TLS_ALGO_MAC_SHA384)
+ return "SHA-384";
+
+ throw TLS_Exception(INTERNAL_ERROR,
+ "CipherSuite: Unknown MAC type " + to_string(algo));
+ }
+
+}
+
+/**
+* CipherSuite Constructor
+*/
+CipherSuite::CipherSuite(u16bit suite_code)
+ {
+ if(suite_code == 0)
+ return;
+
+ TLS_Ciphersuite_Algos algos = lookup_ciphersuite(suite_code);
+
+ if(algos == 0)
+ throw Invalid_Argument("Unknown ciphersuite: " + to_string(suite_code));
+
+ sig_algo = TLS_Ciphersuite_Algos(algos & TLS_ALGO_SIGNER_MASK);
+
+ kex_algo = TLS_Ciphersuite_Algos(algos & TLS_ALGO_KEYEXCH_MASK);
+
+ std::pair<std::string, size_t> cipher_info = cipher_code_to_name(algos);
+
+ cipher = cipher_info.first;
+ cipher_key_length = cipher_info.second;
+
+ mac = mac_code_to_name(algos);
+ }
+
+}
diff --git a/src/tls/tls_suites.h b/src/tls/tls_suites.h
new file mode 100644
index 000000000..8d6db0e8b
--- /dev/null
+++ b/src/tls/tls_suites.h
@@ -0,0 +1,42 @@
+/*
+* Cipher Suites
+* (C) 2004-2010 Jack Lloyd
+*
+* Released under the terms of the Botan license
+*/
+
+#ifndef BOTAN_TLS_CIPHERSUITES_H__
+#define BOTAN_TLS_CIPHERSUITES_H__
+
+#include <botan/types.h>
+#include <botan/tls_magic.h>
+#include <string>
+
+namespace Botan {
+
+/**
+* Ciphersuite Information
+*/
+class BOTAN_DLL CipherSuite
+ {
+ public:
+ static TLS_Ciphersuite_Algos lookup_ciphersuite(u16bit suite);
+
+ std::string cipher_algo() const { return cipher; }
+ std::string mac_algo() const { return mac; }
+
+ size_t cipher_keylen() const { return cipher_key_length; }
+
+ TLS_Ciphersuite_Algos kex_type() const { return kex_algo; }
+ TLS_Ciphersuite_Algos sig_type() const { return sig_algo; }
+
+ CipherSuite(u16bit = 0);
+ private:
+ TLS_Ciphersuite_Algos kex_algo, sig_algo;
+ std::string cipher, mac;
+ size_t cipher_key_length;
+ };
+
+}
+
+#endif