diff options
115 files changed, 10664 insertions, 4848 deletions
diff --git a/configure.py b/configure.py index 71d2a3d39..affa56333 100755 --- a/configure.py +++ b/configure.py @@ -2,7 +2,7 @@ """ Configuration program for botan (http://botan.randombit.net/) - (C) 2009-2011 Jack Lloyd + (C) 2009,2010,2011,2012 Jack Lloyd Distributed under the terms of the Botan license Tested with CPython 2.6, 2.7, 3.1 and PyPy 1.5 @@ -1035,10 +1035,15 @@ def create_template_vars(build_config, options, modules, cc, arch, osinfo): if dir.startswith('src'): parts = dir.split(os.sep)[1:] + + # Handle src/X/X.cpp -> X.o if file == parts[-1] + '.cpp': name = '_'.join(dir.split(os.sep)[1:]) + '.cpp' else: name = '_'.join(dir.split(os.sep)[1:]) + '_' + file + + # Special case hack cause I'm lazy + name = name.replace('tls_tls_', 'tls_') else: name = file diff --git a/doc/contents.txt b/doc/contents.txt index dd600d587..141c9188f 100644 --- a/doc/contents.txt +++ b/doc/contents.txt @@ -12,7 +12,8 @@ Contents filters pubkey x509 - ssl + tls + credentials_manager bigint lowlevel secmem diff --git a/doc/examples/GNUmakefile b/doc/examples/GNUmakefile index a5da47a7c..ea5c8dfe4 100644 --- a/doc/examples/GNUmakefile +++ b/doc/examples/GNUmakefile @@ -19,3 +19,6 @@ clean: eax_test: eax_test.cpp $(CXX) $(CFLAGS) $? $(LIBS) -lboost_regex -o $@ + +asio_tls_server: asio_tls_server.cpp credentials.h + $(CXX) $(CFLAGS) $< $(LIBS) -lboost_thread -lboost_system -o $@ diff --git a/doc/examples/asio_tls_server.cpp b/doc/examples/asio_tls_server.cpp new file mode 100644 index 000000000..5c606a3f2 --- /dev/null +++ b/doc/examples/asio_tls_server.cpp @@ -0,0 +1,330 @@ +#include <iostream> +#include <string> +#include <vector> + +#include <boost/asio.hpp> +#include <boost/bind.hpp> +#include <boost/thread.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> + +#include <botan/tls_server.h> +#include <botan/x509cert.h> +#include <botan/pkcs8.h> +#include <botan/auto_rng.h> +#include <botan/init.h> + +#include "credentials.h" + +using Botan::byte; +using boost::asio::ip::tcp; + +class tls_server_session : public boost::enable_shared_from_this<tls_server_session> + { + public: + typedef boost::shared_ptr<tls_server_session> pointer; + + static pointer create(boost::asio::io_service& io_service, + Botan::TLS::Session_Manager& session_manager, + Botan::Credentials_Manager& credentials, + Botan::TLS::Policy& policy, + Botan::RandomNumberGenerator& rng) + { + return pointer( + new tls_server_session( + io_service, + session_manager, + credentials, + policy, + rng) + ); + } + + tcp::socket& socket() { return m_socket; } + + void start() + { + m_socket.async_read_some( + boost::asio::buffer(m_read_buf, sizeof(m_read_buf)), + m_strand.wrap( + boost::bind(&tls_server_session::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + } + + void stop() { m_socket.close(); } + + private: + tls_server_session(boost::asio::io_service& io_service, + Botan::TLS::Session_Manager& session_manager, + Botan::Credentials_Manager& credentials, + Botan::TLS::Policy& policy, + Botan::RandomNumberGenerator& rng) : + m_strand(io_service), + m_socket(io_service), + m_tls(boost::bind(&tls_server_session::tls_output_wanted, this, _1, _2), + boost::bind(&tls_server_session::tls_data_recv, this, _1, _2, _3), + boost::bind(&tls_server_session::tls_handshake_complete, this, _1), + session_manager, + credentials, + policy, + rng) + { + } + + void handle_read(const boost::system::error_code& error, + size_t bytes_transferred) + { + if(!error) + { + try + { + m_tls.received_data(m_read_buf, bytes_transferred); + } + catch(std::exception& e) + { + std::cout << "Read failed " << e.what() << "\n"; + stop(); + return; + } + + m_socket.async_read_some( + boost::asio::buffer(m_read_buf, sizeof(m_read_buf)), + m_strand.wrap(boost::bind(&tls_server_session::handle_read, shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + } + else + { + stop(); + } + } + + void handle_write(const boost::system::error_code& error) + { + if(!error) + { + m_write_buf.clear(); + + // initiate another write if needed + tls_output_wanted(NULL, 0); + } + else + { + stop(); + } + } + + void tls_output_wanted(const byte buf[], size_t buf_len) + { + if(buf_len > 0) + m_outbox.insert(m_outbox.end(), buf, buf + buf_len); + + // no write pending and have output pending + if(m_write_buf.empty() && !m_outbox.empty()) + { + std::swap(m_outbox, m_write_buf); + + boost::asio::async_write(m_socket, + boost::asio::buffer(&m_write_buf[0], m_write_buf.size()), + m_strand.wrap( + boost::bind(&tls_server_session::handle_write, + shared_from_this(), + boost::asio::placeholders::error))); + } + } + + void tls_data_recv(const byte buf[], size_t buf_len, Botan::TLS::Alert alert) + { + if(alert.is_valid()) + { + if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY) + { + m_tls.close(); + return; + } + } + + if(buf_len > 4) // FIXME: ghetto + { + std::string out; + out += "\r\n"; + out += "HTTP/1.0 200 OK\r\n"; + out += "Server: Botan ASIO test server\r\n"; + if(m_hostname != "") + out += "Host: " + m_hostname + "\r\n"; + out += "Content-Type: text/html\r\n"; + out += "\r\n"; + out += "<html><body>Greets. You said: "; + out += std::string((const char*)buf, buf_len); + out += "</body></html>\r\n\r\n"; + + m_tls.send(reinterpret_cast<const byte*>(&out[0]), + out.size()); + m_tls.close(); + } + } + + bool tls_handshake_complete(const Botan::TLS::Session& session) + { + m_hostname = session.sni_hostname(); + return true; + } + + boost::asio::io_service::strand m_strand; // serialization + + tcp::socket m_socket; + Botan::TLS::Server m_tls; + std::string m_hostname; + + unsigned char m_read_buf[Botan::TLS::MAX_TLS_RECORD_SIZE]; + + // used to hold the data currently being written by the system + std::vector<byte> m_write_buf; + + // used to hold data queued for writing + std::vector<byte> m_outbox; + }; + +class Session_Manager_Locked : public Botan::TLS::Session_Manager + { + public: + bool load_from_session_id(const Botan::MemoryRegion<byte>& session_id, + Botan::TLS::Session& session) + { + boost::lock_guard<boost::mutex> lock(m_mutex); + return m_session_manager.load_from_session_id(session_id, session); + } + + bool load_from_host_info(const std::string& hostname, Botan::u16bit port, + Botan::TLS::Session& session) + { + boost::lock_guard<boost::mutex> lock(m_mutex); + return m_session_manager.load_from_host_info(hostname, port, session); + }; + + void remove_entry(const Botan::MemoryRegion<byte>& session_id) + { + boost::lock_guard<boost::mutex> lock(m_mutex); + m_session_manager.remove_entry(session_id); + } + + void save(const Botan::TLS::Session& session) + { + boost::lock_guard<boost::mutex> lock(m_mutex); + m_session_manager.save(session); + } + + private: + boost::mutex m_mutex; + Botan::TLS::Session_Manager_In_Memory m_session_manager; + + }; + +class tls_server + { + public: + typedef tls_server_session session; + + tls_server(boost::asio::io_service& io_service, unsigned short port) : + m_acceptor(io_service, tcp::endpoint(tcp::v4(), port)), + m_creds(m_rng) + { + session::pointer new_session = make_session(); + + m_acceptor.async_accept( + new_session->socket(), + boost::bind( + &tls_server::handle_accept, + this, + new_session, + boost::asio::placeholders::error) + ); + } + + private: + session::pointer make_session() + { + return session::create( + m_acceptor.get_io_service(), + m_session_manager, + m_creds, + m_policy, + m_rng + ); + } + + void handle_accept(session::pointer new_session, + const boost::system::error_code& error) + { + if (!error) + { + new_session->start(); + + new_session = make_session(); + + m_acceptor.async_accept( + new_session->socket(), + boost::bind( + &tls_server::handle_accept, + this, + new_session, + boost::asio::placeholders::error) + ); + } + } + + tcp::acceptor m_acceptor; + + Botan::AutoSeeded_RNG m_rng; + Session_Manager_Locked m_session_manager; + Botan::TLS::Policy m_policy; + Credentials_Manager_Simple m_creds; + }; + +size_t choose_thread_count() + { + size_t result = boost::thread::hardware_concurrency(); + + if(result) + return result; + + return 2; + } + +int main() + { + try + { + Botan::LibraryInitializer init("thread_safe=true"); + boost::asio::io_service io_service; + + unsigned short port = 4434; + tls_server server(io_service, port); + + const size_t num_threads = choose_thread_count(); + + std::cout << "Using " << num_threads << " threads\n"; + + std::vector<boost::shared_ptr<boost::thread> > threads; + + for(size_t i = 0; i != num_threads; ++i) + { + boost::shared_ptr<boost::thread> thread( + new boost::thread( + boost::bind(&boost::asio::io_service::run, &io_service))); + threads.push_back(thread); + } + + // Wait for all threads in the pool to exit. + for (size_t i = 0; i < threads.size(); ++i) + threads[i]->join(); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + } + + return 0; + } + diff --git a/doc/examples/cms_dec.cpp b/doc/examples/cms_dec.cpp deleted file mode 100644 index 84355fb4a..000000000 --- a/doc/examples/cms_dec.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* -* (C) 2009 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#include <botan/botan.h> -#include <botan/pkcs8.h> -#include <botan/cms_dec.h> -using namespace Botan; - -#include <iostream> -#include <memory> - -int main(int argc, char* argv[]) - { - if(argc != 2) - { - std::cout << "Usage: " << argv[0] << " <filename>\n"; - return 1; - } - - Botan::LibraryInitializer init; - - try { - AutoSeeded_RNG rng; - - X509_Certificate mycert("mycert.pem"); - PKCS8_PrivateKey* mykey = PKCS8::load_key("mykey.pem", rng, "cut"); - - X509_Certificate yourcert("yourcert.pem"); - X509_Certificate cacert("cacert.pem"); - X509_Certificate int_ca("int_ca.pem"); - - X509_Store store; - store.add_cert(mycert); - store.add_cert(yourcert); - store.add_cert(cacert, true); - store.add_cert(int_ca); - - DataSource_Stream message(argv[1]); - - CMS_Decoder decoder(message, store, mykey); - - while(decoder.layer_type() != CMS_Decoder::DATA) - { - CMS_Decoder::Status status = decoder.layer_status(); - CMS_Decoder::Content_Type content = decoder.layer_type(); - - if(status == CMS_Decoder::FAILURE) - { - std::cout << "Failure reading CMS data" << std::endl; - break; - } - - if(content == CMS_Decoder::DIGESTED) - { - std::cout << "Digested data, hash = " << decoder.layer_info() - << std::endl; - std::cout << "Hash is " - << ((status == CMS_Decoder::GOOD) ? "good" : "bad") - << std::endl; - } - - if(content == CMS_Decoder::SIGNED) - { - // how to handle multiple signers? they can all exist within a - // single level... - - std::cout << "Signed by " << decoder.layer_info() << std::endl; - //std::cout << "Sign time: " << decoder.xxx() << std::endl; - std::cout << "Signature is "; - if(status == CMS_Decoder::GOOD) - std::cout << "valid"; - else if(status == CMS_Decoder::BAD) - std::cout << "bad"; - else if(status == CMS_Decoder::NO_KEY) - std::cout << "(cannot check, no known cert)"; - std::cout << std::endl; - } - if(content == CMS_Decoder::ENVELOPED || - content == CMS_Decoder::COMPRESSED || - content == CMS_Decoder::AUTHENTICATED) - { - if(content == CMS_Decoder::ENVELOPED) - std::cout << "Enveloped"; - if(content == CMS_Decoder::COMPRESSED) - std::cout << "Compressed"; - if(content == CMS_Decoder::AUTHENTICATED) - std::cout << "MACed"; - - std::cout << ", algo = " << decoder.layer_info() << std::endl; - - if(content == CMS_Decoder::AUTHENTICATED) - { - std::cout << "MAC status is "; - if(status == CMS_Decoder::GOOD) - std::cout << "valid"; - else if(status == CMS_Decoder::BAD) - std::cout << "bad"; - else if(status == CMS_Decoder::NO_KEY) - std::cout << "(cannot check, no key)"; - std::cout << std::endl; - } - } - decoder.next_layer(); - } - - if(decoder.layer_type() == CMS_Decoder::DATA) - std::cout << "Message is \"" << decoder.get_data() - << '"' << std::endl; - else - std::cout << "No data anywhere?" << std::endl; - } - catch(std::exception& e) - { - std::cerr << e.what() << std::endl; - } - return 0; - } diff --git a/doc/examples/cms_enc.cpp b/doc/examples/cms_enc.cpp deleted file mode 100644 index 2cf813987..000000000 --- a/doc/examples/cms_enc.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -* (C) 2009 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#include <botan/botan.h> -#include <botan/cms_enc.h> -using namespace Botan; - -#include <iostream> -#include <fstream> -#include <memory> - -int main() - { - Botan::LibraryInitializer init; - - try { - - X509_Certificate mycert("mycert.pem"); - X509_Certificate mycert2("mycert2.pem"); - X509_Certificate yourcert("yourcert.pem"); - X509_Certificate cacert("cacert.pem"); - X509_Certificate int_ca("int_ca.pem"); - - AutoSeeded_RNG rng; - - X509_Store store; - store.add_cert(mycert); - store.add_cert(mycert2); - store.add_cert(yourcert); - store.add_cert(int_ca); - store.add_cert(cacert, true); - - const std::string msg = "prioncorp: we don't toy\n"; - - CMS_Encoder encoder(msg); - - encoder.compress("Zlib"); - encoder.digest(); - encoder.encrypt(rng, mycert); - - /* - PKCS8_PrivateKey* mykey = PKCS8::load_key("mykey.pem", rng, "cut"); - encoder.sign(store, *mykey); - */ - - SecureVector<byte> raw = encoder.get_contents(); - std::ofstream out("out.der"); - - out.write((const char*)raw.begin(), raw.size()); - } - catch(std::exception& e) - { - std::cerr << e.what() << std::endl; - } - return 0; - } diff --git a/doc/examples/credentials.h b/doc/examples/credentials.h new file mode 100644 index 000000000..0999b251d --- /dev/null +++ b/doc/examples/credentials.h @@ -0,0 +1,178 @@ + +#ifndef EXAMPLE_CREDENTIALS_MANAGER_H__ +#define EXAMPLE_CREDENTIALS_MANAGER_H__ + +#include <botan/credentials_manager.h> +#include <botan/x509self.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +#include <botan/ecdsa.h> +#include <iostream> +#include <fstream> +#include <memory> + +bool value_exists(const std::vector<std::string>& vec, + const std::string& val) + { + for(size_t i = 0; i != vec.size(); ++i) + if(vec[i] == val) + return true; + return false; + } + +class Credentials_Manager_Simple : public Botan::Credentials_Manager + { + public: + Credentials_Manager_Simple(Botan::RandomNumberGenerator& rng) : rng(rng) {} + + std::string psk_identity_hint(const std::string&, + const std::string&) + { + return ""; + } + + std::string psk_identity(const std::string&, const std::string&, + const std::string& identity_hint) + { + return "Client_identity"; + } + + Botan::SymmetricKey psk(const std::string& type, const std::string& context, + const std::string& identity) + { + if(type == "tls-server" && context == "session-ticket") + { + if(session_ticket_key.length() == 0) + session_ticket_key = Botan::SymmetricKey(rng, 32); + return session_ticket_key; + } + + if(identity == "Client_identity") + return Botan::SymmetricKey("b5a72e1387552e6dc10766dc0eda12961f5b21e17f98ef4c41e6572e53bd7527"); + + throw Botan::Internal_Error("No PSK set for " + identity); + } + + std::pair<Botan::X509_Certificate,Botan::Private_Key*> + load_or_make_cert(const std::string& hostname, + const std::string& key_type, + Botan::RandomNumberGenerator& rng) + { + using namespace Botan; + + const std::string key_fsname_prefix = hostname + "." + key_type + "."; + const std::string key_file_name = key_fsname_prefix + "key"; + const std::string cert_file_name = key_fsname_prefix + "crt"; + + try + { + X509_Certificate cert(cert_file_name); + Private_Key* key = PKCS8::load_key(key_file_name, rng); + + //std::cout << "Loaded existing key/cert from " << cert_file_name << " and " << key_file_name << "\n"; + + return std::make_pair(cert, key); + } + catch(...) {} + + // Failed. Instead, make a new one + + std::cout << "Creating new certificate for identifier '" << hostname << "'\n"; + + X509_Cert_Options opts; + + opts.common_name = hostname; + opts.country = "US"; + opts.email = "root@" + hostname; + opts.dns = hostname; + + std::auto_ptr<Private_Key> key; + if(key_type == "rsa") + key.reset(new RSA_PrivateKey(rng, 1024)); + else if(key_type == "dsa") + key.reset(new DSA_PrivateKey(rng, DL_Group("dsa/jce/1024"))); + else if(key_type == "ecdsa") + key.reset(new ECDSA_PrivateKey(rng, EC_Group("secp256r1"))); + else + throw std::runtime_error("Don't know what to do about key type '" + key_type + "'"); + + X509_Certificate cert = + X509::create_self_signed_cert(opts, *key, "SHA-1", rng); + + // Now save both + + std::cout << "Saving new " << key_type << " key to " << key_file_name << "\n"; + std::ofstream key_file(key_file_name.c_str()); + key_file << PKCS8::PEM_encode(*key, rng, ""); + key_file.close(); + + std::cout << "Saving new " << key_type << " cert to " << key_file_name << "\n"; + std::ofstream cert_file(cert_file_name.c_str()); + cert_file << cert.PEM_encode() << "\n"; + cert_file.close(); + + return std::make_pair(cert, key.release()); + } + + std::vector<Botan::X509_Certificate> cert_chain( + const std::vector<std::string>& cert_key_types, + const std::string& type, + const std::string& context) + { + using namespace Botan; + + std::vector<X509_Certificate> certs; + + try + { + if(type == "tls-server") + { + const std::string hostname = (context == "" ? "localhost" : context); + + std::string key_name = ""; + + if(value_exists(cert_key_types, "RSA")) + key_name = "rsa"; + else if(value_exists(cert_key_types, "DSA")) + key_name = "dsa"; + else if(value_exists(cert_key_types, "ECDSA")) + key_name = "ecdsa"; + + std::pair<X509_Certificate, Private_Key*> cert_and_key = + load_or_make_cert(hostname, key_name, rng); + + certs_and_keys[cert_and_key.first] = cert_and_key.second; + certs.push_back(cert_and_key.first); + } + else if(type == "tls-client") + { + X509_Certificate cert("user-rsa.crt"); + Private_Key* key = PKCS8::load_key("user-rsa.key", rng); + + certs_and_keys[cert] = key; + certs.push_back(cert); + } + } + catch(std::exception& e) + { + std::cout << e.what() << "\n"; + } + + return certs; + } + + Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert, + const std::string& type, + const std::string& context) + { + return certs_and_keys[cert]; + } + + private: + Botan::RandomNumberGenerator& rng; + + Botan::SymmetricKey session_ticket_key; + std::map<Botan::X509_Certificate, Botan::Private_Key*> certs_and_keys; + }; + +#endif diff --git a/doc/examples/self_sig.cpp b/doc/examples/self_sig.cpp index 64b778b71..7cb159db9 100644 --- a/doc/examples/self_sig.cpp +++ b/doc/examples/self_sig.cpp @@ -36,6 +36,9 @@ int main(int argc, char* argv[]) AutoSeeded_RNG rng; RSA_PrivateKey key(rng, 2048); + //DL_Group group(rng, DL_Group::DSA_Kosherizer, 2048, 256); + + //DSA_PrivateKey key(rng, group); std::ofstream priv_key("private.pem"); priv_key << PKCS8::PEM_encode(key, rng, argv[1]); diff --git a/doc/examples/socket.h b/doc/examples/socket.h index f7ce98fea..9e16ab36a 100644 --- a/doc/examples/socket.h +++ b/doc/examples/socket.h @@ -48,6 +48,7 @@ #include <netdb.h> #include <unistd.h> #include <errno.h> + #include <fcntl.h> typedef int socket_t; const socket_t invalid_socket = -1; @@ -66,7 +67,7 @@ class Socket { public: - size_t read(unsigned char[], size_t); + size_t read(unsigned char[], size_t, bool dont_block = false); void write(const unsigned char[], size_t); std::string peer_id() const { return peer; } @@ -158,23 +159,28 @@ Socket::Socket(const std::string& host, unsigned short port) : peer(host) throw std::runtime_error("Socket: connect failed"); } + //fcntl(fd, F_SETFL, O_NONBLOCK); + sockfd = fd; } /** * Read from a Unix socket */ -size_t Socket::read(unsigned char buf[], size_t length) +size_t Socket::read(unsigned char buf[], size_t length, bool partial) { if(sockfd == invalid_socket) throw std::runtime_error("Socket::read: Socket not connected"); size_t got = 0; + int flags = MSG_NOSIGNAL; + while(length) { - ssize_t this_time = ::recv(sockfd, (char*)buf + got, - length, MSG_NOSIGNAL); + ssize_t this_time = ::recv(sockfd, (char*)buf + got, length, flags); + + const bool full_ret = (this_time == (ssize_t)length); if(this_time == 0) break; @@ -183,13 +189,19 @@ size_t Socket::read(unsigned char buf[], size_t length) { if(socket_error_code == EINTR) this_time = 0; + else if(socket_error_code == EAGAIN) + break; else throw std::runtime_error("Socket::read: Socket read failed"); } got += this_time; length -= this_time; + + if(partial && !full_ret) + break; } + return got; } diff --git a/doc/examples/tls_client.cpp b/doc/examples/tls_client.cpp index cedfe1ca8..d0a354e66 100644 --- a/doc/examples/tls_client.cpp +++ b/doc/examples/tls_client.cpp @@ -1,91 +1,233 @@ #include <botan/botan.h> #include <botan/tls_client.h> -#include "socket.h" - -using namespace Botan; - +#include <botan/pkcs8.h> +#include <botan/hex.h> #include <stdio.h> #include <string> #include <iostream> #include <memory> -class Client_TLS_Policy : public TLS_Policy +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#if defined(BOTAN_HAS_TLS_SQLITE_SESSION_MANAGER) + #include <botan/tls_sqlite_sess_mgr.h> +#endif + +#include "credentials.h" + +using namespace Botan; + +using namespace std::tr1::placeholders; + +int connect_to_host(const std::string& host, u16bit port) { - public: - bool check_cert(const std::vector<X509_Certificate>& certs) const - { - for(size_t i = 0; i != certs.size(); ++i) - { - std::cout << certs[i].to_string(); - } + hostent* host_addr = ::gethostbyname(host.c_str()); + + if(host_addr == 0) + throw std::runtime_error("gethostbyname failed for " + host); + + if(host_addr->h_addrtype != AF_INET) // FIXME + throw std::runtime_error(host + " has IPv6 address"); + + int fd = ::socket(PF_INET, SOCK_STREAM, 0); + if(fd == -1) + throw std::runtime_error("Unable to acquire socket"); + + sockaddr_in socket_info; + ::memset(&socket_info, 0, sizeof(socket_info)); + socket_info.sin_family = AF_INET; + socket_info.sin_port = htons(port); + + ::memcpy(&socket_info.sin_addr, + host_addr->h_addr, + host_addr->h_length); + + socket_info.sin_addr = *(struct in_addr*)host_addr->h_addr; // FIXME + + if(::connect(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0) + { + ::close(fd); + throw std::runtime_error("connect failed"); + } + + return fd; + } + +bool handshake_complete(const TLS::Session& session) + { + std::cout << "Handshake complete!\n"; + std::cout << "Protocol version " << session.version().to_string() << "\n"; + std::cout << "Ciphersuite " << std::hex << session.ciphersuite().to_string() << "\n"; + std::cout << "Session ID " << hex_encode(session.session_id()) << "\n"; + std::cout << "Session ticket " << hex_encode(session.session_ticket()) << "\n"; - std::cout << "Warning: not checking cert signatures\n"; + return true; + } + +void socket_write(int sockfd, const byte buf[], size_t length) + { + size_t offset = 0; - return true; + while(length) + { + ssize_t sent = ::send(sockfd, (const char*)buf + offset, + length, MSG_NOSIGNAL); + + if(sent == -1) + { + if(errno == EINTR) + sent = 0; + else + throw std::runtime_error("Socket::write: Socket write failed"); } - }; -int main(int argc, char* argv[]) + offset += sent; + length -= sent; + } + } + +bool got_alert = false; + +void process_data(const byte buf[], size_t buf_size, TLS::Alert alert) { - if(argc != 2 && argc != 3) + if(alert.is_valid()) { - printf("Usage: %s host [port]\n", argv[0]); - return 1; + std::cout << "Alert: " << alert.type_string() << "\n"; + got_alert = true; } - try + for(size_t i = 0; i != buf_size; ++i) { - LibraryInitializer botan_init; + std::cout << buf[i]; + } + } - std::string host = argv[1]; - u32bit port = argc == 3 ? Botan::to_u32bit(argv[2]) : 443; +std::string protocol_chooser(const std::vector<std::string>& protocols) + { + for(size_t i = 0; i != protocols.size(); ++i) + std::cout << "Protocol " << i << " = " << protocols[i] << "\n"; + return "http/1.1"; + } - printf("Connecting to %s:%d...\n", host.c_str(), port); +void doit(RandomNumberGenerator& rng, + TLS::Policy& policy, + TLS::Session_Manager& session_manager, + Credentials_Manager& creds, + const std::string& host, + u16bit port) + { + int sockfd = connect_to_host(host, port); - SocketInitializer socket_init; + TLS::Client client(std::tr1::bind(socket_write, sockfd, _1, _2), + process_data, + handshake_complete, + session_manager, + creds, + policy, + rng, + host); - Socket sock(argv[1], port); + fd_set readfds; - AutoSeeded_RNG rng; + while(true) + { + FD_ZERO(&readfds); + FD_SET(sockfd, &readfds); + FD_SET(STDIN_FILENO, &readfds); - Client_TLS_Policy policy; + ::select(sockfd + 1, &readfds, NULL, NULL, NULL); - TLS_Client tls(std::tr1::bind(&Socket::read, std::tr1::ref(sock), _1, _2), - std::tr1::bind(&Socket::write, std::tr1::ref(sock), _1, _2), - policy, rng); + if(client.is_closed()) + break; - printf("Handshake extablished...\n"); + if(FD_ISSET(sockfd, &readfds)) + { + byte buf[64] = { 0 }; -#if 0 - std::string http_command = "GET / HTTP/1.1\r\n" - "Server: " + host + ':' + to_string(port) + "\r\n\r\n"; -#else - std::string http_command = "GET / HTTP/1.0\r\n\r\n"; -#endif + size_t to_read = rand() % sizeof(buf); + if(to_read == 0) + to_read = 1; - tls.write((const Botan::byte*)http_command.c_str(), - http_command.length()); + ssize_t got = read(sockfd, buf, to_read); - size_t total_got = 0; + if(got == 0) + { + std::cout << "EOF on socket\n"; + break; + } + else if(got == -1) + { + std::cout << "Socket error: " << errno << " " << strerror(errno) << "\n"; + continue; + } - while(true) + const size_t needed = client.received_data(buf, got); + //std::cout << "Socket - got " << got << " bytes, need " << needed << "\n"; + } + else if(FD_ISSET(STDIN_FILENO, &readfds)) { - if(tls.is_closed()) - break; + byte buf[1024] = { 0 }; + ssize_t got = read(STDIN_FILENO, buf, sizeof(buf)); - Botan::byte buf[128+1] = { 0 }; - size_t got = tls.read(buf, sizeof(buf)-1); - printf("%s", buf); - fflush(0); + if(got == 0) + { + std::cout << "EOF on stdin\n"; + client.close(); + break; + } + else if(got == -1) + { + std::cout << "Stdin error: " << errno << " " << strerror(errno) << "\n"; + continue; + } - total_got += got; + client.send(buf, got); } + } + + ::close(sockfd); + } + +int main(int argc, char* argv[]) + { + if(argc != 2 && argc != 3) + { + std::cout << "Usage " << argv[0] << " host [port]\n"; + return 1; + } + + try + { + LibraryInitializer botan_init; + AutoSeeded_RNG rng; + TLS::Policy policy; + +#if defined(BOTAN_HAS_TLS_SQLITE_SESSION_MANAGER) + TLS::Session_Manager_SQLite session_manager("my secret passphrase", rng, + "sessions.db"); +#else + TLS::Session_Manager_In_Memory session_manager; +#endif + + Credentials_Manager_Simple creds(rng); + + std::string host = argv[1]; + u32bit port = argc == 3 ? Botan::to_u32bit(argv[2]) : 443; + + //while(true) + doit(rng, policy, session_manager, creds, host, port); - printf("\nRetrieved %d bytes total\n", total_got); } catch(std::exception& e) { - printf("%s\n", e.what()); + std::cout << "Exception: " << e.what() << "\n"; return 1; } return 0; diff --git a/doc/examples/tls_server.cpp b/doc/examples/tls_server.cpp index 153b26d04..a5f2c5d78 100644 --- a/doc/examples/tls_server.cpp +++ b/doc/examples/tls_server.cpp @@ -1,33 +1,128 @@ #include <botan/botan.h> #include <botan/tls_server.h> +#include <botan/hex.h> #include <botan/rsa.h> #include <botan/dsa.h> #include <botan/x509self.h> +#include <botan/secqueue.h> #include "socket.h" +#include "credentials.h" using namespace Botan; +using namespace std::tr1::placeholders; + #include <stdio.h> #include <string> #include <iostream> #include <memory> -class Server_TLS_Policy : public TLS_Policy +bool handshake_complete(const TLS::Session& session) + { + printf("Handshake complete, protocol=%04X ciphersuite=%s compression=%d\n", + session.version(), session.ciphersuite().to_string().c_str(), + session.compression_method()); + + printf("Session id = %s\n", hex_encode(session.session_id()).c_str()); + printf("Master secret = %s\n", hex_encode(session.master_secret()).c_str()); + return true; + } + +class Blocking_TLS_Server { public: - bool check_cert(const std::vector<X509_Certificate>& certs) const + Blocking_TLS_Server(std::tr1::function<void (const byte[], size_t)> output_fn, + std::tr1::function<size_t (byte[], size_t)> input_fn, + std::vector<std::string>& protocols, + TLS::Session_Manager& sessions, + Credentials_Manager& creds, + TLS::Policy& policy, + RandomNumberGenerator& rng) : + input_fn(input_fn), + server( + output_fn, + std::tr1::bind(&Blocking_TLS_Server::reader_fn, std::tr1::ref(*this), _1, _2, _3), + handshake_complete, + sessions, + creds, + policy, + rng), + exit(false) + { + read_loop(); + } + + size_t read(byte buf[], size_t buf_len) + { + size_t got = read_queue.read(buf, buf_len); + + while(!exit && !got) + { + read_loop(5); // header size + got = read_queue.read(buf, buf_len); + } + + return got; + } + + void write(const byte buf[], size_t buf_len) + { + server.send(buf, buf_len); + } + + void close() { server.close(); } + + bool is_active() const { return server.is_active(); } + + TLS::Server& underlying() { return server; } + private: + void read_loop(size_t init_desired = 0) { - for(size_t i = 0; i != certs.size(); ++i) + size_t desired = init_desired; + + byte buf[4096]; + while(!exit && (!server.is_active() || desired)) { - std::cout << certs[i].to_string(); + const size_t asking = std::max(sizeof(buf), std::min(desired, static_cast<size_t>(1))); + + const size_t socket_got = input_fn(&buf[0], asking); + + if(socket_got == 0) // eof? + { + close(); + printf("got eof on socket\n"); + exit = true; + } + + desired = server.received_data(&buf[0], socket_got); } + } - std::cout << "Warning: not checking cert signatures\n"; + void reader_fn(const byte buf[], size_t buf_len, TLS::Alert alert) + { + if(alert.is_valid()) + { + printf("Alert %s\n", alert.type_string().c_str()); + //exit = true; + } - return true; + printf("Got %d bytes: ", (int)buf_len); + for(size_t i = 0; i != buf_len; ++i) + { + if(isprint(buf[i])) + printf("%c", buf[i]); + } + printf("\n"); + + read_queue.write(buf, buf_len); } + + std::tr1::function<size_t (byte[], size_t)> input_fn; + TLS::Server server; + SecureQueue read_queue; + bool exit; }; int main(int argc, char* argv[]) @@ -40,59 +135,74 @@ int main(int argc, char* argv[]) try { LibraryInitializer botan_init; - SocketInitializer socket_init; + //SocketInitializer socket_init; AutoSeeded_RNG rng; - //RSA_PrivateKey key(rng, 1024); - DSA_PrivateKey key(rng, DL_Group("dsa/jce/1024")); + Server_Socket listener(port); - X509_Cert_Options options( - "localhost/US/Syn Ack Labs/Mathematical Munitions Dept"); + TLS::Policy policy; - X509_Certificate cert = - X509::create_self_signed_cert(options, key, "SHA-1", rng); + TLS::Session_Manager_In_Memory sessions; - Server_Socket listener(port); + Credentials_Manager_Simple creds(rng); - Server_TLS_Policy policy; + std::vector<std::string> protocols; + protocols.push_back("spdy/2"); + protocols.push_back("http/1.0"); while(true) { try { printf("Listening for new connection on port %d\n", port); - Socket* sock = listener.accept(); + std::auto_ptr<Socket> sock(listener.accept()); printf("Got new connection\n"); - TLS_Server tls( - std::tr1::bind(&Socket::read, std::tr1::ref(sock), _1, _2), - std::tr1::bind(&Socket::write, std::tr1::ref(sock), _1, _2), - policy, - rng, - cert, - key); + Blocking_TLS_Server tls( + std::tr1::bind(&Socket::write, std::tr1::ref(sock), _1, _2), + std::tr1::bind(&Socket::read, std::tr1::ref(sock), _1, _2, true), + protocols, + sessions, + creds, + policy, + rng); - std::string hostname = tls.requested_hostname(); + const char* msg = "Welcome to the best echo server evar\n"; + tls.write((const Botan::byte*)msg, strlen(msg)); - if(hostname != "") - printf("Client requested host '%s'\n", hostname.c_str()); + std::string line; - printf("Writing some text\n"); + while(tls.is_active()) + { + byte b; + size_t got = tls.read(&b, 1); - char msg[] = "Foo\nBar\nBaz\nQuux\n"; - tls.write((const Botan::byte*)msg, strlen(msg)); + if(got == 0) + break; + + line += (char)b; + if(b == '\n') + { + //std::cout << line; + + tls.write(reinterpret_cast<const byte*>(line.data()), line.size()); - printf("Now trying a read...\n"); + if(line == "quit\n") + { + tls.close(); + break; + } - char buf[1024] = { 0 }; - u32bit got = tls.read((Botan::byte*)buf, sizeof(buf)-1); - printf("%d: '%s'\n", got, buf); + if(line == "reneg\n") + tls.underlying().renegotiate(); - tls.close(); + line.clear(); + } + } } - catch(std::exception& e) { printf("%s\n", e.what()); } + catch(std::exception& e) { printf("Connection problem: %s\n", e.what()); } } } catch(std::exception& e) diff --git a/doc/index.txt b/doc/index.txt index 75e2ca434..33de06f55 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -4,7 +4,7 @@ Welcome Botan is a :doc:`BSD-licensed <license>` crypto library for C++. It provides applications with most any :doc:`cryptographic algorithm -<algos>` you might be looking for, along with :doc:`SSL/TLS <ssl>`, +<algos>` you might be looking for, along with :doc:`SSL/TLS <tls>`, :doc:`X.509 certificates and CRLs <x509>`, a :doc:`pipeline-style message processing system <filters>`, and a wide variety of other features. A third party open source implementation of `SSHv2 diff --git a/doc/ssl.txt b/doc/ssl.txt deleted file mode 100644 index f536b7198..000000000 --- a/doc/ssl.txt +++ /dev/null @@ -1,58 +0,0 @@ - -.. _ssl_api: - -SSL and TLS -======================================== - -.. versionadded:: 1.9.4 - -Botan supports both client and server implementations of the SSL/TLS -protocols, including SSL v3, TLS v1.0, and TLS v1.1. The insecure and -obsolete SSL v2 is not supported. - -The implementation uses ``std::tr1::function``, so it may not have -been compiled into the version you are using; you can test for the -feature macro ``BOTAN_HAS_SSL_TLS`` to check. - -TLS Clients ----------------------------------------- - -.. cpp:class:: TLS_Client - - .. cpp:function:: TLS_Client( \ - 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) - - Creates a TLS client. It will call *input_fn* to read bytes from - the network and call *output_fn* when bytes need to be written to - the network. - - .. cpp:function:: size_t read(byte* buf, size_t buf_len) - - Reads up to *buf_len* bytes from the open connection into *buf*, - returning the number of bytes actually written. - - .. cpp:function:: void write(const byte* buf, size_t buf_len) - - Writes *buf_len* bytes in *buf* to the remote side - - .. cpp:function:: void close() - - Closes the connection - - .. cpp:function:: std::vector<X509_Certificate> peer_cert_chain() - - Returns the certificate chain of the server - -A simple TLS client example: - -.. literalinclude:: examples/tls_client.cpp - - -TLS Servers ----------------------------------------- - -A simple TLS server - -.. literalinclude:: examples/tls_server.cpp diff --git a/doc/tls.txt b/doc/tls.txt new file mode 100644 index 000000000..dd4fb1270 --- /dev/null +++ b/doc/tls.txt @@ -0,0 +1,151 @@ + +.. _tls_api: + +SSL and TLS +======================================== + +.. versionadded:: 1.10.2 + +Botan supports both client and server implementations of the SSL/TLS +protocols, including SSL v3, TLS v1.0, and TLS v1.1 (the insecure and +obsolete SSL v2 protocol is not supported, beyond processing SSL v2 +client hellos which some implementations send for backwards +compatability). + +The implementation uses ``std::tr1::function``, so it may not have +been compiled into the version you are using; you can test for the +feature macro ``BOTAN_HAS_TLS`` to check. + +General TLS Interface +---------------------------------------- + +TLS servers and clients share most of an interface, called +`TLS_Channel`. The primary difference is in terms of how the objects +are constructed. A TLS channel (either client or server object) has +these methods available: + +.. cpp:class:: TLS_Channel + + .. cpp:function size_t received_data(const byte buf[], size_t buf_size) + + This function is used to provide data sent by the counterparty (eg + data that you read off the socket layer). Depending on the current + protocol state and the amount of data provided this may result in one + or more callback functions that were provided to the constructor being + called. + + .. cpp:function void send(const byte buf[], size_t buf_size) + + If the connection has completed the initial handshake process, the + data provided is sent to the counterparty as TLS + traffic. Otherwise, an exception is thrown. + + .. cpp:function:: void close() + + A close notification is sent to the counterparty, and the internal + state is cleared. + + .. cpp:function:: bool is_active() + + Returns true if and only if a handshake has been completed on this + connection. + + .. cpp:function:: bool is_closed() + + Returns true if and only if a close notification has been sent or + received, or if a fatal alert of any kind was received from the + counterparty. + + .. cpp:function:: void renegotiate() + + Initiates a renegotiation. The counterparty is allowed by the + protocol to ignore this request. If a successful renegotiation + occurs, the *handshake_complete* callback will be called again. + + .. cpp:function:: std::vector<X509_Certificate> peer_cert_chain() + + Returns the certificate chain of the server + + + +TLS Clients +---------------------------------------- + +.. cpp:class:: TLS_Client + + .. cpp:function:: TLS_Client( \ + std::tr1::function<void, const byte*, size_t> socket_output_fn, \ + std::tr1::function<void, const byte*, size_t, u16bit> proc_fn, \ + std::tr1::function<bool, const TLS_Session&> handshake_complete, \ + TLS_Session_Manager& session_manager, \ + Credentials_Manager& credendials_manager, \ + const TLS_Policy& policy, \ + RandomNumberGenerator& rng, \ + const std::string& servername = "", \ + std::tr1::function<std::string, std::vector<std::string> > next_protocol) + + Initialize a new TLS client. The constructor will immediately + initiate a new session. The *socket_output_fn* callback will be + called with output that should be sent to the counterparty. + + The *proc_fn* will be called with data sent by the counterparty + after it has been processed. The byte array and size_t represent + the plaintext; the u16bit value provides notification if the + counterparty sent an alert via the TLS alert system. Possible values + of alert data are included in the Alert_Type enum. Particularly + relevant is the CLOSE_NOTIFY value. + + The *handshake_complete* function is called when a handshake + (either initial or renegotiation) is completed. The return value of + the callback specifies if the session should be cached for later + resumption. If the function for some reason desires to prevent the + connection from completing, it should throw an exception + (preferably a TLS_Exception, which can provide more specific alert + information to the counterparty). + + The *session_manager* is an interface for storing TLS sessions, + which allows for session resumption upon reconnecting to a server. + In the absence of a need for persistent sessions, use + `TLS_Session_Manager_In_Memory` which caches connections for the + lifetime of a single process. + + The *credentials_manager* is an interface that will be called to + retrieve any certificates, secret keys, pre-shared keys, or SRP + intformation; see :doc:`credentials <credentials_manager>` for more + information. + + Use *servername* to specify the DNS name of the server you are + attempting to connect to, if you know it. + + The optional *next_protocol* callback is called if the server + indicates it supports the next protocol notification extension. + The callback wlil be called with a list of protocol names that the + server advertises, and the client can select from them or return an + unadvertised protocol. + +A simple TLS client example: + +.. literalinclude:: examples/tls_client.cpp + + +TLS Servers +---------------------------------------- + +.. cpp:class:: TLS_Server + + .. cpp:function:: TLS_Server(std::tr1::function<void, const byte*, size_t> socket_output_fn, \ + std::tr1::function<void, const byte*, size_t, u16bit> proc_fn, \ + std::tr1::function<bool, const TLS_Session&> handshake_complete, \ + TLS_Session_Manager& session_manager, \ + Credentials_Manager& creds, \ + const TLS_Policy& policy, \ + RandomNumberGenerator& rng, \ + const std::vector<std::string>& protocols) + +The first 7 arguments are treated similiarly to `TLS_Client`. The +final (optional) argument, protocols, specifies the protocols the +server is willing to advertise it supports. + +A TLS server that can handle concurrent connections using asio: + +.. literalinclude:: examples/asio_tls_server.cpp diff --git a/src/asn1/ber_dec.cpp b/src/asn1/ber_dec.cpp index b31c7b903..164226e69 100644 --- a/src/asn1/ber_dec.cpp +++ b/src/asn1/ber_dec.cpp @@ -141,7 +141,11 @@ size_t find_eoc(DataSource* ber) void BER_Object::assert_is_a(ASN1_Tag type_tag, ASN1_Tag class_tag) { if(this->type_tag != type_tag || this->class_tag != class_tag) - throw BER_Decoding_Error("Tag mismatch when decoding"); + throw BER_Decoding_Error("Tag mismatch when decoding got " + + to_string(this->type_tag) + "/" + + to_string(this->class_tag) + " expected " + + to_string(type_tag) + "/" + + to_string(class_tag)); } /* @@ -400,6 +404,29 @@ BER_Decoder& BER_Decoder::decode(size_t& out, } /* +* Decode a small BER encoded INTEGER +*/ +u64bit BER_Decoder::decode_constrained_integer(ASN1_Tag type_tag, + ASN1_Tag class_tag, + size_t T_bytes) + { + if(T_bytes > 8) + throw BER_Decoding_Error("Can't decode small integer over 8 bytes"); + + BigInt integer; + decode(integer, type_tag, class_tag); + + if(integer.bits() > 8*T_bytes) + throw BER_Decoding_Error("Decoded integer value larger than expected"); + + u64bit out = 0; + for(size_t i = 0; i != 8; ++i) + out = (out << 8) | integer.byte_at(7-i); + + return out; + } + +/* * Decode a BER encoded INTEGER */ BER_Decoder& BER_Decoder::decode(BigInt& out, diff --git a/src/asn1/ber_dec.h b/src/asn1/ber_dec.h index 5f79d3fc1..f8b8a704a 100644 --- a/src/asn1/ber_dec.h +++ b/src/asn1/ber_dec.h @@ -20,33 +20,61 @@ class BOTAN_DLL BER_Decoder { public: BER_Object get_next_object(); - void push_back(const BER_Object&); + void push_back(const BER_Object& obj); bool more_items() const; BER_Decoder& verify_end(); BER_Decoder& discard_remaining(); - BER_Decoder start_cons(ASN1_Tag, ASN1_Tag = UNIVERSAL); + BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL); BER_Decoder& end_cons(); - BER_Decoder& raw_bytes(MemoryRegion<byte>&); + BER_Decoder& raw_bytes(MemoryRegion<byte>& v); BER_Decoder& decode_null(); - BER_Decoder& decode(bool&); - BER_Decoder& decode(size_t&); - BER_Decoder& decode(class BigInt&); - BER_Decoder& decode(MemoryRegion<byte>&, ASN1_Tag); + BER_Decoder& decode(bool& v); + BER_Decoder& decode(size_t& v); + BER_Decoder& decode(class BigInt& v); + BER_Decoder& decode(MemoryRegion<byte>& v, ASN1_Tag type_tag); - BER_Decoder& decode(bool&, ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); - BER_Decoder& decode(size_t&, ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); - BER_Decoder& decode(class BigInt&, - ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); - BER_Decoder& decode(MemoryRegion<byte>&, ASN1_Tag, - ASN1_Tag, ASN1_Tag = CONTEXT_SPECIFIC); + BER_Decoder& decode(bool& v, + ASN1_Tag type_tag, + ASN1_Tag class_tag = CONTEXT_SPECIFIC); - BER_Decoder& decode(class ASN1_Object&); + BER_Decoder& decode(size_t& v, + ASN1_Tag type_tag, + ASN1_Tag class_tag = CONTEXT_SPECIFIC); - BER_Decoder& decode_octet_string_bigint(class BigInt&); + BER_Decoder& decode(class BigInt& v, + ASN1_Tag type_tag, + ASN1_Tag class_tag = CONTEXT_SPECIFIC); + + BER_Decoder& decode(MemoryRegion<byte>& v, + ASN1_Tag real_type, + ASN1_Tag type_tag, + ASN1_Tag class_tag = CONTEXT_SPECIFIC); + + BER_Decoder& decode(class ASN1_Object& obj); + + BER_Decoder& decode_octet_string_bigint(class BigInt& b); + + u64bit decode_constrained_integer(ASN1_Tag type_tag, + ASN1_Tag class_tag, + size_t T_bytes); + + template<typename T> BER_Decoder& decode_integer_type(T& out) + { + return decode_integer_type<T>(out, INTEGER, UNIVERSAL); + } + + template<typename T> + BER_Decoder& decode_integer_type(T& out, + ASN1_Tag type_tag, + ASN1_Tag class_tag = CONTEXT_SPECIFIC) + { + out = decode_constrained_integer(type_tag, class_tag, sizeof(out)); + return (*this); + } template<typename T> BER_Decoder& decode_optional(T& out, @@ -71,8 +99,9 @@ class BOTAN_DLL BER_Decoder return (*this); } - BER_Decoder& decode_optional_string(MemoryRegion<byte>&, - ASN1_Tag, u16bit); + BER_Decoder& decode_optional_string(MemoryRegion<byte>& out, + ASN1_Tag real_type, + u16bit type_no); BER_Decoder(DataSource&); BER_Decoder(const byte[], size_t); diff --git a/src/build-data/cc/gcc.txt b/src/build-data/cc/gcc.txt index 3de85099c..6173f6271 100644 --- a/src/build-data/cc/gcc.txt +++ b/src/build-data/cc/gcc.txt @@ -13,7 +13,7 @@ add_lib_option -l lang_flags "-D_REENTRANT -Wno-long-long" warning_flags "-W -Wall" -maintainer_warning_flags "-Werror -Weffc++ -Wall -Wextra -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wold-style-cast" +maintainer_warning_flags "-Werror -Wall -Wextra -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wold-style-cast" lib_opt_flags "-O3" check_opt_flags "-O2" diff --git a/src/cert/x509cert/x509cert.cpp b/src/cert/x509cert/x509cert.cpp index 7d9370f2a..7b57f6b1c 100644 --- a/src/cert/x509cert/x509cert.cpp +++ b/src/cert/x509cert/x509cert.cpp @@ -284,6 +284,34 @@ X509_DN X509_Certificate::subject_dn() const return create_dn(subject); } +namespace { + +bool cert_subject_dns_match(const std::string& name, + const std::vector<std::string>& cert_names) + { + for(size_t i = 0; i != cert_names.size(); ++i) + { + // support basic wildcarding? + if(cert_names[i] == name) + return true; + } + + return false; + } + +} + +bool X509_Certificate::matches_dns_name(const std::string& name) const + { + if(cert_subject_dns_match(name, subject_info("DNS"))) + return true; + + if(cert_subject_dns_match(name, subject_info("Name"))) + return true; + + return false; + } + /* * Compare two certificates for equality */ @@ -296,6 +324,24 @@ bool X509_Certificate::operator==(const X509_Certificate& other) const subject == other.subject); } +bool X509_Certificate::operator<(const X509_Certificate& other) const + { + /* If signature values are not equal, sort by lexicographic ordering of that */ + if(sig != other.sig) + { + if(sig < other.sig) + return true; + return false; + } + + /* + * same signatures, highly unlikely case, revert to compare + * of entire contents + */ + + return to_string() < other.to_string(); + } + /* * X.509 Certificate Comparison */ diff --git a/src/cert/x509cert/x509cert.h b/src/cert/x509cert/x509cert.h index 8798ef1c2..26c57e524 100644 --- a/src/cert/x509cert/x509cert.h +++ b/src/cert/x509cert/x509cert.h @@ -146,12 +146,24 @@ class BOTAN_DLL X509_Certificate : public X509_Object std::string to_string() const; /** + * Check if a certain DNS name matches up with the information in + * the cert + */ + bool matches_dns_name(const std::string& name) const; + + /** * Check to certificates for equality. * @return true both certificates are (binary) equal */ bool operator==(const X509_Certificate& other) const; /** + * Impose an arbitrary (but consistent) ordering + * @return true if this is less than other by some unspecified criteria + */ + bool operator<(const X509_Certificate& other) const; + + /** * Create a certificate from a data source providing the DER or * PEM encoded certificate. * @param source the data source diff --git a/src/codec/pem/pem.cpp b/src/codec/pem/pem.cpp index 005ec7310..52a22d8ef 100644 --- a/src/codec/pem/pem.cpp +++ b/src/codec/pem/pem.cpp @@ -110,6 +110,19 @@ SecureVector<byte> decode(DataSource& source, std::string& label) return base64.read_all(); } +SecureVector<byte> decode_check_label(const std::string& pem, + const std::string& label_want) + { + DataSource_Memory src(pem); + return decode_check_label(src, label_want); + } + +SecureVector<byte> decode(const std::string& pem, std::string& label) + { + DataSource_Memory src(pem); + return decode(src, label); + } + /* * Search for a PEM signature */ diff --git a/src/codec/pem/pem.h b/src/codec/pem/pem.h index d15bfe978..10267f029 100644 --- a/src/codec/pem/pem.h +++ b/src/codec/pem/pem.h @@ -14,18 +14,56 @@ namespace Botan { namespace PEM_Code { -/* -* PEM Encoding/Decoding +/** +* Encode some binary data in PEM format +*/ +BOTAN_DLL std::string encode(const byte der[], + size_t der_len, + const std::string& label, + size_t line_width = 64); + +/** +* Encode some binary data in PEM format */ -BOTAN_DLL std::string encode(const byte[], size_t, - const std::string&, size_t = 64); -BOTAN_DLL std::string encode(const MemoryRegion<byte>&, - const std::string&, size_t = 64); +BOTAN_DLL std::string encode(const MemoryRegion<byte>& der, + const std::string& label, + size_t line_width = 64); -BOTAN_DLL SecureVector<byte> decode(DataSource&, std::string&); -BOTAN_DLL SecureVector<byte> decode_check_label(DataSource&, - const std::string&); -BOTAN_DLL bool matches(DataSource&, const std::string& = "", +/** +* Decode PEM data +* @param label is set to the PEM label found for later inspection +*/ +BOTAN_DLL SecureVector<byte> decode(DataSource& pem, + std::string& label); + +/** +* Decode PEM data +* @param label is set to the PEM label found for later inspection +*/ +BOTAN_DLL SecureVector<byte> decode(const std::string& pem, + std::string& label); + +/** +* Decode PEM data +* @param label is what we expect the label to be +*/ +BOTAN_DLL SecureVector<byte> decode_check_label( + DataSource& pem, + const std::string& label); + +/** +* Decode PEM data +* @param label is what we expect the label to be +*/ +BOTAN_DLL SecureVector<byte> decode_check_label( + const std::string& pem, + const std::string& label); + +/** +* Heuristic test for PEM data. +*/ +BOTAN_DLL bool matches(DataSource& source, + const std::string& extra = "", size_t search_range = 4096); } diff --git a/src/credentials/credentials_manager.cpp b/src/credentials/credentials_manager.cpp new file mode 100644 index 000000000..ef5d44819 --- /dev/null +++ b/src/credentials/credentials_manager.cpp @@ -0,0 +1,120 @@ +/* +* Credentials Manager +* (C) 2011,2012 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/credentials_manager.h> +#include <botan/x509stor.h> + +namespace Botan { + +std::string Credentials_Manager::psk_identity_hint(const std::string&, + const std::string&) + { + return ""; + } + +std::string Credentials_Manager::psk_identity(const std::string&, + const std::string&, + const std::string&) + { + return ""; + } + +SymmetricKey Credentials_Manager::psk(const std::string&, + const std::string&, + const std::string& identity) + { + throw Internal_Error("No PSK set for identity " + identity); + } + +std::string Credentials_Manager::srp_identifier(const std::string&, + const std::string&) + { + return ""; + } + +std::string Credentials_Manager::srp_password(const std::string&, + const std::string&, + const std::string&) + { + return ""; + } + +bool Credentials_Manager::srp_verifier(const std::string&, + const std::string&, + const std::string&, + BigInt&, + BigInt&, + BigInt&, + MemoryRegion<byte>&, + bool) + { + return false; + } + +std::vector<X509_Certificate> Credentials_Manager::cert_chain( + const std::vector<std::string>&, + const std::string&, + const std::string&) + { + return std::vector<X509_Certificate>(); + } + +std::vector<X509_Certificate> Credentials_Manager::cert_chain_single_type( + const std::string& cert_key_type, + const std::string& type, + const std::string& context) + { + std::vector<std::string> cert_types; + cert_types.push_back(cert_key_type); + return cert_chain(cert_types, type, context); + } + +Private_Key* Credentials_Manager::private_key_for(const X509_Certificate&, + const std::string&, + const std::string&) + { + return 0; + } + +std::vector<X509_Certificate> +Credentials_Manager::trusted_certificate_authorities( + const std::string&, + const std::string&) + { + return std::vector<X509_Certificate>(); + } + +void Credentials_Manager::verify_certificate_chain( + const std::string& type, + const std::string& purported_hostname, + const std::vector<X509_Certificate>& cert_chain) + { + if(cert_chain.empty()) + throw std::invalid_argument("Certificate chain was empty"); + + if(!cert_chain[0].matches_dns_name(purported_hostname)) + throw std::runtime_error("Certificate did not match hostname"); + + std::vector<X509_Certificate> CAs = trusted_certificate_authorities(type, purported_hostname); + + X509_Store store; + + for(size_t i = 0; i != CAs.size(); ++i) + store.add_cert(CAs[i], true); + for(size_t i = 0; i != cert_chain.size(); ++i) + store.add_cert(cert_chain[i]); + + X509_Code result = store.validate_cert(cert_chain[0], X509_Store::TLS_SERVER); + + if(CAs.empty() && result == CERT_ISSUER_NOT_FOUND) + return; + + if(result != VERIFIED) + throw std::runtime_error("Certificate did not validate"); + } + +} diff --git a/src/credentials/credentials_manager.h b/src/credentials/credentials_manager.h new file mode 100644 index 000000000..e1b4268e3 --- /dev/null +++ b/src/credentials/credentials_manager.h @@ -0,0 +1,145 @@ +/* +* Credentials Manager +* (C) 2011,2012 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CREDENTIALS_MANAGER_H__ +#define BOTAN_CREDENTIALS_MANAGER_H__ + +#include <botan/x509cert.h> +#include <botan/symkey.h> +#include <string> + +namespace Botan { + +class BigInt; + +/** +* Interface for a credentials manager. +* +* A type is a fairly static value that represents the general nature +* of the transaction occuring. Currently used values are "tls-client" +* and "tls-server". Context represents a hostname, email address, +* username, or other identifier. +*/ +class BOTAN_DLL Credentials_Manager + { + public: + virtual ~Credentials_Manager() {} + + virtual std::string psk_identity_hint(const std::string& type, + const std::string& context); + + /** + * @param identity_hint was passed by the server (but may be empty) + * @return the PSK identity we want to use + */ + virtual std::string psk_identity(const std::string& type, + const std::string& context, + const std::string& identity_hint); + + /** + * @return the PSK used for identity, or throw an exception if no + * key exists + */ + virtual SymmetricKey psk(const std::string& type, + const std::string& context, + const std::string& identity); + + /** + * @return identifier for client-side SRP auth, if available + for this type/context. Should return empty string + if password auth not desired/available. + */ + virtual std::string srp_identifier(const std::string& type, + const std::string& context); + + /** + * @param identifier specifies what identifier we want the + * password for. This will be a value previously returned + * by srp_identifier. + * @return password for client-side SRP auth, if available + for this identifier/type/context. + */ + virtual std::string srp_password(const std::string& type, + const std::string& context, + const std::string& identifier); + + /** + * Retrieve SRP verifier parameters + */ + virtual bool srp_verifier(const std::string& type, + const std::string& context, + const std::string& identifier, + BigInt& group_prime, + BigInt& group_generator, + BigInt& verifier, + MemoryRegion<byte>& salt, + bool generate_fake_on_unknown); + + /** + * Return a cert chain we can use, ordered from leaf to root. + * Assumed that we can get the private key of the leaf with + * private_key_for + * + * @param cert_key_type is a set string representing the allowed + * key type ("RSA", "DSA", "ECDSA", etc) or empty if no + * preference. + */ + virtual std::vector<X509_Certificate> cert_chain( + const std::vector<std::string>& cert_key_types, + const std::string& type, + const std::string& context); + + /** + * Return a cert chain we can use, ordered from leaf to root. + * Assumed that we can get the private key of the leaf with + * private_key_for + * + * @param cert_key_type is a set string representing the allowed + * key type ("RSA", "DSA", "ECDSA", etc) or empty if no + * preference. + */ + std::vector<X509_Certificate> cert_chain_single_type( + const std::string& cert_key_type, + const std::string& type, + const std::string& context); + + /** + * Return a list of the certificates of CAs that we trust in this + * type/context. + */ + virtual std::vector<X509_Certificate> trusted_certificate_authorities( + const std::string& type, + const std::string& context); + + /** + * Check the certificate chain is valid up to a trusted root, and + * optionally (if hostname != "") that the hostname given is + * consistent with the leaf certificate. + * + * This function should throw an exception derived from + * std::exception with an informative what() result if the + * certificate chain cannot be verified. + */ + virtual void verify_certificate_chain( + const std::string& type, + const std::string& hostname, + const std::vector<X509_Certificate>& cert_chain); + + /** + * @return private key associated with this certificate if we should + * use it with this context. cert was returned by cert_chain + * @note this object should retain ownership of the returned key; + * it should not be deleted by the caller. + */ + virtual Private_Key* private_key_for(const X509_Certificate& cert, + const std::string& type, + const std::string& context); + }; + +} + +#endif diff --git a/src/credentials/info.txt b/src/credentials/info.txt new file mode 100644 index 000000000..f6dcdd64d --- /dev/null +++ b/src/credentials/info.txt @@ -0,0 +1 @@ +define CREDENTIALS_MANAGER diff --git a/src/filters/secqueue.cpp b/src/filters/secqueue.cpp index 8dc95ea99..f65aff2a9 100644 --- a/src/filters/secqueue.cpp +++ b/src/filters/secqueue.cpp @@ -208,4 +208,9 @@ bool SecureQueue::end_of_data() const return (size() == 0); } +bool SecureQueue::empty() const + { + return (size() == 0); + } + } diff --git a/src/filters/secqueue.h b/src/filters/secqueue.h index 632ae857d..e773b69bc 100644 --- a/src/filters/secqueue.h +++ b/src/filters/secqueue.h @@ -28,6 +28,8 @@ class BOTAN_DLL SecureQueue : public Fanout_Filter, public DataSource bool end_of_data() const; + bool empty() const; + /** * @return number of bytes available in the queue */ diff --git a/src/filters/zlib/zlib.cpp b/src/filters/zlib/zlib.cpp index 0f88b5558..517660e3b 100644 --- a/src/filters/zlib/zlib.cpp +++ b/src/filters/zlib/zlib.cpp @@ -91,10 +91,12 @@ class Zlib_Stream /* * Zlib_Compression Constructor */ -Zlib_Compression::Zlib_Compression(size_t l) : - level((l >= 9) ? 9 : l), buffer(DEFAULT_BUFFERSIZE) +Zlib_Compression::Zlib_Compression(size_t l, bool raw_deflate) : + level((l >= 9) ? 9 : l), + raw_deflate(raw_deflate), + buffer(DEFAULT_BUFFERSIZE), + zlib(0) { - zlib = 0; } /* @@ -104,7 +106,17 @@ void Zlib_Compression::start_msg() { clear(); zlib = new Zlib_Stream; - if(deflateInit(&(zlib->stream), level) != Z_OK) + + int res = deflateInit2(&(zlib->stream), + level, + Z_DEFLATED, + (raw_deflate ? -15 : 15), + 8, + Z_DEFAULT_STRATEGY); + + if(res == Z_STREAM_ERROR) + throw Invalid_Argument("Bad setting in deflateInit2"); + else if(res != Z_OK) throw Memory_Exhaustion(); } @@ -185,10 +197,12 @@ void Zlib_Compression::clear() /* * Zlib_Decompression Constructor */ -Zlib_Decompression::Zlib_Decompression() : buffer(DEFAULT_BUFFERSIZE) +Zlib_Decompression::Zlib_Decompression(bool raw_deflate) : + raw_deflate(raw_deflate), + buffer(DEFAULT_BUFFERSIZE), + zlib(0), + no_writes(true) { - zlib = 0; - no_writes = true; } /* @@ -198,7 +212,8 @@ void Zlib_Decompression::start_msg() { clear(); zlib = new Zlib_Stream; - if(inflateInit(&(zlib->stream)) != Z_OK) + + if(inflateInit2(&(zlib->stream), (raw_deflate ? -15 : 15)) != Z_OK) throw Memory_Exhaustion(); } diff --git a/src/filters/zlib/zlib.h b/src/filters/zlib/zlib.h index 77ec5eecc..60117f2bc 100644 --- a/src/filters/zlib/zlib.h +++ b/src/filters/zlib/zlib.h @@ -31,15 +31,20 @@ class BOTAN_DLL Zlib_Compression : public Filter void flush(); /** - @param level how much effort to use on compressing (0 to 9); - higher levels are slower but tend to give better compression + * @param level how much effort to use on compressing (0 to 9); + * higher levels are slower but tend to give better + * compression + * @param raw_deflate if true no zlib header/trailer will be used */ - Zlib_Compression(size_t level = 6); + Zlib_Compression(size_t level = 6, + bool raw_deflate = false); ~Zlib_Compression() { clear(); } private: void clear(); const size_t level; + const bool raw_deflate; + SecureVector<byte> buffer; class Zlib_Stream* zlib; }; @@ -56,10 +61,13 @@ class BOTAN_DLL Zlib_Decompression : public Filter void start_msg(); void end_msg(); - Zlib_Decompression(); + Zlib_Decompression(bool raw_deflate = false); ~Zlib_Decompression() { clear(); } private: void clear(); + + const bool raw_deflate; + SecureVector<byte> buffer; class Zlib_Stream* zlib; bool no_writes; diff --git a/src/kdf/prf_tls/info.txt b/src/kdf/prf_tls/info.txt index 9531a6a83..113c92251 100644 --- a/src/kdf/prf_tls/info.txt +++ b/src/kdf/prf_tls/info.txt @@ -1,4 +1,5 @@ define TLS_V10_PRF +define TLS_V12_PRF <requires> hmac diff --git a/src/kdf/prf_tls/prf_tls.cpp b/src/kdf/prf_tls/prf_tls.cpp index 2b57cdd25..3790650b0 100644 --- a/src/kdf/prf_tls/prf_tls.cpp +++ b/src/kdf/prf_tls/prf_tls.cpp @@ -23,7 +23,15 @@ void P_hash(MemoryRegion<byte>& output, const byte secret[], size_t secret_len, const byte seed[], size_t seed_len) { - mac->set_key(secret, secret_len); + try + { + mac->set_key(secret, secret_len); + } + catch(Invalid_Key_Length) + { + throw Internal_Error("The premaster secret of " + to_string(secret_len) + + " bytes is too long for the PRF"); + } SecureVector<byte> A(seed, seed_len); diff --git a/src/libstate/get_enc.cpp b/src/libstate/get_enc.cpp index 6a87268e8..b7c9a7146 100644 --- a/src/libstate/get_enc.cpp +++ b/src/libstate/get_enc.cpp @@ -96,7 +96,11 @@ EMSA* get_emsa(const std::string& algo_spec) #if defined(BOTAN_HAS_EMSA1) if(request.algo_name() == "EMSA1" && request.arg_count() == 1) + { + if(request.arg(0) == "Raw") + return new EMSA_Raw; return new EMSA1(af.make_hash_function(request.arg(0))); + } #endif #if defined(BOTAN_HAS_EMSA1_BSI) @@ -195,14 +199,24 @@ KDF* get_kdf(const std::string& algo_spec) return new X942_PRF(request.arg(0)); // OID #endif +#if defined(BOTAN_HAS_SSL_V3_PRF) + if(request.algo_name() == "SSL3-PRF" && request.arg_count() == 0) + return new SSL3_PRF; +#endif + #if defined(BOTAN_HAS_TLS_V10_PRF) if(request.algo_name() == "TLS-PRF" && request.arg_count() == 0) return new TLS_PRF; #endif -#if defined(BOTAN_HAS_SSL_V3_PRF) - if(request.algo_name() == "SSL3-PRF" && request.arg_count() == 0) - return new SSL3_PRF; +#if defined(BOTAN_HAS_TLS_V10_PRF) + if(request.algo_name() == "TLS-PRF" && request.arg_count() == 0) + return new TLS_PRF; +#endif + +#if defined(BOTAN_HAS_TLS_V12_PRF) + if(request.algo_name() == "TLS-12-PRF" && request.arg_count() == 1) + return new TLS_12_PRF(af.make_mac("HMAC(" + request.arg(0) + ")")); #endif throw Algorithm_Not_Found(algo_spec); diff --git a/src/mac/hmac/hmac.h b/src/mac/hmac/hmac.h index b76a058f4..9de1bc7b5 100644 --- a/src/mac/hmac/hmac.h +++ b/src/mac/hmac/hmac.h @@ -27,7 +27,7 @@ class BOTAN_DLL HMAC : public MessageAuthenticationCode Key_Length_Specification key_spec() const { - return Key_Length_Specification(0, 2*hash->hash_block_size()); + return Key_Length_Specification(0, 512); } /** diff --git a/src/ssl/c_kex.cpp b/src/ssl/c_kex.cpp deleted file mode 100644 index 0f20b819c..000000000 --- a/src/ssl/c_kex.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* -* 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/ssl/cert_req.cpp b/src/ssl/cert_req.cpp deleted file mode 100644 index b8b2624bf..000000000 --- a/src/ssl/cert_req.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* -* 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/ssl/cert_ver.cpp b/src/ssl/cert_ver.cpp deleted file mode 100644 index 3220a8c9e..000000000 --- a/src/ssl/cert_ver.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* -* 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/ssl/finished.cpp b/src/ssl/finished.cpp deleted file mode 100644 index d76fbd884..000000000 --- a/src/ssl/finished.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* -* 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/ssl/hello.cpp b/src/ssl/hello.cpp deleted file mode 100644 index ae0d9607b..000000000 --- a/src/ssl/hello.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* -* 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/ssl/info.txt b/src/ssl/info.txt deleted file mode 100644 index 169b76115..000000000 --- a/src/ssl/info.txt +++ /dev/null @@ -1,71 +0,0 @@ -define SSL_TLS - -load_on request - -<comment> -A new TLS API is being developed. This version has numerous -performance and usability issues and will not be supported in the -future. Only use it if you need it for compatability with code written -against previous versions. -</comment> - -uses_tr1 yes - -<header:public> -tls_client.h -tls_connection.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/ssl/rec_read.cpp b/src/ssl/rec_read.cpp deleted file mode 100644 index 7e295f8a4..000000000 --- a/src/ssl/rec_read.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* -* 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 - { - bool padding_good = true; - - for(size_t i = 0; i != pad_size; ++i) - if(plaintext[plaintext.size()-i-1] != pad_value) - padding_good = false; - - if(!padding_good) - 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/ssl/rec_wri.cpp b/src/ssl/rec_wri.cpp deleted file mode 100644 index 59dead3cd..000000000 --- a/src/ssl/rec_wri.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* -* 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) - { - 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/ssl/s_kex.cpp b/src/ssl/s_kex.cpp deleted file mode 100644 index 1e7de31d0..000000000 --- a/src/ssl/s_kex.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* -* 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/ssl/tls_alerts.h b/src/ssl/tls_alerts.h deleted file mode 100644 index 241599aa8..000000000 --- a/src/ssl/tls_alerts.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -* 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/ssl/tls_client.cpp b/src/ssl/tls_client.cpp deleted file mode 100644 index a136752fd..000000000 --- a/src/ssl/tls_client.cpp +++ /dev/null @@ -1,586 +0,0 @@ -/* -* TLS Client -* (C) 2004-2010 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<size_t (byte[], size_t)> input_fn, - std::tr1::function<void (const byte[], size_t)> output_fn, - const TLS_Policy& policy, - RandomNumberGenerator& rng) : - input_fn(input_fn), - policy(policy), - rng(rng), - writer(output_fn) - { - initialize(); - } - -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; - } - -/** -* Initialize a TLS client connection -*/ -void TLS_Client::initialize() - { - std::string error_str; - Alert_Type error_type = NO_ALERT_TYPE; - - try { - state = 0; - active = false; - writer.set_version(policy.pref_version()); - do_handshake(); - } - catch(TLS_Exception& e) - { - error_str = e.what(); - error_type = e.type(); - } - catch(std::exception& e) - { - error_str = e.what(); - error_type = HANDSHAKE_FAILURE; - } - - if(error_type != NO_ALERT_TYPE) - { - if(active) - { - active = false; - reader.reset(); - - writer.alert(FATAL, error_type); - writer.reset(); - } - - if(state) - { - delete state; - state = 0; - } - - throw Stream_IO_Error("TLS_Client: Handshake failed: " + error_str); - } - } - -/** -* Return the peer's certificate chain -*/ -std::vector<X509_Certificate> TLS_Client::peer_cert_chain() const - { - return peer_certs; - } - -/** -* Write to a TLS connection -*/ -void TLS_Client::write(const byte buf[], size_t length) - { - if(!active) - throw TLS_Exception(INTERNAL_ERROR, - "TLS_Client::write called while closed"); - - writer.send(APPLICATION_DATA, buf, length); - } - -/** -* Read from a TLS connection -*/ -size_t TLS_Client::read(byte out[], size_t length) - { - if(!active) - return 0; - - 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; - } - -/** -* Close a TLS connection -*/ -void TLS_Client::close() - { - close(WARNING, CLOSE_NOTIFY); - } - -/** -* Check connection status -*/ -bool TLS_Client::is_closed() const - { - if(!active) - return true; - return false; - } - -/** -* Close a TLS connection -*/ -void TLS_Client::close(Alert_Level level, Alert_Type alert_code) - { - if(active) - { - try { - writer.alert(level, alert_code); - writer.flush(); - } - catch(...) {} - - active = false; - } - } - -/** -* Iterate the TLS state machine -*/ -void TLS_Client::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; - if(state) - { - delete state; - state = 0; - } - } - } - else - throw Unexpected_Message("Unknown message type received"); - } - -/** -* 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"); - } - -/** -* Perform a client-side TLS handshake -*/ -void TLS_Client::do_handshake() - { - state = new Handshake_State; - - state->client_hello = new Client_Hello(rng, writer, policy, state->hash); - - while(true) - { - if(active && !state) - break; - if(!active && !state) - throw TLS_Exception(HANDSHAKE_FAILURE, "TLS_Client: Handshake failed (do_handshake)"); - - state_machine(); - } - } - -} diff --git a/src/ssl/tls_client.h b/src/ssl/tls_client.h deleted file mode 100644 index 7d2ce9cda..000000000 --- a/src/ssl/tls_client.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -* TLS Client -* (C) 2004-2010 Jack Lloyd -* -* Released under the terms of the Botan license -*/ - -#ifndef BOTAN_TLS_CLIENT_H__ -#define BOTAN_TLS_CLIENT_H__ - -#include <botan/tls_connection.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 TLS_Connection - { - public: - size_t read(byte buf[], size_t buf_len); - void write(const byte buf[], size_t buf_len); - - void close(); - bool is_closed() const; - - std::vector<X509_Certificate> peer_cert_chain() const; - - void add_client_cert(const X509_Certificate& cert, - Private_Key* cert_key); - - TLS_Client(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); - - ~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>&); - - std::tr1::function<size_t (byte[], size_t)> input_fn; - - const TLS_Policy& policy; - RandomNumberGenerator& rng; - - Record_Writer writer; - Record_Reader reader; - - std::vector<X509_Certificate> peer_certs; - std::vector<std::pair<X509_Certificate, Private_Key*> > certs; - - class Handshake_State* state; - SecureVector<byte> session_id; - SecureQueue read_buf; - bool active; - }; - -} - -#endif diff --git a/src/ssl/tls_connection.h b/src/ssl/tls_connection.h deleted file mode 100644 index bbefa2114..000000000 --- a/src/ssl/tls_connection.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -* TLS Connection -* (C) 2004-2006 Jack Lloyd -* -* Released under the terms of the Botan license -*/ - -#ifndef BOTAN_TLS_CONNECTION_H__ -#define BOTAN_TLS_CONNECTION_H__ - -#include <botan/x509cert.h> -#include <vector> - -namespace Botan { - -/** -* TLS Connection -*/ -class BOTAN_DLL TLS_Connection - { - public: - virtual size_t read(byte[], size_t) = 0; - virtual void write(const byte[], size_t) = 0; - size_t read(byte& in) { return read(&in, 1); } - void write(byte out) { write(&out, 1); } - - virtual std::vector<X509_Certificate> peer_cert_chain() const = 0; - - virtual void close() = 0; - - virtual ~TLS_Connection() {} - }; - -} - -#endif diff --git a/src/ssl/tls_handshake_hash.cpp b/src/ssl/tls_handshake_hash.cpp deleted file mode 100644 index 7c1e2e385..000000000 --- a/src/ssl/tls_handshake_hash.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* -* 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/ssl/tls_magic.h b/src/ssl/tls_magic.h deleted file mode 100644 index 00898738e..000000000 --- a/src/ssl/tls_magic.h +++ /dev/null @@ -1,192 +0,0 @@ -/* -* 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/ssl/tls_messages.h b/src/ssl/tls_messages.h deleted file mode 100644 index e7eaa56e1..000000000 --- a/src/ssl/tls_messages.h +++ /dev/null @@ -1,297 +0,0 @@ -/* -* 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/ssl/tls_policy.cpp b/src/ssl/tls_policy.cpp deleted file mode 100644 index b73ff7850..000000000 --- a/src/ssl/tls_policy.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* -* 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/ssl/tls_policy.h b/src/ssl/tls_policy.h deleted file mode 100644 index 461164d2f..000000000 --- a/src/ssl/tls_policy.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -* 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/ssl/tls_record.h b/src/ssl/tls_record.h deleted file mode 100644 index 09fd921c6..000000000 --- a/src/ssl/tls_record.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -* 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(); - - 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/ssl/tls_server.cpp b/src/ssl/tls_server.cpp deleted file mode 100644 index 8964be3d7..000000000 --- a/src/ssl/tls_server.cpp +++ /dev/null @@ -1,494 +0,0 @@ -/* -* 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/ssl/tls_server.h b/src/ssl/tls_server.h deleted file mode 100644 index a6b0f9cb4..000000000 --- a/src/ssl/tls_server.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -* TLS Server -* (C) 2004-2010 Jack Lloyd -* -* Released under the terms of the Botan license -*/ - -#ifndef BOTAN_TLS_SERVER_H__ -#define BOTAN_TLS_SERVER_H__ - -#include <botan/tls_connection.h> -#include <botan/tls_record.h> -#include <botan/tls_policy.h> -#include <vector> - -namespace Botan { - -/** -* TLS Server -*/ -class BOTAN_DLL TLS_Server : public TLS_Connection - { - 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/ssl/tls_session_key.cpp b/src/ssl/tls_session_key.cpp deleted file mode 100644 index 7c75d1758..000000000 --- a/src/ssl/tls_session_key.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* -* 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/ssl/tls_session_key.h b/src/ssl/tls_session_key.h deleted file mode 100644 index 51397984b..000000000 --- a/src/ssl/tls_session_key.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -* 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/ssl/tls_state.cpp b/src/ssl/tls_state.cpp deleted file mode 100644 index 6aaf5e201..000000000 --- a/src/ssl/tls_state.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -* 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/ssl/tls_state.h b/src/ssl/tls_state.h deleted file mode 100644 index e2728198f..000000000 --- a/src/ssl/tls_state.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -* 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/ssl/tls_suites.cpp b/src/ssl/tls_suites.cpp deleted file mode 100644 index 07cbec608..000000000 --- a/src/ssl/tls_suites.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/* -* 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/ssl/tls_suites.h b/src/ssl/tls_suites.h deleted file mode 100644 index 8d6db0e8b..000000000 --- a/src/ssl/tls_suites.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* 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 diff --git a/src/tls/c_hello.cpp b/src/tls/c_hello.cpp new file mode 100644 index 000000000..d3fff8b00 --- /dev/null +++ b/src/tls/c_hello.cpp @@ -0,0 +1,382 @@ +/* +* TLS Hello Request and Client Hello Messages +* (C) 2004-2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_session_key.h> +#include <botan/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/internal/stl_util.h> +#include <botan/time.h> + +namespace Botan { + +namespace TLS { + +enum { + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF +}; + +MemoryVector<byte> make_hello_random(RandomNumberGenerator& rng) + { + MemoryVector<byte> buf(32); + const u32bit time32 = system_time(); + store_be(time32, buf); + rng.randomize(&buf[4], buf.size() - 4); + return buf; + } + +/* +* Create a new Hello Request message +*/ +Hello_Request::Hello_Request(Record_Writer& writer) + { + writer.send(*this); + } + +/* +* Deserialize a Hello Request message +*/ +Hello_Request::Hello_Request(const MemoryRegion<byte>& buf) + { + if(buf.size()) + throw Decoding_Error("Bad Hello_Request, has non-zero size"); + } + +/* +* Serialize a Hello Request message +*/ +MemoryVector<byte> Hello_Request::serialize() const + { + return MemoryVector<byte>(); + } + +/* +* Create a new Client Hello message +*/ +Client_Hello::Client_Hello(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + RandomNumberGenerator& rng, + const MemoryRegion<byte>& reneg_info, + bool next_protocol, + const std::string& hostname, + const std::string& srp_identifier) : + m_version(policy.pref_version()), + m_random(make_hello_random(rng)), + m_suites(ciphersuite_list(policy, (srp_identifier != ""))), + m_comp_methods(policy.compression()), + m_hostname(hostname), + m_srp_identifier(srp_identifier), + m_next_protocol(next_protocol), + m_fragment_size(0), + m_secure_renegotiation(true), + m_renegotiation_info(reneg_info), + m_supported_curves(policy.allowed_ecc_curves()), + m_supports_session_ticket(true) + { + std::vector<std::string> hashes = policy.allowed_hashes(); + std::vector<std::string> sigs = policy.allowed_signature_methods(); + + for(size_t i = 0; i != hashes.size(); ++i) + for(size_t j = 0; j != sigs.size(); ++j) + m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j])); + + hash.update(writer.send(*this)); + } + +/* +* Create a new Client Hello message (session resumption case) +*/ +Client_Hello::Client_Hello(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + RandomNumberGenerator& rng, + const Session& session, + bool next_protocol) : + m_version(session.version()), + m_session_id(session.session_id()), + m_random(make_hello_random(rng)), + m_suites(ciphersuite_list(policy, (session.srp_identifier() != ""))), + m_comp_methods(policy.compression()), + m_hostname(session.sni_hostname()), + m_srp_identifier(session.srp_identifier()), + m_next_protocol(next_protocol), + m_fragment_size(session.fragment_size()), + m_secure_renegotiation(session.secure_renegotiation()), + m_supported_curves(policy.allowed_ecc_curves()), + m_supports_session_ticket(true), + m_session_ticket(session.session_ticket()) + { + if(!value_exists(m_suites, session.ciphersuite_code())) + m_suites.push_back(session.ciphersuite_code()); + + if(!value_exists(m_comp_methods, session.compression_method())) + m_comp_methods.push_back(session.compression_method()); + + std::vector<std::string> hashes = policy.allowed_hashes(); + std::vector<std::string> sigs = policy.allowed_signature_methods(); + + for(size_t i = 0; i != hashes.size(); ++i) + for(size_t j = 0; j != sigs.size(); ++j) + m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j])); + + hash.update(writer.send(*this)); + } + +/* +* Read a counterparty client hello +*/ +Client_Hello::Client_Hello(const MemoryRegion<byte>& buf, Handshake_Type type) + { + m_next_protocol = false; + m_secure_renegotiation = false; + m_supports_session_ticket = false; + m_fragment_size = 0; + + if(type == CLIENT_HELLO) + deserialize(buf); + else + deserialize_sslv2(buf); + } + +/* +* Serialize a Client Hello message +*/ +MemoryVector<byte> Client_Hello::serialize() const + { + MemoryVector<byte> buf; + + buf.push_back(m_version.major_version()); + buf.push_back(m_version.minor_version()); + buf += m_random; + + append_tls_length_value(buf, m_session_id, 1); + append_tls_length_value(buf, m_suites, 2); + append_tls_length_value(buf, m_comp_methods, 1); + + /* + * May not want to send extensions at all in some cases. + * If so, should include SCSV value (if reneg info is empty, if + * not we are renegotiating with a modern server and should only + * send that extension. + */ + + Extensions extensions; + + // Initial handshake + if(m_renegotiation_info.empty()) + { + extensions.add(new Renegotation_Extension(m_renegotiation_info)); + extensions.add(new Server_Name_Indicator(m_hostname)); + extensions.add(new SRP_Identifier(m_srp_identifier)); + extensions.add(new Supported_Elliptic_Curves(m_supported_curves)); + + if(m_version >= Protocol_Version::TLS_V12) + extensions.add(new Signature_Algorithms(m_supported_algos)); + + if(m_next_protocol) + extensions.add(new Next_Protocol_Notification()); + + extensions.add(new Session_Ticket(m_session_ticket)); + } + else + { + // renegotiation + extensions.add(new Renegotation_Extension(m_renegotiation_info)); + extensions.add(new Session_Ticket(m_session_ticket)); + } + + buf += extensions.serialize(); + + 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 m_session_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 + m_session_id_len + cipher_spec_len + challenge_len); + + if(buf.size() != expected_size) + throw Decoding_Error("Client_Hello: SSLv2 hello corrupted"); + + if(m_session_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; + + m_suites.push_back(make_u16bit(buf[i+1], buf[i+2])); + } + + m_version = Protocol_Version(buf[1], buf[2]); + + m_random.resize(challenge_len); + copy_mem(&m_random[0], &buf[9+cipher_spec_len+m_session_id_len], challenge_len); + + m_secure_renegotiation = + value_exists(m_suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV)); + } + +/* +* 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); + + const byte major_version = reader.get_byte(); + const byte minor_version = reader.get_byte(); + + m_version = Protocol_Version(major_version, minor_version); + + m_random = reader.get_fixed<byte>(32); + + m_session_id = reader.get_range<byte>(1, 0, 32); + + m_suites = reader.get_range_vector<u16bit>(2, 1, 32767); + + m_comp_methods = reader.get_range_vector<byte>(1, 1, 255); + + Extensions extensions(reader); + + if(Server_Name_Indicator* sni = extensions.get<Server_Name_Indicator>()) + { + m_hostname = sni->host_name(); + } + + if(SRP_Identifier* srp = extensions.get<SRP_Identifier>()) + { + m_srp_identifier = srp->identifier(); + } + + if(Next_Protocol_Notification* npn = extensions.get<Next_Protocol_Notification>()) + { + if(!npn->protocols().empty()) + throw Decoding_Error("Client sent non-empty NPN extension"); + + m_next_protocol = true; + } + + if(Maximum_Fragment_Length* frag = extensions.get<Maximum_Fragment_Length>()) + { + m_fragment_size = frag->fragment_size(); + } + + if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>()) + { + // checked by Client / Server as they know the handshake state + m_secure_renegotiation = true; + m_renegotiation_info = reneg->renegotiation_info(); + } + + if(Supported_Elliptic_Curves* ecc = extensions.get<Supported_Elliptic_Curves>()) + m_supported_curves = ecc->curves(); + + if(Signature_Algorithms* sigs = extensions.get<Signature_Algorithms>()) + { + m_supported_algos = sigs->supported_signature_algorthms(); + } + else + { + if(m_version >= Protocol_Version::TLS_V12) + { + /* + The rule for when a TLS 1.2 client not sending the extension + is strange; in theory, the server is supposed to act as if + the client had sent only SHA-1 using whatever signature + algorithm we end up negotiating. Right here, we don't know + what we'll end up negotiating (depends on policy), but we do + know that we'll only negotiate something the client sent, so + we can safely say it supports everything here and know that + we'll filter it out later. + */ + m_supported_algos.push_back(std::make_pair("SHA-1", "RSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "DSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA")); + } + else + { + // For versions before TLS 1.2, insert fake values for the old defaults + + m_supported_algos.push_back(std::make_pair("TLS.Digest.0", "RSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "DSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA")); + } + } + + if(Maximum_Fragment_Length* frag = extensions.get<Maximum_Fragment_Length>()) + { + m_fragment_size = frag->fragment_size(); + } + + if(Session_Ticket* ticket = extensions.get<Session_Ticket>()) + { + m_supports_session_ticket = true; + m_session_ticket = ticket->contents(); + } + + if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>()) + { + // checked by TLS_Client / TLS_Server as they know the handshake state + m_secure_renegotiation = true; + m_renegotiation_info = reneg->renegotiation_info(); + } + + if(value_exists(m_suites, static_cast<u16bit>(TLS_EMPTY_RENEGOTIATION_INFO_SCSV))) + { + /* + * Clients are allowed to send both the extension and the SCSV + * though it is not recommended. If it did, require that the + * extension value be empty. + */ + if(m_secure_renegotiation) + { + if(!m_renegotiation_info.empty()) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client send SCSV and non-empty extension"); + } + } + + m_secure_renegotiation = true; + m_renegotiation_info.clear(); + } + } + +/* +* Check if we offered this ciphersuite +*/ +bool Client_Hello::offered_suite(u16bit ciphersuite) const + { + for(size_t i = 0; i != m_suites.size(); ++i) + if(m_suites[i] == ciphersuite) + return true; + return false; + } + +} + +} diff --git a/src/tls/c_kex.cpp b/src/tls/c_kex.cpp new file mode 100644 index 000000000..16c02e2b8 --- /dev/null +++ b/src/tls/c_kex.cpp @@ -0,0 +1,391 @@ +/* +* 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/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/internal/assert.h> +#include <botan/credentials_manager.h> +#include <botan/pubkey.h> +#include <botan/dh.h> +#include <botan/ecdh.h> +#include <botan/rsa.h> +#include <botan/srp6.h> +#include <botan/rng.h> +#include <botan/loadstor.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +namespace { + +SecureVector<byte> strip_leading_zeros(const MemoryRegion<byte>& input) + { + size_t leading_zeros = 0; + + for(size_t i = 0; i != input.size(); ++i) + { + if(input[i] != 0) + break; + ++leading_zeros; + } + + SecureVector<byte> output(&input[leading_zeros], + input.size() - leading_zeros); + return output; + } + +} + +/* +* Create a new Client Key Exchange message +*/ +Client_Key_Exchange::Client_Key_Exchange(Record_Writer& writer, + Handshake_State* state, + Credentials_Manager& creds, + const std::vector<X509_Certificate>& peer_certs, + const std::string& hostname, + RandomNumberGenerator& rng) + { + const std::string kex_algo = state->suite.kex_algo(); + + if(kex_algo == "PSK") + { + std::string identity_hint = ""; + + if(state->server_kex) + { + TLS_Data_Reader reader(state->server_kex->params()); + identity_hint = reader.get_string(2, 0, 65535); + } + + const std::string hostname = state->client_hello->sni_hostname(); + + const std::string psk_identity = creds.psk_identity("tls-client", + hostname, + identity_hint); + + append_tls_length_value(key_material, psk_identity, 2); + + SymmetricKey psk = creds.psk("tls-client", hostname, psk_identity); + + MemoryVector<byte> zeros(psk.length()); + + append_tls_length_value(pre_master, zeros, 2); + append_tls_length_value(pre_master, psk.bits_of(), 2); + } + else if(state->server_kex) + { + TLS_Data_Reader reader(state->server_kex->params()); + + SymmetricKey psk; + + if(kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") + { + std::string identity_hint = reader.get_string(2, 0, 65535); + + const std::string hostname = state->client_hello->sni_hostname(); + + const std::string psk_identity = creds.psk_identity("tls-client", + hostname, + identity_hint); + + append_tls_length_value(key_material, psk_identity, 2); + + psk = creds.psk("tls-client", hostname, psk_identity); + } + + if(kex_algo == "DH" || kex_algo == "DHE_PSK") + { + BigInt p = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + BigInt Y = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + + if(reader.remaining_bytes()) + throw Decoding_Error("Bad params size for DH key exchange"); + + DL_Group group(p, g); + + if(!group.verify_group(rng, true)) + throw Internal_Error("DH group failed validation, possible attack"); + + DH_PublicKey counterparty_key(group, Y); + + // FIXME Check that public key is residue? + + DH_PrivateKey priv_key(rng, group); + + PK_Key_Agreement ka(priv_key, "Raw"); + + SecureVector<byte> dh_secret = strip_leading_zeros( + ka.derive_key(0, counterparty_key.public_value()).bits_of()); + + if(kex_algo == "DH") + pre_master = dh_secret; + else + { + append_tls_length_value(pre_master, dh_secret, 2); + append_tls_length_value(pre_master, psk.bits_of(), 2); + } + + append_tls_length_value(key_material, priv_key.public_value(), 2); + } + else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") + { + const byte curve_type = reader.get_byte(); + + if(curve_type != 3) + throw Decoding_Error("Server sent non-named ECC curve"); + + const u16bit curve_id = reader.get_u16bit(); + + const std::string name = Supported_Elliptic_Curves::curve_id_to_name(curve_id); + + if(name == "") + throw Decoding_Error("Server sent unknown named curve " + to_string(curve_id)); + + EC_Group group(name); + + MemoryVector<byte> ecdh_key = reader.get_range<byte>(1, 1, 255); + + ECDH_PublicKey counterparty_key(group, OS2ECP(ecdh_key, group.get_curve())); + + ECDH_PrivateKey priv_key(rng, group); + + PK_Key_Agreement ka(priv_key, "Raw"); + + SecureVector<byte> ecdh_secret = ka.derive_key(0, counterparty_key.public_value()).bits_of(); + + if(kex_algo == "ECDH") + pre_master = ecdh_secret; + else + { + append_tls_length_value(pre_master, ecdh_secret, 2); + append_tls_length_value(pre_master, psk.bits_of(), 2); + } + + append_tls_length_value(key_material, priv_key.public_value(), 1); + } + else if(kex_algo == "SRP_SHA") + { + const BigInt N = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + const BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + MemoryVector<byte> salt = reader.get_range<byte>(1, 1, 255); + const BigInt B = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + + const std::string srp_group = srp6_group_identifier(N, g); + + const std::string srp_identifier = + creds.srp_identifier("tls-client", hostname); + + const std::string srp_password = + creds.srp_password("tls-client", hostname, srp_identifier); + + std::pair<BigInt, SymmetricKey> srp_vals = + srp6_client_agree(srp_identifier, + srp_password, + srp_group, + "SHA-1", + salt, + B, + rng); + + append_tls_length_value(key_material, BigInt::encode(srp_vals.first), 2); + pre_master = srp_vals.second.bits_of(); + } + else + { + throw Internal_Error("Client_Key_Exchange: Unknown kex " + + kex_algo); + } + } + else + { + // No server key exchange msg better mean RSA kex + RSA key in cert + + if(kex_algo != "RSA") + throw Unexpected_Message("No server kex but negotiated kex " + kex_algo); + + if(peer_certs.empty()) + throw Internal_Error("No certificate and no server key exchange"); + + std::auto_ptr<Public_Key> pub_key(peer_certs[0].subject_public_key()); + + if(const RSA_PublicKey* rsa_pub = dynamic_cast<const RSA_PublicKey*>(pub_key.get())) + { + const Protocol_Version pref_version = state->client_hello->version(); + + pre_master = rng.random_vec(48); + pre_master[0] = pref_version.major_version(); + pre_master[1] = pref_version.minor_version(); + + PK_Encryptor_EME encryptor(*rsa_pub, "PKCS1v15"); + + MemoryVector<byte> encrypted_key = encryptor.encrypt(pre_master, rng); + + if(state->version() == Protocol_Version::SSL_V3) + key_material = encrypted_key; // no length field + else + append_tls_length_value(key_material, encrypted_key, 2); + } + else + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Expected a RSA key in server cert but got " + + pub_key->algo_name()); + } + + state->hash.update(writer.send(*this)); + } + +/* +* Read a Client Key Exchange message +*/ +Client_Key_Exchange::Client_Key_Exchange(const MemoryRegion<byte>& contents, + const Handshake_State* state, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng) + { + const std::string kex_algo = state->suite.kex_algo(); + + if(kex_algo == "RSA") + { + BOTAN_ASSERT(state->server_certs && !state->server_certs->cert_chain().empty(), + "No server certificate to use for RSA"); + + const Private_Key* private_key = state->server_rsa_kex_key; + + if(!private_key) + throw Internal_Error("Expected RSA kex but no server kex key set"); + + if(!dynamic_cast<const RSA_PrivateKey*>(private_key)) + throw Internal_Error("Expected RSA key but got " + private_key->algo_name()); + + PK_Decryptor_EME decryptor(*private_key, "PKCS1v15"); + + Protocol_Version client_version = state->client_hello->version(); + + try + { + if(state->version() == Protocol_Version::SSL_V3) + { + pre_master = decryptor.decrypt(contents); + } + else + { + TLS_Data_Reader reader(contents); + pre_master = decryptor.decrypt(reader.get_range<byte>(2, 0, 65535)); + } + + if(pre_master.size() != 48 || + client_version.major_version() != pre_master[0] || + client_version.minor_version() != pre_master[1]) + { + throw Decoding_Error("Client_Key_Exchange: Secret corrupted"); + } + } + catch(...) + { + // Randomize the hide timing channel + pre_master = rng.random_vec(48); + pre_master[0] = client_version.major_version(); + pre_master[1] = client_version.minor_version(); + } + } + else + { + TLS_Data_Reader reader(contents); + + SymmetricKey psk; + + if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") + { + const std::string psk_identity = reader.get_string(2, 0, 65535); + + psk = creds.psk("tls-server", + state->client_hello->sni_hostname(), + psk_identity); + + if(psk.length() == 0) + { + if(policy.hide_unknown_users()) + psk = SymmetricKey(rng, 16); + else + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "No PSK for identifier " + psk_identity); + } + + } + + if(kex_algo == "PSK") + { + MemoryVector<byte> zeros(psk.length()); + append_tls_length_value(pre_master, zeros, 2); + append_tls_length_value(pre_master, psk.bits_of(), 2); + } + else if(kex_algo == "SRP_SHA") + { + throw Internal_Error("SRP_SHA server side not done"); + } + else if(kex_algo == "DH" || kex_algo == "DHE_PSK" || + kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") + { + const Private_Key& private_key = state->server_kex->server_kex_key(); + + const PK_Key_Agreement_Key* ka_key = + dynamic_cast<const PK_Key_Agreement_Key*>(&private_key); + + if(!ka_key) + throw Internal_Error("Expected key agreement key type but got " + + private_key.algo_name()); + + try + { + PK_Key_Agreement ka(*ka_key, "Raw"); + + MemoryVector<byte> client_pubkey; + + if(ka_key->algo_name() == "DH") + client_pubkey = reader.get_range<byte>(2, 0, 65535); + else + client_pubkey = reader.get_range<byte>(1, 0, 255); + + SecureVector<byte> shared_secret = ka.derive_key(0, client_pubkey).bits_of(); + + if(ka_key->algo_name() == "DH") + shared_secret = strip_leading_zeros(shared_secret); + + if(kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") + { + append_tls_length_value(pre_master, shared_secret, 2); + append_tls_length_value(pre_master, psk.bits_of(), 2); + } + else + pre_master = shared_secret; + } + catch(std::exception &e) + { + /* + * 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(ka_key->public_value().size()); + } + } + else + throw Internal_Error("Client_Key_Exchange: Unknown kex type " + kex_algo); + } + } + +} + +} diff --git a/src/tls/cert_req.cpp b/src/tls/cert_req.cpp new file mode 100644 index 000000000..df70cc43d --- /dev/null +++ b/src/tls/cert_req.cpp @@ -0,0 +1,239 @@ +/* +* Certificate Request Message +* (C) 2004-2006,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/loadstor.h> + +namespace Botan { + +namespace TLS { + +namespace { + +std::string cert_type_code_to_name(byte code) + { + switch(code) + { + case 1: + return "RSA"; + case 2: + return "DSA"; + case 64: + return "ECDSA"; + default: + return ""; // DH or something else + } + } + +byte cert_type_name_to_code(const std::string& name) + { + if(name == "RSA") + return 1; + if(name == "DSA") + return 2; + if(name == "ECDSA") + return 64; + + throw Invalid_Argument("Unknown cert type " + name); + } + +} + +/** +* Create a new Certificate Request message +*/ +Certificate_Req::Certificate_Req(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + const std::vector<X509_Certificate>& ca_certs, + Protocol_Version version) + { + for(size_t i = 0; i != ca_certs.size(); ++i) + names.push_back(ca_certs[i].subject_dn()); + + cert_key_types.push_back("RSA"); + cert_key_types.push_back("DSA"); + cert_key_types.push_back("ECDSA"); + + if(version >= Protocol_Version::TLS_V12) + { + std::vector<std::string> hashes = policy.allowed_hashes(); + std::vector<std::string> sigs = policy.allowed_signature_methods(); + + for(size_t i = 0; i != hashes.size(); ++i) + for(size_t j = 0; j != sigs.size(); ++j) + m_supported_algos.push_back(std::make_pair(hashes[i], sigs[j])); + } + + hash.update(writer.send(*this)); + } + +/** +* Deserialize a Certificate Request message +*/ +Certificate_Req::Certificate_Req(const MemoryRegion<byte>& buf, + Protocol_Version version) + { + if(buf.size() < 4) + throw Decoding_Error("Certificate_Req: Bad certificate request"); + + TLS_Data_Reader reader(buf); + + std::vector<byte> cert_type_codes = reader.get_range_vector<byte>(1, 1, 255); + + for(size_t i = 0; i != cert_type_codes.size(); ++i) + { + const std::string cert_type_name = cert_type_code_to_name(cert_type_codes[i]); + + if(cert_type_name == "") // something we don't know + continue; + + cert_key_types.push_back(cert_type_name); + } + + if(version >= Protocol_Version::TLS_V12) + { + std::vector<byte> sig_hash_algs = reader.get_range_vector<byte>(2, 2, 65534); + + if(sig_hash_algs.size() % 2 != 0) + throw Decoding_Error("Bad length for signature IDs in certificate request"); + + for(size_t i = 0; i != sig_hash_algs.size(); i += 2) + { + std::string hash = Signature_Algorithms::hash_algo_name(sig_hash_algs[i]); + std::string sig = Signature_Algorithms::sig_algo_name(sig_hash_algs[i+1]); + m_supported_algos.push_back(std::make_pair(hash, sig)); + } + } + else + { + // The hardcoded settings from previous protocol versions + m_supported_algos.push_back(std::make_pair("TLS.Digest.0", "RSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "DSA")); + m_supported_algos.push_back(std::make_pair("SHA-1", "ECDSA")); + } + + u16bit purported_size = reader.get_u16bit(); + + if(reader.remaining_bytes() != purported_size) + throw Decoding_Error("Inconsistent length in certificate request"); + + while(reader.has_remaining()) + { + std::vector<byte> name_bits = reader.get_range_vector<byte>(2, 0, 65535); + + BER_Decoder decoder(&name_bits[0], name_bits.size()); + X509_DN name; + decoder.decode(name); + names.push_back(name); + } + } + +/** +* Serialize a Certificate Request message +*/ +MemoryVector<byte> Certificate_Req::serialize() const + { + MemoryVector<byte> buf; + + std::vector<byte> cert_types; + + for(size_t i = 0; i != cert_key_types.size(); ++i) + cert_types.push_back(cert_type_name_to_code(cert_key_types[i])); + + append_tls_length_value(buf, cert_types, 1); + + if(!m_supported_algos.empty()) + { + buf += Signature_Algorithms(m_supported_algos).serialize(); + } + + for(size_t i = 0; i != names.size(); ++i) + { + DER_Encoder encoder; + encoder.encode(names[i]); + + append_tls_length_value(buf, encoder.get_contents(), 2); + } + + return buf; + } + +/** +* Create a new Certificate message +*/ +Certificate::Certificate(Record_Writer& writer, + Handshake_Hash& hash, + const std::vector<X509_Certificate>& cert_list) : + m_certs(cert_list) + { + hash.update(writer.send(*this)); + } + +/** +* Deserialize a Certificate message +*/ +Certificate::Certificate(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]); + + if(total_size != buf.size() - 3) + throw Decoding_Error("Certificate: Message malformed"); + + const byte* certs = &buf[3]; + + while(certs != buf.end()) + { + if(buf.end() - certs < 3) + throw Decoding_Error("Certificate: Message malformed"); + + const size_t cert_size = make_u32bit(0, certs[0], certs[1], certs[2]); + + if(buf.end() - certs < (3 + cert_size)) + throw Decoding_Error("Certificate: Message malformed"); + + DataSource_Memory cert_buf(&certs[3], cert_size); + m_certs.push_back(X509_Certificate(cert_buf)); + + certs += cert_size + 3; + } + } + +/** +* Serialize a Certificate message +*/ +MemoryVector<byte> Certificate::serialize() const + { + MemoryVector<byte> buf(3); + + for(size_t i = 0; i != m_certs.size(); ++i) + { + MemoryVector<byte> raw_cert = m_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; + } + +} + +} diff --git a/src/tls/cert_ver.cpp b/src/tls/cert_ver.cpp new file mode 100644 index 000000000..73acf3de1 --- /dev/null +++ b/src/tls/cert_ver.cpp @@ -0,0 +1,117 @@ +/* +* Certificate Verify Message +* (C) 2004,2006,2011,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/internal/assert.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +/* +* Create a new Certificate Verify message +*/ +Certificate_Verify::Certificate_Verify(Record_Writer& writer, + Handshake_State* state, + RandomNumberGenerator& rng, + const Private_Key* priv_key) + { + BOTAN_ASSERT_NONNULL(priv_key); + + std::pair<std::string, Signature_Format> format = + state->choose_sig_format(priv_key, hash_algo, sig_algo, true); + + PK_Signer signer(*priv_key, format.first, format.second); + + if(state->version() == Protocol_Version::SSL_V3) + { + SecureVector<byte> md5_sha = state->hash.final_ssl3( + state->keys.master_secret()); + + if(priv_key->algo_name() == "DSA") + signature = signer.sign_message(&md5_sha[16], md5_sha.size()-16, rng); + else + signature = signer.sign_message(md5_sha, rng); + } + else + { + signature = signer.sign_message(state->hash.get_contents(), rng); + } + + state->hash.update(writer.send(*this)); + } + +/* +* Deserialize a Certificate Verify message +*/ +Certificate_Verify::Certificate_Verify(const MemoryRegion<byte>& buf, + Protocol_Version version) + { + TLS_Data_Reader reader(buf); + + if(version >= Protocol_Version::TLS_V12) + { + hash_algo = Signature_Algorithms::hash_algo_name(reader.get_byte()); + sig_algo = Signature_Algorithms::sig_algo_name(reader.get_byte()); + } + + signature = reader.get_range<byte>(2, 0, 65535); + } + +/* +* Serialize a Certificate Verify message +*/ +MemoryVector<byte> Certificate_Verify::serialize() const + { + MemoryVector<byte> buf; + + if(hash_algo != "" && sig_algo != "") + { + buf.push_back(Signature_Algorithms::hash_algo_code(hash_algo)); + buf.push_back(Signature_Algorithms::sig_algo_code(sig_algo)); + } + + 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; + } + +/* +* Verify a Certificate Verify message +*/ +bool Certificate_Verify::verify(const X509_Certificate& cert, + Handshake_State* state) + { + std::auto_ptr<Public_Key> key(cert.subject_public_key()); + + std::pair<std::string, Signature_Format> format = + state->understand_sig_format(key.get(), hash_algo, sig_algo, true); + + PK_Verifier verifier(*key, format.first, format.second); + + if(state->version() == Protocol_Version::SSL_V3) + { + SecureVector<byte> md5_sha = state->hash.final_ssl3( + state->keys.master_secret()); + + return verifier.verify_message(&md5_sha[16], md5_sha.size()-16, + &signature[0], signature.size()); + } + + return verifier.verify_message(state->hash.get_contents(), signature); + } + +} + +} diff --git a/src/tls/finished.cpp b/src/tls/finished.cpp new file mode 100644 index 000000000..a494bf932 --- /dev/null +++ b/src/tls/finished.cpp @@ -0,0 +1,104 @@ +/* +* Finished Message +* (C) 2004-2006,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/tls_record.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +namespace { + +/* +* Compute the verify_data +*/ +MemoryVector<byte> finished_compute_verify(Handshake_State* state, + Connection_Side side) + { + if(state->version() == Protocol_Version::SSL_V3) + { + const byte SSL_CLIENT_LABEL[] = { 0x43, 0x4C, 0x4E, 0x54 }; + const byte SSL_SERVER_LABEL[] = { 0x53, 0x52, 0x56, 0x52 }; + + Handshake_Hash hash = state->hash; // don't modify state + + MemoryVector<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(state->keys.master_secret()); + } + else + { + 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 }; + + std::auto_ptr<KDF> prf(state->protocol_specific_prf()); + + MemoryVector<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 += state->hash.final(state->version(), state->suite.mac_algo()); + + return prf->derive_key(12, state->keys.master_secret(), input); + } + } + +} + +/* +* Create a new Finished message +*/ +Finished::Finished(Record_Writer& writer, + Handshake_State* state, + Connection_Side side) + { + verification_data = finished_compute_verify(state, side); + state->hash.update(writer.send(*this)); + } + +/* +* Serialize a Finished message +*/ +MemoryVector<byte> Finished::serialize() const + { + return verification_data; + } + +/* +* Deserialize a Finished message +*/ +Finished::Finished(const MemoryRegion<byte>& buf) + { + verification_data = buf; + } + +/* +* Verify a Finished message +*/ +bool Finished::verify(Handshake_State* state, + Connection_Side side) + { + return (verification_data == finished_compute_verify(state, side)); + } + +} + +} diff --git a/src/tls/hello_verify.cpp b/src/tls/hello_verify.cpp new file mode 100644 index 000000000..c7aae94a1 --- /dev/null +++ b/src/tls/hello_verify.cpp @@ -0,0 +1,61 @@ +/* +* DTLS Hello Verify Request +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/lookup.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +Hello_Verify_Request::Hello_Verify_Request(const MemoryRegion<byte>& buf) + { + if(buf.size() < 3) + throw Decoding_Error("Hello verify request too small"); + + if(buf[0] != 254 || (buf[1] != 255 && buf[1] != 253)) + throw Decoding_Error("Unknown version from server in hello verify request"); + + m_cookie.resize(buf.size() - 2); + copy_mem(&m_cookie[0], &buf[2], buf.size() - 2); + } + +Hello_Verify_Request::Hello_Verify_Request(const MemoryVector<byte>& client_hello_bits, + const std::string& client_identity, + const SymmetricKey& secret_key) + { + std::auto_ptr<MessageAuthenticationCode> hmac(get_mac("HMAC(SHA-256)")); + hmac->set_key(secret_key); + + hmac->update_be(client_hello_bits.size()); + hmac->update(client_hello_bits); + hmac->update_be(client_identity.size()); + hmac->update(client_identity); + + m_cookie = hmac->final(); + } + +MemoryVector<byte> Hello_Verify_Request::serialize() const + { + /* DTLS 1.2 server implementations SHOULD use DTLS version 1.0 + regardless of the version of TLS that is expected to be + negotiated (RFC 6347, section 4.2.1) + */ + + Protocol_Version format_version(Protocol_Version::TLS_V11); + + MemoryVector<byte> bits; + bits.push_back(format_version.major_version()); + bits.push_back(format_version.minor_version()); + bits += m_cookie; + return bits; + } + +} + +} diff --git a/src/tls/info.txt b/src/tls/info.txt new file mode 100644 index 000000000..229cf658f --- /dev/null +++ b/src/tls/info.txt @@ -0,0 +1,93 @@ +define TLS + +load_on auto + +<comment> +The TLS code is complex, new, and not yet reviewed, there may be +serious bugs or security issues. +</comment> + +uses_tr1 yes + +<header:public> +tls_alert.h +tls_channel.h +tls_ciphersuite.h +tls_client.h +tls_exceptn.h +tls_magic.h +tls_policy.h +tls_record.h +tls_server.h +tls_session.h +tls_session_manager.h +tls_version.h +</header:public> + +<header:internal> +tls_extensions.h +tls_handshake_hash.h +tls_handshake_reader.h +tls_handshake_state.h +tls_messages.h +tls_reader.h +tls_session_key.h +</header:internal> + +<source> +c_hello.cpp +c_kex.cpp +cert_req.cpp +cert_ver.cpp +finished.cpp +hello_verify.cpp +next_protocol.cpp +rec_read.cpp +rec_wri.cpp +s_hello.cpp +s_kex.cpp +session_ticket.cpp +tls_alert.cpp +tls_channel.cpp +tls_ciphersuite.cpp +tls_client.cpp +tls_extensions.cpp +tls_handshake_hash.cpp +tls_handshake_reader.cpp +tls_handshake_state.cpp +tls_policy.cpp +tls_server.cpp +tls_session.cpp +tls_session_key.cpp +tls_session_manager.cpp +tls_suite_info.cpp +tls_version.cpp +</source> + +<requires> +aes +arc4 +asn1 +credentials +des +dh +dsa +ecdh +ecdsa +eme_pkcs +emsa3 +filters +hmac +kdf2 +md5 +prf_ssl3 +prf_tls +rng +rsa +seed +srp6 +sha1 +sha2_32 +ssl3mac +x509cert +</requires> diff --git a/src/tls/next_protocol.cpp b/src/tls/next_protocol.cpp new file mode 100644 index 000000000..17b77fb6e --- /dev/null +++ b/src/tls/next_protocol.cpp @@ -0,0 +1,55 @@ +/* +* Next Protocol Negotation +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_reader.h> +#include <botan/tls_record.h> + +namespace Botan { + +namespace TLS { + +Next_Protocol::Next_Protocol(Record_Writer& writer, + Handshake_Hash& hash, + const std::string& protocol) : + m_protocol(protocol) + { + hash.update(writer.send(*this)); + } + +Next_Protocol::Next_Protocol(const MemoryRegion<byte>& buf) + { + TLS_Data_Reader reader(buf); + + m_protocol = reader.get_string(1, 0, 255); + + reader.get_range_vector<byte>(1, 0, 255); // padding, ignored + } + +MemoryVector<byte> Next_Protocol::serialize() const + { + MemoryVector<byte> buf; + + append_tls_length_value(buf, + reinterpret_cast<const byte*>(m_protocol.data()), + m_protocol.size(), + 1); + + const byte padding_len = 32 - ((m_protocol.size() + 2) % 32); + + buf.push_back(padding_len); + + for(size_t i = 0; i != padding_len; ++i) + buf.push_back(0); + + return buf; + } + +} + +} diff --git a/src/tls/rec_read.cpp b/src/tls/rec_read.cpp new file mode 100644 index 000000000..d1fab4692 --- /dev/null +++ b/src/tls/rec_read.cpp @@ -0,0 +1,351 @@ +/* +* TLS Record Reading +* (C) 2004-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_record.h> +#include <botan/lookup.h> +#include <botan/loadstor.h> +#include <botan/internal/tls_session_key.h> +#include <botan/internal/rounding.h> +#include <botan/internal/assert.h> + +namespace Botan { + +namespace TLS { + +Record_Reader::Record_Reader() : + m_readbuf(TLS_HEADER_SIZE + MAX_CIPHERTEXT_SIZE), + m_mac(0) + { + reset(); + set_maximum_fragment_size(0); + } + +/* +* Reset the state +*/ +void Record_Reader::reset() + { + m_macbuf.clear(); + + zeroise(m_readbuf); + m_readbuf_pos = 0; + + m_cipher.reset(); + + delete m_mac; + m_mac = 0; + + m_block_size = 0; + m_iv_size = 0; + m_version = Protocol_Version(); + m_seq_no = 0; + set_maximum_fragment_size(0); + } + +void Record_Reader::set_maximum_fragment_size(size_t max_fragment) + { + if(max_fragment == 0) + m_max_fragment = MAX_PLAINTEXT_SIZE; + else + m_max_fragment = clamp(max_fragment, 128, MAX_PLAINTEXT_SIZE); + } + +/* +* Set the version to use +*/ +void Record_Reader::set_version(Protocol_Version version) + { + m_version = version; + } + +/* +* Set the keys for reading +*/ +void Record_Reader::activate(Connection_Side side, + const Ciphersuite& suite, + const Session_Keys& keys, + byte compression_method) + { + m_cipher.reset(); + delete m_mac; + m_mac = 0; + m_seq_no = 0; + + if(compression_method != NO_COMPRESSION) + throw Internal_Error("Negotiated unknown compression algorithm"); + + 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)) + { + m_cipher.append(get_cipher( + cipher_algo + "/CBC/NoPadding", + cipher_key, iv, DECRYPTION) + ); + m_block_size = block_size_of(cipher_algo); + + if(m_version >= Protocol_Version::TLS_V11) + m_iv_size = m_block_size; + else + m_iv_size = 0; + } + else if(have_stream_cipher(cipher_algo)) + { + m_cipher.append(get_cipher(cipher_algo, cipher_key, DECRYPTION)); + m_block_size = 0; + m_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(m_version == Protocol_Version::SSL_V3) + m_mac = af.make_mac("SSL3-MAC(" + mac_algo + ")"); + else + m_mac = af.make_mac("HMAC(" + mac_algo + ")"); + + m_mac->set_key(mac_key); + m_macbuf.resize(m_mac->output_length()); + } + else + throw Invalid_Argument("Record_Reader: Unknown hash " + mac_algo); + } + +size_t Record_Reader::fill_buffer_to(const byte*& input, + size_t& input_size, + size_t& input_consumed, + size_t desired) + { + if(desired <= m_readbuf_pos) + return 0; // already have it + + const size_t space_available = (m_readbuf.size() - m_readbuf_pos); + const size_t taken = std::min(input_size, desired - m_readbuf_pos); + + if(taken > space_available) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Record is larger than allowed maximum size"); + + copy_mem(&m_readbuf[m_readbuf_pos], input, taken); + m_readbuf_pos += taken; + input_consumed += taken; + input_size -= taken; + input += taken; + + return (desired - m_readbuf_pos); // how many bytes do we still need? + } + +/* +* Retrieve the next record +*/ +size_t Record_Reader::add_input(const byte input_array[], size_t input_sz, + size_t& consumed, + byte& msg_type, + MemoryVector<byte>& msg) + { + const byte* input = &input_array[0]; + + consumed = 0; + + if(m_readbuf_pos < TLS_HEADER_SIZE) // header incomplete? + { + if(size_t needed = fill_buffer_to(input, input_sz, consumed, TLS_HEADER_SIZE)) + return needed; + + BOTAN_ASSERT_EQUAL(m_readbuf_pos, TLS_HEADER_SIZE, + "Have an entire header"); + } + + // Possible SSLv2 format client hello + if((!m_mac) && (m_readbuf[0] & 0x80) && (m_readbuf[2] == 1)) + { + if(m_readbuf[3] == 0 && m_readbuf[4] == 2) + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client claims to only support SSLv2, rejecting"); + + if(m_readbuf[3] >= 3) // SSLv2 mapped TLS hello, then? + { + size_t record_len = make_u16bit(m_readbuf[0], m_readbuf[1]) & 0x7FFF; + + if(size_t needed = fill_buffer_to(input, input_sz, consumed, record_len + 2)) + return needed; + + BOTAN_ASSERT_EQUAL(m_readbuf_pos, (record_len + 2), + "Have the entire SSLv2 hello"); + + msg_type = HANDSHAKE; + + msg.resize(record_len + 4); + + // Fake v3-style handshake message wrapper + msg[0] = CLIENT_HELLO_SSLV2; + msg[1] = 0; + msg[2] = m_readbuf[0] & 0x7F; + msg[3] = m_readbuf[1]; + + copy_mem(&msg[4], &m_readbuf[2], m_readbuf_pos - 2); + m_readbuf_pos = 0; + return 0; + } + } + + if(m_readbuf[0] != CHANGE_CIPHER_SPEC && + m_readbuf[0] != ALERT && + m_readbuf[0] != HANDSHAKE && + m_readbuf[0] != APPLICATION_DATA) + { + throw Unexpected_Message( + "Unknown record type " + to_string(m_readbuf[0]) + " from counterparty"); + } + + const size_t record_len = make_u16bit(m_readbuf[3], m_readbuf[4]); + + if(m_version.major_version()) + { + if(m_readbuf[1] != m_version.major_version() || + m_readbuf[2] != m_version.minor_version()) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Got unexpected version from counterparty"); + } + } + + if(record_len > MAX_CIPHERTEXT_SIZE) + throw TLS_Exception(Alert::RECORD_OVERFLOW, + "Got message that exceeds maximum size"); + + if(size_t needed = fill_buffer_to(input, input_sz, consumed, + TLS_HEADER_SIZE + record_len)) + return needed; + + BOTAN_ASSERT_EQUAL(static_cast<size_t>(TLS_HEADER_SIZE) + record_len, + m_readbuf_pos, + "Have the full record"); + + // Null mac means no encryption either, only valid during handshake + if(!m_mac) + { + if(m_readbuf[0] != CHANGE_CIPHER_SPEC && + m_readbuf[0] != ALERT && + m_readbuf[0] != HANDSHAKE) + { + throw Decoding_Error("Invalid msg type received during handshake"); + } + + msg_type = m_readbuf[0]; + msg.resize(record_len); + copy_mem(&msg[0], &m_readbuf[TLS_HEADER_SIZE], record_len); + + m_readbuf_pos = 0; + return 0; // got a full record + } + + // Otherwise, decrypt, check MAC, return plaintext + + // FIXME: avoid memory allocation by processing in place + m_cipher.process_msg(&m_readbuf[TLS_HEADER_SIZE], record_len); + size_t got_back = m_cipher.read(&m_readbuf[TLS_HEADER_SIZE], record_len, Pipe::LAST_MESSAGE); + BOTAN_ASSERT_EQUAL(got_back, record_len, "Cipher encrypted full amount"); + + BOTAN_ASSERT_EQUAL(m_cipher.remaining(Pipe::LAST_MESSAGE), 0, + "Cipher had no remaining inputs"); + + size_t pad_size = 0; + + if(m_block_size) + { + byte pad_value = m_readbuf[TLS_HEADER_SIZE + (record_len-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 + * succeed. 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(m_version == Protocol_Version::SSL_V3) + { + if(pad_value > m_block_size) + pad_size = 0; + } + else + { + bool padding_good = true; + + for(size_t i = 0; i != pad_size; ++i) + if(m_readbuf[TLS_HEADER_SIZE + (record_len-i-1)] != pad_value) + padding_good = false; + + if(!padding_good) + pad_size = 0; + } + } + + const size_t mac_pad_iv_size = m_macbuf.size() + pad_size + m_iv_size; + + if(record_len < mac_pad_iv_size) + throw Decoding_Error("Record sent with invalid length"); + + const u16bit plain_length = record_len - mac_pad_iv_size; + + if(plain_length > m_max_fragment) + throw TLS_Exception(Alert::RECORD_OVERFLOW, "Plaintext record is too large"); + + m_mac->update_be(m_seq_no); + m_mac->update(m_readbuf[0]); // msg_type + + if(m_version != Protocol_Version::SSL_V3) + { + m_mac->update(m_version.major_version()); + m_mac->update(m_version.minor_version()); + } + + m_mac->update_be(plain_length); + m_mac->update(&m_readbuf[TLS_HEADER_SIZE + m_iv_size], plain_length); + + ++m_seq_no; + + m_mac->final(m_macbuf); + + const size_t mac_offset = record_len - (m_macbuf.size() + pad_size); + + if(!same_mem(&m_readbuf[TLS_HEADER_SIZE + mac_offset], &m_macbuf[0], m_macbuf.size())) + throw TLS_Exception(Alert::BAD_RECORD_MAC, "Message authentication failure"); + + msg_type = m_readbuf[0]; + + msg.resize(plain_length); + copy_mem(&msg[0], &m_readbuf[TLS_HEADER_SIZE + m_iv_size], plain_length); + m_readbuf_pos = 0; + return 0; + } + +} + +} diff --git a/src/tls/rec_wri.cpp b/src/tls/rec_wri.cpp new file mode 100644 index 000000000..85f178ffe --- /dev/null +++ b/src/tls/rec_wri.cpp @@ -0,0 +1,317 @@ +/* +* TLS Record Writing +* (C) 2004-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_record.h> +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_session_key.h> +#include <botan/internal/tls_handshake_hash.h> +#include <botan/lookup.h> +#include <botan/internal/rounding.h> +#include <botan/internal/assert.h> +#include <botan/loadstor.h> +#include <botan/libstate.h> + +namespace Botan { + +namespace TLS { + +/* +* Record_Writer Constructor +*/ +Record_Writer::Record_Writer(std::tr1::function<void (const byte[], size_t)> out) : + m_output_fn(out), + m_writebuf(TLS_HEADER_SIZE + MAX_CIPHERTEXT_SIZE), + m_mac(0) + { + reset(); + set_maximum_fragment_size(0); + } + +void Record_Writer::set_maximum_fragment_size(size_t max_fragment) + { + if(max_fragment == 0) + m_max_fragment = MAX_PLAINTEXT_SIZE; + else + m_max_fragment = clamp(max_fragment, 128, MAX_PLAINTEXT_SIZE); + } + +/* +* Reset the state +*/ +void Record_Writer::reset() + { + set_maximum_fragment_size(0); + m_cipher.reset(); + + delete m_mac; + m_mac = 0; + + m_version = Protocol_Version(); + m_block_size = 0; + m_mac_size = 0; + m_iv_size = 0; + + m_seq_no = 0; + } + +/* +* Set the version to use +*/ +void Record_Writer::set_version(Protocol_Version version) + { + m_version = version; + } + +/* +* Set the keys for writing +*/ +void Record_Writer::activate(Connection_Side side, + const Ciphersuite& suite, + const Session_Keys& keys, + byte compression_method) + { + m_cipher.reset(); + delete m_mac; + m_mac = 0; + + if(compression_method != NO_COMPRESSION) + throw Internal_Error("Negotiated unknown compression algorithm"); + + /* + RFC 4346: + A sequence number is incremented after each record: specifically, + the first record transmitted under a particular connection state + MUST use sequence number 0 + */ + m_seq_no = 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)) + { + m_cipher.append(get_cipher( + cipher_algo + "/CBC/NoPadding", + cipher_key, iv, ENCRYPTION) + ); + m_block_size = block_size_of(cipher_algo); + + if(m_version >= Protocol_Version::TLS_V11) + m_iv_size = m_block_size; + else + m_iv_size = 0; + } + else if(have_stream_cipher(cipher_algo)) + { + m_cipher.append(get_cipher(cipher_algo, cipher_key, ENCRYPTION)); + m_block_size = 0; + m_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(m_version == Protocol_Version::SSL_V3) + m_mac = af.make_mac("SSL3-MAC(" + mac_algo + ")"); + else + m_mac = af.make_mac("HMAC(" + mac_algo + ")"); + + m_mac->set_key(mac_key); + m_mac_size = m_mac->output_length(); + } + else + throw Invalid_Argument("Record_Writer: Unknown hash " + mac_algo); + } + +MemoryVector<byte> Record_Writer::send(Handshake_Message& msg) + { + const MemoryVector<byte> buf = msg.serialize(); + MemoryVector<byte> send_buf(4); + + const size_t buf_size = buf.size(); + + send_buf[0] = msg.type(); + + for(size_t i = 1; i != 4; ++i) + send_buf[i] = get_byte<u32bit>(i, buf_size); + + send_buf += buf; + + send(HANDSHAKE, &send_buf[0], send_buf.size()); + + return send_buf; + } + +/* +* Send one or more records to the other side +*/ +void Record_Writer::send(byte type, const byte input[], size_t length) + { + if(length == 0) + return; + + /* + * If using CBC mode in SSLv3/TLS v1.0, send a single byte of + * plaintext to randomize the (implicit) IV of the following main + * block. If using a stream cipher, or TLS v1.1, this isn't + * necessary. + * + * An empty record also works but apparently some implementations do + * not like this (https://bugzilla.mozilla.org/show_bug.cgi?id=665814) + * + * See http://www.openssl.org/~bodo/tls-cbc.txt for background. + */ + if((type == APPLICATION) && (m_block_size > 0) && (m_iv_size == 0)) + { + send_record(type, &input[0], 1); + input += 1; + length -= 1; + } + + while(length) + { + const size_t sending = std::min(length, m_max_fragment); + send_record(type, &input[0], sending); + + input += sending; + length -= sending; + } + } + +/* +* Encrypt and send the record +*/ +void Record_Writer::send_record(byte type, const byte input[], size_t length) + { + if(length >= MAX_PLAINTEXT_SIZE) + throw Internal_Error("Record_Writer: Compressed packet is too big"); + + if(m_mac_size == 0) // initial unencrypted handshake records + { + m_writebuf[0] = type; + m_writebuf[1] = m_version.major_version(); + m_writebuf[2] = m_version.minor_version(); + m_writebuf[3] = get_byte<u16bit>(0, length); + m_writebuf[4] = get_byte<u16bit>(1, length); + + copy_mem(&m_writebuf[TLS_HEADER_SIZE], input, length); + + m_output_fn(&m_writebuf[0], TLS_HEADER_SIZE + length); + return; + } + + m_mac->update_be(m_seq_no); + m_mac->update(type); + + if(m_version != Protocol_Version::SSL_V3) + { + m_mac->update(m_version.major_version()); + m_mac->update(m_version.minor_version()); + } + + m_mac->update(get_byte<u16bit>(0, length)); + m_mac->update(get_byte<u16bit>(1, length)); + m_mac->update(input, length); + + const size_t buf_size = round_up(m_iv_size + length + + m_mac->output_length() + + (m_block_size ? 1 : 0), + m_block_size); + + if(buf_size >= MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Record_Writer: Record is too big"); + + BOTAN_ASSERT(m_writebuf.size() >= TLS_HEADER_SIZE + MAX_CIPHERTEXT_SIZE, + "Write buffer is big enough"); + + // TLS record header + m_writebuf[0] = type; + m_writebuf[1] = m_version.major_version(); + m_writebuf[2] = m_version.minor_version(); + m_writebuf[3] = get_byte<u16bit>(0, buf_size); + m_writebuf[4] = get_byte<u16bit>(1, buf_size); + + byte* buf_write_ptr = &m_writebuf[TLS_HEADER_SIZE]; + + if(m_iv_size) + { + RandomNumberGenerator& rng = global_state().global_rng(); + rng.randomize(buf_write_ptr, m_iv_size); + buf_write_ptr += m_iv_size; + } + + copy_mem(buf_write_ptr, input, length); + buf_write_ptr += length; + + m_mac->final(buf_write_ptr); + buf_write_ptr += m_mac->output_length(); + + if(m_block_size) + { + const size_t pad_val = + buf_size - (m_iv_size + length + m_mac->output_length() + 1); + + for(size_t i = 0; i != pad_val + 1; ++i) + { + *buf_write_ptr = pad_val; + buf_write_ptr += 1; + } + } + + // FIXME: this could be done in-place without copying + m_cipher.process_msg(&m_writebuf[TLS_HEADER_SIZE], buf_size); + + const size_t ctext_size = m_cipher.remaining(Pipe::LAST_MESSAGE); + + BOTAN_ASSERT_EQUAL(ctext_size, buf_size, "Cipher encrypted full amount"); + + if(ctext_size > MAX_CIPHERTEXT_SIZE) + throw Internal_Error("Produced ciphertext larger than protocol allows"); + + m_cipher.read(&m_writebuf[TLS_HEADER_SIZE], ctext_size, Pipe::LAST_MESSAGE); + + BOTAN_ASSERT_EQUAL(m_cipher.remaining(Pipe::LAST_MESSAGE), 0, + "No data remains in pipe"); + + m_output_fn(&m_writebuf[0], TLS_HEADER_SIZE + buf_size); + + m_seq_no++; + } + +/* +* Send an alert +*/ +void Record_Writer::send_alert(const Alert& alert) + { + const byte alert_bits[2] = { alert.is_fatal() ? 2 : 1, + alert.type() }; + + send(ALERT, alert_bits, sizeof(alert_bits)); + } + +} + +} diff --git a/src/tls/s_hello.cpp b/src/tls/s_hello.cpp new file mode 100644 index 000000000..bb93108d9 --- /dev/null +++ b/src/tls/s_hello.cpp @@ -0,0 +1,174 @@ +;/* +* TLS Server Hello and Server Hello Done +* (C) 2004-2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_session_key.h> +#include <botan/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/internal/stl_util.h> + +namespace Botan { + +namespace TLS { + +/* +* Create a new Server Hello message +*/ +Server_Hello::Server_Hello(Record_Writer& writer, + Handshake_Hash& hash, + const MemoryRegion<byte>& session_id, + Protocol_Version ver, + u16bit ciphersuite, + byte compression, + size_t max_fragment_size, + bool client_has_secure_renegotiation, + const MemoryRegion<byte>& reneg_info, + bool offer_session_ticket, + bool client_has_npn, + const std::vector<std::string>& next_protocols, + RandomNumberGenerator& rng) : + m_version(ver), + m_session_id(session_id), + m_random(make_hello_random(rng)), + m_ciphersuite(ciphersuite), + m_comp_method(compression), + m_fragment_size(max_fragment_size), + m_secure_renegotiation(client_has_secure_renegotiation), + m_renegotiation_info(reneg_info), + m_next_protocol(client_has_npn), + m_next_protocols(next_protocols), + m_supports_session_ticket(offer_session_ticket) + { + hash.update(writer.send(*this)); + } + +/* +* Deserialize a Server Hello message +*/ +Server_Hello::Server_Hello(const MemoryRegion<byte>& buf) + { + m_secure_renegotiation = false; + m_supports_session_ticket = false; + m_next_protocol = false; + + if(buf.size() < 38) + throw Decoding_Error("Server_Hello: Packet corrupted"); + + TLS_Data_Reader reader(buf); + + const byte major_version = reader.get_byte(); + const byte minor_version = reader.get_byte(); + + m_version = Protocol_Version(major_version, minor_version); + + if(m_version != Protocol_Version::SSL_V3 && + m_version != Protocol_Version::TLS_V10 && + m_version != Protocol_Version::TLS_V11 && + m_version != Protocol_Version::TLS_V12) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Server_Hello: Unsupported server version"); + } + + m_random = reader.get_fixed<byte>(32); + + m_session_id = reader.get_range<byte>(1, 0, 32); + + m_ciphersuite = reader.get_u16bit(); + + m_comp_method = reader.get_byte(); + + Extensions extensions(reader); + + if(Renegotation_Extension* reneg = extensions.get<Renegotation_Extension>()) + { + // checked by Client / Server as they know the handshake state + m_secure_renegotiation = true; + m_renegotiation_info = reneg->renegotiation_info(); + } + + if(Next_Protocol_Notification* npn = extensions.get<Next_Protocol_Notification>()) + { + m_next_protocols = npn->protocols(); + m_next_protocol = true; + } + + if(Session_Ticket* ticket = extensions.get<Session_Ticket>()) + { + if(!ticket->contents().empty()) + throw Decoding_Error("TLS server sent non-empty session ticket extension"); + m_supports_session_ticket = true; + } + } + +/* +* Serialize a Server Hello message +*/ +MemoryVector<byte> Server_Hello::serialize() const + { + MemoryVector<byte> buf; + + buf.push_back(m_version.major_version()); + buf.push_back(m_version.minor_version()); + buf += m_random; + + append_tls_length_value(buf, m_session_id, 1); + + buf.push_back(get_byte(0, m_ciphersuite)); + buf.push_back(get_byte(1, m_ciphersuite)); + + buf.push_back(m_comp_method); + + Extensions extensions; + + if(m_secure_renegotiation) + extensions.add(new Renegotation_Extension(m_renegotiation_info)); + + if(m_fragment_size != 0) + extensions.add(new Maximum_Fragment_Length(m_fragment_size)); + + if(m_next_protocol) + extensions.add(new Next_Protocol_Notification(m_next_protocols)); + + if(m_supports_session_ticket) + extensions.add(new Session_Ticket()); + + buf += extensions.serialize(); + + return buf; + } + +/* +* Create a new Server Hello Done message +*/ +Server_Hello_Done::Server_Hello_Done(Record_Writer& writer, + Handshake_Hash& hash) + { + hash.update(writer.send(*this)); + } + +/* +* Deserialize a Server Hello Done message +*/ +Server_Hello_Done::Server_Hello_Done(const MemoryRegion<byte>& buf) + { + if(buf.size()) + throw Decoding_Error("Server_Hello_Done: Must be empty, and is not"); + } + +/* +* Serialize a Server Hello Done message +*/ +MemoryVector<byte> Server_Hello_Done::serialize() const + { + return MemoryVector<byte>(); + } + +} + +} diff --git a/src/tls/s_kex.cpp b/src/tls/s_kex.cpp new file mode 100644 index 000000000..24bc6ecaa --- /dev/null +++ b/src/tls/s_kex.cpp @@ -0,0 +1,283 @@ +/* +* Server Key Exchange Message +* (C) 2004-2010,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_reader.h> +#include <botan/internal/tls_extensions.h> +#include <botan/tls_record.h> +#include <botan/internal/assert.h> +#include <botan/credentials_manager.h> +#include <botan/loadstor.h> +#include <botan/pubkey.h> +#include <botan/dh.h> +#include <botan/ecdh.h> +#include <botan/rsa.h> +#include <botan/srp6.h> +#include <botan/oids.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +/** +* Create a new Server Key Exchange message +*/ +Server_Key_Exchange::Server_Key_Exchange(Record_Writer& writer, + Handshake_State* state, + const Policy& policy, + Credentials_Manager& creds, + RandomNumberGenerator& rng, + const Private_Key* signing_key) : + m_kex_key(0) + { + const std::string hostname = state->client_hello->sni_hostname(); + const std::string kex_algo = state->suite.kex_algo(); + + if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") + { + std::string identity_hint = + creds.psk_identity_hint("tls-server", hostname); + + append_tls_length_value(m_params, identity_hint, 2); + } + + if(kex_algo == "DH" || kex_algo == "DHE_PSK") + { + std::auto_ptr<DH_PrivateKey> dh(new DH_PrivateKey(rng, policy.dh_group())); + + append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_p()), 2); + append_tls_length_value(m_params, BigInt::encode(dh->get_domain().get_g()), 2); + append_tls_length_value(m_params, dh->public_value(), 2); + m_kex_key = dh.release(); + } + else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") + { + const std::vector<std::string>& curves = + state->client_hello->supported_ecc_curves(); + + if(curves.empty()) + throw Internal_Error("Client sent no ECC extension but we negotiated ECDH"); + + const std::string curve_name = policy.choose_curve(curves); + + if(curve_name == "") + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Could not agree on an ECC curve with the client"); + + EC_Group ec_group(curve_name); + + std::auto_ptr<ECDH_PrivateKey> ecdh(new ECDH_PrivateKey(rng, ec_group)); + + const std::string ecdh_domain_oid = ecdh->domain().get_oid(); + const std::string domain = OIDS::lookup(OID(ecdh_domain_oid)); + + if(domain == "") + throw Internal_Error("Could not find name of ECDH domain " + ecdh_domain_oid); + + const u16bit named_curve_id = Supported_Elliptic_Curves::name_to_curve_id(domain); + + m_params.push_back(3); // named curve + m_params.push_back(get_byte(0, named_curve_id)); + m_params.push_back(get_byte(1, named_curve_id)); + + append_tls_length_value(m_params, ecdh->public_value(), 1); + + m_kex_key = ecdh.release(); + } + else if(kex_algo == "SRP_SHA") + { + const std::string srp_identifier = state->client_hello->srp_identifier(); + + BigInt N, g, v; + MemoryVector<byte> salt; + + const bool found = creds.srp_verifier("tls-server", hostname, + srp_identifier, + N, g, v, salt, + policy.hide_unknown_users()); + + if(!found) + throw TLS_Exception(Alert::UNKNOWN_PSK_IDENTITY, + "Unknown SRP user " + srp_identifier); + +#if 0 + BigInt B = srp6_server_step1(v, srp6_group_identifier(N, g), + "SHA-1", rng); +#else + BigInt B = 0; +#endif + + append_tls_length_value(m_params, BigInt::encode(N), 2); + append_tls_length_value(m_params, BigInt::encode(g), 2); + append_tls_length_value(m_params, salt, 1); + append_tls_length_value(m_params, BigInt::encode(B), 2); + + /* + * To finish, client key exchange needs to know + * group_id, v, b, B + */ + } + else if(kex_algo != "PSK") + throw Internal_Error("Server_Key_Exchange: Unknown kex type " + kex_algo); + + if(state->suite.sig_algo() != "") + { + BOTAN_ASSERT(signing_key, "No signing key set"); + + std::pair<std::string, Signature_Format> format = + state->choose_sig_format(signing_key, m_hash_algo, m_sig_algo, false); + + PK_Signer signer(*signing_key, format.first, format.second); + + signer.update(state->client_hello->random()); + signer.update(state->server_hello->random()); + signer.update(params()); + m_signature = signer.signature(rng); + } + + state->hash.update(writer.send(*this)); + } + +/** +* Deserialize a Server Key Exchange message +*/ +Server_Key_Exchange::Server_Key_Exchange(const MemoryRegion<byte>& buf, + const std::string& kex_algo, + const std::string& sig_algo, + Protocol_Version version) : + m_kex_key(0) + { + if(buf.size() < 6) + throw Decoding_Error("Server_Key_Exchange: Packet corrupted"); + + TLS_Data_Reader reader(buf); + + /* + * We really are just serializing things back to what they were + * before, but unfortunately to know where the signature is we need + * to be able to parse the whole thing anyway. + */ + + if(kex_algo == "PSK" || kex_algo == "DHE_PSK" || kex_algo == "ECDHE_PSK") + { + const std::string identity_hint = reader.get_string(2, 0, 65535); + append_tls_length_value(m_params, identity_hint, 2); + } + + if(kex_algo == "DH" || kex_algo == "DHE_PSK") + { + // 3 bigints, DH p, g, Y + + for(size_t i = 0; i != 3; ++i) + { + BigInt v = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + append_tls_length_value(m_params, BigInt::encode(v), 2); + } + } + else if(kex_algo == "ECDH" || kex_algo == "ECDHE_PSK") + { + const byte curve_type = reader.get_byte(); + + if(curve_type != 3) + throw Decoding_Error("Server_Key_Exchange: Server sent non-named ECC curve"); + + const u16bit curve_id = reader.get_u16bit(); + + const std::string name = Supported_Elliptic_Curves::curve_id_to_name(curve_id); + + MemoryVector<byte> ecdh_key = reader.get_range<byte>(1, 1, 255); + + if(name == "") + throw Decoding_Error("Server_Key_Exchange: Server sent unknown named curve " + + to_string(curve_id)); + + m_params.push_back(curve_type); + m_params.push_back(get_byte(0, curve_id)); + m_params.push_back(get_byte(1, curve_id)); + append_tls_length_value(m_params, ecdh_key, 1); + } + else if(kex_algo == "SRP_SHA") + { + // 2 bigints (N,g) then salt, then server B + + const BigInt N = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + const BigInt g = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + MemoryVector<byte> salt = reader.get_range<byte>(1, 1, 255); + const BigInt B = BigInt::decode(reader.get_range<byte>(2, 1, 65535)); + + append_tls_length_value(m_params, BigInt::encode(N), 2); + append_tls_length_value(m_params, BigInt::encode(g), 2); + append_tls_length_value(m_params, salt, 1); + append_tls_length_value(m_params, BigInt::encode(B), 2); + } + else if(kex_algo != "PSK") + throw Decoding_Error("Server_Key_Exchange: Unsupported kex type " + kex_algo); + + if(sig_algo != "") + { + if(version >= Protocol_Version::TLS_V12) + { + m_hash_algo = Signature_Algorithms::hash_algo_name(reader.get_byte()); + m_sig_algo = Signature_Algorithms::sig_algo_name(reader.get_byte()); + } + + m_signature = reader.get_range<byte>(2, 0, 65535); + } + } + + +/** +* Serialize a Server Key Exchange message +*/ +MemoryVector<byte> Server_Key_Exchange::serialize() const + { + MemoryVector<byte> buf = params(); + + if(m_signature.size()) + { + // This should be an explicit version check + if(m_hash_algo != "" && m_sig_algo != "") + { + buf.push_back(Signature_Algorithms::hash_algo_code(m_hash_algo)); + buf.push_back(Signature_Algorithms::sig_algo_code(m_sig_algo)); + } + + append_tls_length_value(buf, m_signature, 2); + } + + return buf; + } + +/** +* Verify a Server Key Exchange message +*/ +bool Server_Key_Exchange::verify(const X509_Certificate& cert, + Handshake_State* state) const + { + std::auto_ptr<Public_Key> key(cert.subject_public_key()); + + std::pair<std::string, Signature_Format> format = + state->understand_sig_format(key.get(), m_hash_algo, m_sig_algo, false); + + PK_Verifier verifier(*key, format.first, format.second); + + verifier.update(state->client_hello->random()); + verifier.update(state->server_hello->random()); + verifier.update(params()); + + return verifier.check_signature(m_signature); + } + +const Private_Key& Server_Key_Exchange::server_kex_key() const + { + BOTAN_ASSERT(m_kex_key, "Key is non-NULL"); + return *m_kex_key; + } +} + +} diff --git a/src/tls/session_ticket.cpp b/src/tls/session_ticket.cpp new file mode 100644 index 000000000..273996a16 --- /dev/null +++ b/src/tls/session_ticket.cpp @@ -0,0 +1,57 @@ +/* +* Session Tickets +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_messages.h> +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_reader.h> +#include <botan/tls_record.h> +#include <botan/loadstor.h> + +namespace Botan { + +namespace TLS { + +New_Session_Ticket::New_Session_Ticket(Record_Writer& writer, + Handshake_Hash& hash, + const MemoryRegion<byte>& ticket, + u32bit lifetime) : + m_ticket_lifetime_hint(lifetime), + m_ticket(ticket) + { + hash.update(writer.send(*this)); + } + +New_Session_Ticket::New_Session_Ticket(Record_Writer& writer, + Handshake_Hash& hash) : + m_ticket_lifetime_hint(0) + { + hash.update(writer.send(*this)); + } + +New_Session_Ticket::New_Session_Ticket(const MemoryRegion<byte>& buf) : + m_ticket_lifetime_hint(0) + { + if(buf.size() < 6) + throw Decoding_Error("Session ticket message too short to be valid"); + + TLS_Data_Reader reader(buf); + + m_ticket_lifetime_hint = reader.get_u32bit(); + m_ticket = reader.get_range<byte>(2, 0, 65535); + } + +MemoryVector<byte> New_Session_Ticket::serialize() const + { + MemoryVector<byte> buf(4); + store_be(m_ticket_lifetime_hint, &buf[0]); + append_tls_length_value(buf, m_ticket, 2); + return buf; + } + +} + +} diff --git a/src/tls/sessions_sqlite/info.txt b/src/tls/sessions_sqlite/info.txt new file mode 100644 index 000000000..c5fc35952 --- /dev/null +++ b/src/tls/sessions_sqlite/info.txt @@ -0,0 +1,11 @@ +define TLS_SQLITE_SESSION_MANAGER + +load_on request + +<libs> +all -> sqlite3 +</libs> + +<requires> +pbkdf2 +</requires> diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp new file mode 100644 index 000000000..cb831aadf --- /dev/null +++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.cpp @@ -0,0 +1,343 @@ +/* +* SQLite TLS Session Manager +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_sqlite_sess_mgr.h> +#include <botan/internal/assert.h> +#include <botan/lookup.h> +#include <botan/hex.h> +#include <botan/time.h> +#include <botan/loadstor.h> +#include <memory> + +#include <sqlite3.h> + +namespace Botan { + +namespace TLS { + +namespace { + +class sqlite3_statement + { + public: + sqlite3_statement(sqlite3* db, const std::string& base_sql) + { + int rc = sqlite3_prepare_v2(db, base_sql.c_str(), -1, &m_stmt, 0); + + if(rc != SQLITE_OK) + throw std::runtime_error("sqlite3_prepare failed " + base_sql + ", code " + to_string(rc)); + } + + void bind(int column, const std::string& val) + { + int rc = sqlite3_bind_text(m_stmt, column, val.c_str(), -1, SQLITE_TRANSIENT); + if(rc != SQLITE_OK) + throw std::runtime_error("sqlite3_bind_text failed, code " + to_string(rc)); + } + + void bind(int column, int val) + { + int rc = sqlite3_bind_int(m_stmt, column, val); + if(rc != SQLITE_OK) + throw std::runtime_error("sqlite3_bind_int failed, code " + to_string(rc)); + } + + void bind(int column, const MemoryRegion<byte>& val) + { + int rc = sqlite3_bind_blob(m_stmt, column, &val[0], val.size(), SQLITE_TRANSIENT); + if(rc != SQLITE_OK) + throw std::runtime_error("sqlite3_bind_text failed, code " + to_string(rc)); + } + + std::pair<const byte*, size_t> get_blob(int column) + { + BOTAN_ASSERT(sqlite3_column_type(m_stmt, 0) == SQLITE_BLOB, + "Return value is a blob"); + + const void* session_blob = sqlite3_column_blob(m_stmt, column); + const int session_blob_size = sqlite3_column_bytes(m_stmt, column); + + BOTAN_ASSERT(session_blob_size >= 0, "Blob size is non-negative"); + + return std::make_pair(static_cast<const byte*>(session_blob), + static_cast<size_t>(session_blob_size)); + } + + size_t get_size_t(int column) + { + BOTAN_ASSERT(sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER, + "Return count is an integer"); + + const int sessions_int = sqlite3_column_int(m_stmt, column); + + BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative"); + + return static_cast<size_t>(sessions_int); + } + + void spin() + { + while(sqlite3_step(m_stmt) == SQLITE_ROW) + {} + } + + int step() + { + return sqlite3_step(m_stmt); + } + + sqlite3_stmt* stmt() { return m_stmt; } + + ~sqlite3_statement() { sqlite3_finalize(m_stmt); } + private: + sqlite3_stmt* m_stmt; + }; + +size_t row_count(sqlite3* db, const std::string& table_name) + { + sqlite3_statement stmt(db, "select count(*) from " + table_name); + + if(stmt.step() == SQLITE_ROW) + return stmt.get_size_t(0); + else + throw std::runtime_error("Querying size of table " + table_name + " failed"); + } + +void create_table(sqlite3* db, const char* table_schema) + { + char* errmsg = 0; + int rc = sqlite3_exec(db, table_schema, 0, 0, &errmsg); + + if(rc != SQLITE_OK) + { + const std::string err_msg = errmsg; + sqlite3_free(errmsg); + sqlite3_close(db); + throw std::runtime_error("sqlite3_exec for table failed - " + err_msg); + } + } + + +SymmetricKey derive_key(const std::string& passphrase, + const byte salt[], + size_t salt_len, + size_t iterations, + size_t& check_val) + { + std::auto_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(SHA-512)")); + + SecureVector<byte> x = pbkdf->derive_key(32 + 3, + passphrase, + salt, salt_len, + iterations).bits_of(); + + check_val = make_u32bit(0, x[0], x[1], x[2]); + return SymmetricKey(&x[3], x.size() - 3); + } + +} + +Session_Manager_SQLite::Session_Manager_SQLite(const std::string& passphrase, + RandomNumberGenerator& rng, + const std::string& db_filename, + size_t max_sessions, + u32bit session_lifetime) : + m_rng(rng), + m_max_sessions(max_sessions), + m_session_lifetime(session_lifetime) + { + int rc = sqlite3_open(db_filename.c_str(), &m_db); + + if(rc) + { + const std::string err_msg = sqlite3_errmsg(m_db); + sqlite3_close(m_db); + throw std::runtime_error("sqlite3_open failed - " + err_msg); + } + + create_table(m_db, + "create table if not exists tls_sessions " + "(" + "session_id TEXT PRIMARY KEY, " + "session_start INTEGER, " + "hostname TEXT, " + "hostport INTEGER, " + "session BLOB" + ")"); + + create_table(m_db, + "create table if not exists tls_sessions_metadata " + "(" + "passphrase_salt BLOB, " + "passphrase_iterations INTEGER, " + "passphrase_check INTEGER " + ")"); + + const size_t salts = row_count(m_db, "tls_sessions_metadata"); + + if(salts == 1) + { + // existing db + sqlite3_statement stmt(m_db, "select * from tls_sessions_metadata"); + + int rc = stmt.step(); + if(rc == SQLITE_ROW) + { + std::pair<const byte*, size_t> salt = stmt.get_blob(0); + const size_t iterations = stmt.get_size_t(1); + const size_t check_val_db = stmt.get_size_t(2); + + size_t check_val_created; + m_session_key = derive_key(passphrase, + salt.first, + salt.second, + iterations, + check_val_created); + + if(check_val_created != check_val_db) + throw std::runtime_error("Session database password not valid"); + } + } + else + { + // maybe just zap the salts + sessions tables in this case? + if(salts != 0) + throw std::runtime_error("Seemingly corrupted database, multiple salts found"); + + // new database case + + MemoryVector<byte> salt = rng.random_vec(16); + const size_t iterations = 64 * 1024; + size_t check_val = 0; + + m_session_key = derive_key(passphrase, &salt[0], salt.size(), + iterations, check_val); + + sqlite3_statement stmt(m_db, "insert into tls_sessions_metadata" + " values(?1, ?2, ?3)"); + + stmt.bind(1, salt); + stmt.bind(2, iterations); + stmt.bind(3, check_val); + + stmt.spin(); + } + } + +Session_Manager_SQLite::~Session_Manager_SQLite() + { + sqlite3_close(m_db); + } + +bool Session_Manager_SQLite::load_from_session_id(const MemoryRegion<byte>& session_id, + Session& session) + { + sqlite3_statement stmt(m_db, "select session from tls_sessions where session_id = ?1"); + + stmt.bind(1, hex_encode(session_id)); + + int rc = stmt.step(); + + while(rc == SQLITE_ROW) + { + std::pair<const byte*, size_t> blob = stmt.get_blob(0); + + try + { + session = Session::decrypt(blob.first, blob.second, m_session_key); + return true; + } + catch(...) + { + } + + rc = stmt.step(); + } + + return false; + } + +bool Session_Manager_SQLite::load_from_host_info(const std::string& hostname, + u16bit port, + Session& session) + { + sqlite3_statement stmt(m_db, "select session from tls_sessions" + " where hostname = ?1 and hostport = ?2" + " order by session_start desc"); + + stmt.bind(1, hostname); + stmt.bind(2, port); + + int rc = stmt.step(); + + while(rc == SQLITE_ROW) + { + std::pair<const byte*, size_t> blob = stmt.get_blob(0); + + try + { + session = Session::decrypt(blob.first, blob.second, m_session_key); + return true; + } + catch(...) + { + } + + rc = stmt.step(); + } + + return false; + } + +void Session_Manager_SQLite::remove_entry(const MemoryRegion<byte>& session_id) + { + sqlite3_statement stmt(m_db, "delete from tls_sessions where session_id = ?1"); + + stmt.bind(1, hex_encode(session_id)); + + stmt.spin(); + } + +void Session_Manager_SQLite::save(const Session& session) + { + sqlite3_statement stmt(m_db, "insert or replace into tls_sessions" + " values(?1, ?2, ?3, ?4, ?5)"); + + stmt.bind(1, hex_encode(session.session_id())); + stmt.bind(2, session.start_time()); + stmt.bind(3, session.sni_hostname()); + stmt.bind(4, 0); + stmt.bind(5, session.encrypt(m_session_key, m_rng)); + + stmt.spin(); + + prune_session_cache(); + } + +void Session_Manager_SQLite::prune_session_cache() + { + sqlite3_statement remove_expired(m_db, "delete from tls_sessions where session_start <= ?1"); + + remove_expired.bind(1, system_time() - m_session_lifetime); + + remove_expired.spin(); + + const size_t sessions = row_count(m_db, "tls_sessions"); + + if(sessions > m_max_sessions) + { + sqlite3_statement remove_some(m_db, "delete from tls_sessions where session_id in " + "(select session_id from tls_sessions limit ?1)"); + + remove_some.bind(1, sessions - m_max_sessions); + remove_some.spin(); + } + } + +} + +} diff --git a/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h new file mode 100644 index 000000000..7ac2caeb2 --- /dev/null +++ b/src/tls/sessions_sqlite/tls_sqlite_sess_mgr.h @@ -0,0 +1,71 @@ +/* +* SQLite TLS Session Manager +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef TLS_SQLITE_SESSION_MANAGER_H__ +#define TLS_SQLITE_SESSION_MANAGER_H__ + +#include <botan/tls_session_manager.h> +#include <botan/rng.h> + +class sqlite3; + +namespace Botan { + +namespace TLS { + +/** +*/ +class BOTAN_DLL Session_Manager_SQLite : public Session_Manager + { + public: + /** + * @param passphrase used to encrypt the session data + * @param db_filename filename of the SQLite database file. + The table names tls_sessions and tls_sessions_metadata + will be used + * @param max_sessions a hint on the maximum number of sessions + * to keep in memory at any one time. (If zero, don't cap) + * @param session_lifetime sessions are expired after this many + * seconds have elapsed from initial handshake. + */ + Session_Manager_SQLite(const std::string& passphrase, + RandomNumberGenerator& rng, + const std::string& db_filename, + size_t max_sessions = 1000, + u32bit session_lifetime = 7200); + + ~Session_Manager_SQLite(); + + bool load_from_session_id(const MemoryRegion<byte>& session_id, + Session& session); + + bool load_from_host_info(const std::string& hostname, u16bit port, + Session& session); + + void remove_entry(const MemoryRegion<byte>& session_id); + + void save(const Session& session_data); + + u32bit session_lifetime() const { return m_session_lifetime; } + private: + Session_Manager_SQLite(const Session_Manager_SQLite&); + Session_Manager_SQLite& operator=(const Session_Manager_SQLite&); + + void prune_session_cache(); + + SymmetricKey m_session_key; + RandomNumberGenerator& m_rng; + size_t m_max_sessions; + u32bit m_session_lifetime; + class sqlite3* m_db; + }; + +} + +} + +#endif diff --git a/src/tls/tls_alert.cpp b/src/tls/tls_alert.cpp new file mode 100644 index 000000000..b526eeac3 --- /dev/null +++ b/src/tls/tls_alert.cpp @@ -0,0 +1,115 @@ +/* +* Alert Message +* (C) 2004-2006,2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_alert.h> +#include <botan/exceptn.h> + +namespace Botan { + +namespace TLS { + +Alert::Alert(const MemoryRegion<byte>& buf) + { + if(buf.size() != 2) + throw Decoding_Error("Alert: Bad size " + to_string(buf.size()) + + " for alert message"); + + if(buf[0] == 1) fatal = false; + else if(buf[0] == 2) fatal = true; + else + throw Decoding_Error("Alert: Bad code for alert level"); + + const byte dc = buf[1]; + + /* + * This is allowed by the specification but is not allocated and we're + * using it internally as a special 'no alert' type. + */ + if(dc == 255) + throw Internal_Error("Alert: description code 255, rejecting"); + + type_code = static_cast<Type>(dc); + } + +std::string Alert::type_string() const + { + switch(type()) + { + case CLOSE_NOTIFY: + return "close_notify"; + case UNEXPECTED_MESSAGE: + return "unexpected_message"; + case BAD_RECORD_MAC: + return "bad_record_mac"; + case DECRYPTION_FAILED: + return "decryption_failed"; + case RECORD_OVERFLOW: + return "record_overflow"; + case DECOMPRESSION_FAILURE: + return "decompression_failure"; + case HANDSHAKE_FAILURE: + return "handshake_failure"; + case NO_CERTIFICATE: + return "no_certificate"; + case BAD_CERTIFICATE: + return "bad_certificate"; + case UNSUPPORTED_CERTIFICATE: + return "unsupported_certificate"; + case CERTIFICATE_REVOKED: + return "certificate_revoked"; + case CERTIFICATE_EXPIRED: + return "certificate_expired"; + case CERTIFICATE_UNKNOWN: + return "certificate_unknown"; + case ILLEGAL_PARAMETER: + return "illegal_parameter"; + case UNKNOWN_CA: + return "unknown_ca"; + case ACCESS_DENIED: + return "access_denied"; + case DECODE_ERROR: + return "decode_error"; + case DECRYPT_ERROR: + return "decrypt_error"; + case EXPORT_RESTRICTION: + return "export_restriction"; + case PROTOCOL_VERSION: + return "protocol_version"; + case INSUFFICIENT_SECURITY: + return "insufficient_security"; + case INTERNAL_ERROR: + return "internal_error"; + case USER_CANCELED: + return "user_canceled"; + case NO_RENEGOTIATION: + return "no_renegotiation"; + + case UNSUPPORTED_EXTENSION: + return "unsupported_extension"; + case UNRECOGNIZED_NAME: + return "unrecognized_name"; + + case UNKNOWN_PSK_IDENTITY: + return "unknown_psk_identity"; + + case NULL_ALERT: + return ""; + } + + /* + * This is effectively the default case for the switch above, but we + * leave it out so that when an alert type is added to the enum the + * compiler can warn us that it is not included in the switch + * statement. + */ + return "unrecognized_alert_" + to_string(type()); + } + + +} + +} diff --git a/src/tls/tls_alert.h b/src/tls/tls_alert.h new file mode 100644 index 000000000..0446a8c30 --- /dev/null +++ b/src/tls/tls_alert.h @@ -0,0 +1,97 @@ +/* +* Alert Message +* (C) 2004-2006,2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_ALERT_H__ +#define BOTAN_TLS_ALERT_H__ + +#include <botan/secmem.h> +#include <string> + +namespace Botan { + +namespace TLS { + +/** +* SSL/TLS Alert Message +*/ +class BOTAN_DLL Alert + { + public: + enum Type { + CLOSE_NOTIFY = 0, + UNEXPECTED_MESSAGE = 10, + BAD_RECORD_MAC = 20, + DECRYPTION_FAILED = 21, + RECORD_OVERFLOW = 22, + DECOMPRESSION_FAILURE = 30, + HANDSHAKE_FAILURE = 40, + NO_CERTIFICATE = 41, // SSLv3 only + 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, + + UNSUPPORTED_EXTENSION = 110, + UNRECOGNIZED_NAME = 112, + + UNKNOWN_PSK_IDENTITY = 115, + + NULL_ALERT = 255 + }; + + /** + * @return true iff this alert is non-empty + */ + bool is_valid() const { return (type_code != NULL_ALERT); } + + /** + * @return if this alert is a fatal one or not + */ + bool is_fatal() const { return fatal; } + + /** + * @return type of alert + */ + Type type() const { return type_code; } + + /** + * @return type of alert + */ + std::string type_string() const; + + /** + * Deserialize an Alert message + * @param buf the serialized alert + */ + Alert(const MemoryRegion<byte>& buf); + + Alert(Type alert_type, bool is_fatal = false) : + fatal(is_fatal), type_code(alert_type) {} + + Alert() : fatal(false), type_code(NULL_ALERT) {} + private: + bool fatal; + Type type_code; + }; + +} + +} + +#endif diff --git a/src/tls/tls_channel.cpp b/src/tls/tls_channel.cpp new file mode 100644 index 000000000..f45ce4bda --- /dev/null +++ b/src/tls/tls_channel.cpp @@ -0,0 +1,289 @@ +/* +* TLS Channels +* (C) 2011-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_channel.h> +#include <botan/internal/tls_handshake_state.h> +#include <botan/internal/tls_messages.h> +#include <botan/internal/assert.h> +#include <botan/loadstor.h> + +namespace Botan { + +namespace TLS { + +Channel::Channel(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete) : + proc_fn(proc_fn), + handshake_fn(handshake_complete), + writer(socket_output_fn), + state(0), + handshake_completed(false), + connection_closed(false) + { + } + +Channel::~Channel() + { + delete state; + state = 0; + } + +size_t Channel::received_data(const byte buf[], size_t buf_size) + { + try + { + while(buf_size) + { + byte rec_type = CONNECTION_CLOSED; + MemoryVector<byte> record; + size_t consumed = 0; + + const size_t needed = reader.add_input(buf, buf_size, + consumed, + rec_type, record); + + buf += consumed; + buf_size -= consumed; + + BOTAN_ASSERT(buf_size == 0 || needed == 0, + "Got a full record or consumed all input"); + + if(buf_size == 0 && needed != 0) + return needed; // need more data to complete record + + if(rec_type == APPLICATION_DATA) + { + if(handshake_completed) + { + /* + * OpenSSL among others sends empty records in versions + * before TLS v1.1 in order to randomize the IV of the + * following record. Avoid spurious callbacks. + */ + if(record.size() > 0) + proc_fn(&record[0], record.size(), Alert()); + } + 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_msg(record); + + alert_notify(alert_msg); + + proc_fn(0, 0, alert_msg); + + if(alert_msg.type() == Alert::CLOSE_NOTIFY) + { + if(connection_closed) + reader.reset(); + else + send_alert(Alert(Alert::CLOSE_NOTIFY)); // reply in kind + } + else if(alert_msg.is_fatal()) + { + // delete state immediately + connection_closed = true; + + delete state; + state = 0; + + writer.reset(); + reader.reset(); + } + } + else + throw Unexpected_Message("Unknown TLS message type " + + to_string(rec_type) + " received"); + } + + return 0; // on a record boundary + } + catch(TLS_Exception& e) + { + send_alert(Alert(e.type(), true)); + throw; + } + catch(Decoding_Error& e) + { + send_alert(Alert(Alert::DECODE_ERROR, true)); + throw; + } + catch(Internal_Error& e) + { + send_alert(Alert(Alert::INTERNAL_ERROR, true)); + throw; + } + catch(std::exception& e) + { + send_alert(Alert(Alert::INTERNAL_ERROR, true)); + throw; + } + } + +/* +* Split up and process handshake messages +*/ +void Channel::read_handshake(byte rec_type, + const MemoryRegion<byte>& rec_buf) + { + if(rec_type == HANDSHAKE) + { + if(!state) + state = new Handshake_State(new Stream_Handshake_Reader); + state->handshake_reader()->add_input(&rec_buf[0], rec_buf.size()); + } + + BOTAN_ASSERT(state, "Handshake message recieved without state in place"); + + while(true) + { + Handshake_Type type = HANDSHAKE_NONE; + + if(rec_type == HANDSHAKE) + { + if(state->handshake_reader()->have_full_record()) + { + std::pair<Handshake_Type, MemoryVector<byte> > msg = + state->handshake_reader()->get_next_record(); + process_handshake_msg(msg.first, msg.second); + } + else + break; + } + else if(rec_type == CHANGE_CIPHER_SPEC) + { + if(state->handshake_reader()->empty() && rec_buf.size() == 1 && rec_buf[0] == 1) + process_handshake_msg(HANDSHAKE_CCS, MemoryVector<byte>()); + else + throw Decoding_Error("Malformed ChangeCipherSpec message"); + } + else + throw Decoding_Error("Unknown message type in handshake processing"); + + if(type == HANDSHAKE_CCS || !state || !state->handshake_reader()->have_full_record()) + break; + } + } + +void Channel::send(const byte buf[], size_t buf_size) + { + if(!is_active()) + throw std::runtime_error("Data cannot be sent on inactive TLS connection"); + + writer.send(APPLICATION_DATA, buf, buf_size); + } + +void Channel::send_alert(const Alert& alert) + { + if(alert.is_valid() && !connection_closed) + { + try + { + writer.send_alert(alert); + } + catch(...) { /* swallow it */ } + } + + if(!connection_closed && (alert.type() == Alert::CLOSE_NOTIFY || alert.is_fatal())) + { + connection_closed = true; + + delete state; + state = 0; + + writer.reset(); + } + } + +void Channel::Secure_Renegotiation_State::update(Client_Hello* client_hello) + { + if(initial_handshake) + { + secure_renegotiation = client_hello->secure_renegotiation(); + } + else + { + if(secure_renegotiation != client_hello->secure_renegotiation()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client changed its mind about secure renegotiation"); + } + + if(client_hello->secure_renegotiation()) + { + const MemoryVector<byte>& data = client_hello->renegotiation_info(); + + if(initial_handshake) + { + if(!data.empty()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client sent renegotiation data on initial handshake"); + } + else + { + if(data != for_client_hello()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client sent bad renegotiation data"); + } + } + } + +void Channel::Secure_Renegotiation_State::update(Server_Hello* server_hello) + { + if(initial_handshake) + { + /* If the client offered but server rejected, then this toggles + * secure_renegotiation to off + */ + secure_renegotiation = server_hello->secure_renegotiation(); + } + else + { + if(secure_renegotiation != server_hello->secure_renegotiation()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server changed its mind about secure renegotiation"); + } + + if(secure_renegotiation) + { + const MemoryVector<byte>& data = server_hello->renegotiation_info(); + + if(initial_handshake) + { + if(!data.empty()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server sent renegotiation data on initial handshake"); + } + else + { + if(data != for_server_hello()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server sent bad renegotiation data"); + } + } + + initial_handshake = false; + } + +void Channel::Secure_Renegotiation_State::update(Finished* client_finished, + Finished* server_finished) + { + client_verify = client_finished->verify_data(); + server_verify = server_finished->verify_data(); + } + +} + +} diff --git a/src/tls/tls_channel.h b/src/tls/tls_channel.h new file mode 100644 index 000000000..53af0bdfc --- /dev/null +++ b/src/tls/tls_channel.h @@ -0,0 +1,139 @@ +/* +* TLS Channel +* (C) 2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_CHANNEL_H__ +#define BOTAN_TLS_CHANNEL_H__ + +#include <botan/tls_policy.h> +#include <botan/tls_record.h> +#include <botan/tls_session.h> +#include <botan/tls_alert.h> +#include <botan/x509cert.h> +#include <vector> + +namespace Botan { + +namespace TLS { + +/** +* Generic interface for TLS endpoint +*/ +class BOTAN_DLL Channel + { + public: + /** + * 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) + */ + virtual size_t received_data(const byte buf[], size_t buf_size); + + /** + * Inject plaintext intended for counterparty + */ + virtual void send(const byte buf[], size_t buf_size); + + /** + * Send a close notification alert + */ + void close() { send_alert(Alert(Alert::CLOSE_NOTIFY)); } + + /** + * @return true iff the connection is active for sending application data + */ + bool is_active() const { return handshake_completed && !is_closed(); } + + /** + * @return true iff the connection has been definitely closed + */ + bool is_closed() const { return connection_closed; } + + /** + * Attempt to renegotiate the session + */ + virtual void renegotiate() = 0; + + /** + * @return certificate chain of the peer (may be empty) + */ + std::vector<X509_Certificate> peer_cert_chain() const { return peer_certs; } + + Channel(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete); + + virtual ~Channel(); + protected: + + /** + * Send a TLS alert message. If the alert is fatal, the + * internal state (keys, etc) will be reset + * @param level is warning or fatal + * @param type is the type of alert + */ + void send_alert(const Alert& alert); + + virtual void read_handshake(byte rec_type, + const MemoryRegion<byte>& rec_buf); + + virtual void process_handshake_msg(Handshake_Type type, + const MemoryRegion<byte>& contents) = 0; + + virtual void alert_notify(const Alert& alert) = 0; + + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn; + std::tr1::function<bool (const Session&)> handshake_fn; + + Record_Writer writer; + Record_Reader reader; + + std::vector<X509_Certificate> peer_certs; + + class Handshake_State* state; + + class Secure_Renegotiation_State + { + public: + Secure_Renegotiation_State() : initial_handshake(true), + secure_renegotiation(false) + {} + + void update(class Client_Hello* client_hello); + void update(class Server_Hello* server_hello); + + void update(class Finished* client_finished, + class Finished* server_finished); + + const MemoryVector<byte>& for_client_hello() const + { return client_verify; } + + MemoryVector<byte> for_server_hello() const + { + MemoryVector<byte> buf = client_verify; + buf += server_verify; + return buf; + } + + bool supported() const { return secure_renegotiation; } + bool renegotiation() const { return !initial_handshake; } + private: + bool initial_handshake; + bool secure_renegotiation; + MemoryVector<byte> client_verify, server_verify; + }; + + Secure_Renegotiation_State secure_renegotiation; + + bool handshake_completed; + bool connection_closed; + }; + +} + +} + +#endif diff --git a/src/tls/tls_ciphersuite.cpp b/src/tls/tls_ciphersuite.cpp new file mode 100644 index 000000000..afe0e68ee --- /dev/null +++ b/src/tls/tls_ciphersuite.cpp @@ -0,0 +1,103 @@ +/* +* TLS Cipher Suite +* (C) 2004-2010,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_ciphersuite.h> +#include <botan/parsing.h> +#include <sstream> +#include <stdexcept> + +namespace Botan { + +namespace TLS { + +Ciphersuite Ciphersuite::by_name(const std::string& name) + { + for(size_t i = 0; i != 65536; ++i) + { + Ciphersuite suite = Ciphersuite::by_id(i); + + if(!suite.valid()) + continue; // not a ciphersuite we know, skip + + if(suite.to_string() == name) + return suite; + } + + return Ciphersuite(); // some unknown ciphersuite + } + +bool Ciphersuite::psk_ciphersuite() const + { + return (kex_algo() == "PSK" || + kex_algo() == "DHE_PSK" || + kex_algo() == "ECDHE_PSK"); + } + +bool Ciphersuite::ecc_ciphersuite() const + { + return (kex_algo() == "ECDH" || sig_algo() == "ECDSA"); + } + +std::string Ciphersuite::to_string() const + { + if(m_cipher_keylen == 0) + throw std::runtime_error("Ciphersuite::to_string - no value set"); + + std::ostringstream out; + + out << "TLS_"; + + if(kex_algo() != "RSA") + { + if(kex_algo() == "DH") + out << "DHE"; + else if(kex_algo() == "ECDH") + out << "ECDHE"; + else + out << kex_algo(); + + out << '_'; + } + + if(sig_algo() == "DSA") + out << "DSS_"; + else if(sig_algo() != "") + out << sig_algo() << '_'; + + out << "WITH_"; + + if(cipher_algo() == "ARC4") + { + out << "RC4_128_"; + } + else + { + if(cipher_algo() == "3DES") + out << "3DES_EDE"; + else if(cipher_algo() == "Camellia") + out << "CAMELLIA_" << Botan::to_string(8*cipher_keylen()); + else + out << replace_char(cipher_algo(), '-', '_'); + + out << "_CBC_"; + } + + if(mac_algo() == "SHA-1") + out << "SHA"; + else if(mac_algo() == "SHA-256") + out << "SHA256"; + else if(mac_algo() == "SHA-384") + out << "SHA384"; + else + out << mac_algo(); + + return out.str(); + } + +} + +} diff --git a/src/tls/tls_ciphersuite.h b/src/tls/tls_ciphersuite.h new file mode 100644 index 000000000..dcb4b6a6f --- /dev/null +++ b/src/tls/tls_ciphersuite.h @@ -0,0 +1,73 @@ +/* +* TLS Cipher Suites +* (C) 2004-2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_CIPHER_SUITES_H__ +#define BOTAN_TLS_CIPHER_SUITES_H__ + +#include <botan/types.h> +#include <string> + +namespace Botan { + +namespace TLS { + +/** +* Ciphersuite Information +*/ +class BOTAN_DLL Ciphersuite + { + public: + /** + * Convert an SSL/TLS ciphersuite to algorithm fields + */ + static Ciphersuite by_id(u16bit suite); + + static Ciphersuite by_name(const std::string& name); + + /** + * Formats the ciphersuite back to an RFC-style ciphersuite string + */ + std::string to_string() const; + + bool psk_ciphersuite() const; + bool ecc_ciphersuite() const; + + std::string kex_algo() const { return m_kex_algo; } + std::string sig_algo() const { return m_sig_algo; } + + std::string cipher_algo() const { return m_cipher_algo; } + std::string mac_algo() const { return m_mac_algo; } + + size_t cipher_keylen() const { return m_cipher_keylen; } + + bool valid() const { return (m_cipher_keylen > 0); } + + Ciphersuite() : m_cipher_keylen(0) {} + + Ciphersuite(const std::string& sig_algo, + const std::string& kex_algo, + const std::string& mac_algo, + const std::string& cipher_algo, + size_t cipher_algo_keylen) : + m_sig_algo(sig_algo), + m_kex_algo(kex_algo), + m_mac_algo(mac_algo), + m_cipher_algo(cipher_algo), + m_cipher_keylen(cipher_algo_keylen) + { + } + + private: + std::string m_sig_algo, m_kex_algo, m_mac_algo, m_cipher_algo; + size_t m_cipher_keylen; + }; + +} + +} + +#endif diff --git a/src/tls/tls_client.cpp b/src/tls/tls_client.cpp new file mode 100644 index 000000000..850d053a2 --- /dev/null +++ b/src/tls/tls_client.cpp @@ -0,0 +1,460 @@ +/* +* TLS Client +* (C) 2004-2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_client.h> +#include <botan/internal/tls_handshake_state.h> +#include <botan/internal/tls_messages.h> +#include <botan/internal/stl_util.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +/* +* TLS Client Constructor +*/ +Client::Client(std::tr1::function<void (const byte[], size_t)> output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_fn, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + const std::string& hostname, + std::tr1::function<std::string (std::vector<std::string>)> next_protocol) : + Channel(output_fn, proc_fn, handshake_fn), + policy(policy), + rng(rng), + session_manager(session_manager), + creds(creds) + { + writer.set_version(Protocol_Version::SSL_V3); + + state = new Handshake_State(new Stream_Handshake_Reader); + state->set_expected_next(SERVER_HELLO); + + state->client_npn_cb = next_protocol; + + const std::string srp_identifier = creds.srp_identifier("tls-client", hostname); + + const bool send_npn_request = static_cast<bool>(next_protocol); + + if(hostname != "") + { + Session session_info; + if(session_manager.load_from_host_info(hostname, 0, session_info)) + { + if(session_info.srp_identifier() == srp_identifier) + { + state->client_hello = new Client_Hello( + writer, + state->hash, + policy, + rng, + session_info, + send_npn_request); + + state->resume_master_secret = session_info.master_secret(); + } + } + } + + if(!state->client_hello) // not resuming + { + state->client_hello = new Client_Hello( + writer, + state->hash, + policy, + rng, + secure_renegotiation.for_client_hello(), + send_npn_request, + hostname, + srp_identifier); + } + + secure_renegotiation.update(state->client_hello); + } + +/* +* Send a new client hello to renegotiate +*/ +void Client::renegotiate() + { + if(state) + return; // currently in handshake + + state = new Handshake_State(new Stream_Handshake_Reader); + state->set_expected_next(SERVER_HELLO); + + state->client_hello = new Client_Hello(writer, state->hash, policy, rng, + secure_renegotiation.for_client_hello()); + + secure_renegotiation.update(state->client_hello); + } + +void Client::alert_notify(const Alert& alert) + { + if(alert.type() == Alert::NO_RENEGOTIATION) + { + if(handshake_completed && state) + { + delete state; + state = 0; + } + } + } + +/* +* Process a handshake message +*/ +void Client::process_handshake_msg(Handshake_Type type, + const MemoryRegion<byte>& contents) + { + if(state == 0) + throw Unexpected_Message("Unexpected handshake message from server"); + + if(type == HELLO_REQUEST) + { + Hello_Request hello_request(contents); + + // Ignore request entirely if we are currently negotiating a handshake + if(state->client_hello) + return; + + if(!secure_renegotiation.supported() && policy.require_secure_renegotiation()) + { + delete state; + state = 0; + + // RFC 5746 section 4.2 + send_alert(Alert(Alert::NO_RENEGOTIATION)); + return; + } + + state->client_hello = new Client_Hello(writer, state->hash, policy, rng, + secure_renegotiation.for_client_hello()); + secure_renegotiation.update(state->client_hello); + + state->set_expected_next(SERVER_HELLO); + + return; + } + + state->confirm_transition_to(type); + + if(type != HANDSHAKE_CCS && type != FINISHED) + state->hash.update(type, contents); + + if(type == SERVER_HELLO) + { + state->server_hello = new Server_Hello(contents); + + if(!state->client_hello->offered_suite(state->server_hello->ciphersuite())) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server replied with ciphersuite we didn't send"); + } + + if(!value_exists(state->client_hello->compression_methods(), + state->server_hello->compression_method())) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server replied with compression method we didn't send"); + } + + if(!state->client_hello->next_protocol_notification() && + state->server_hello->next_protocol_notification()) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server sent next protocol but we didn't request it"); + } + + if(state->server_hello->supports_session_ticket()) + { + if(!state->client_hello->supports_session_ticket()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server sent session ticket extension but we did not"); + } + + state->set_version(state->server_hello->version()); + + writer.set_version(state->version()); + reader.set_version(state->version()); + + secure_renegotiation.update(state->server_hello); + + state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite()); + + const bool server_returned_same_session_id = + !state->server_hello->session_id().empty() && + (state->server_hello->session_id() == state->client_hello->session_id()); + + if(server_returned_same_session_id) + { + // successful resumption + + /* + * In this case, we offered the version used in the original + * session, and the server must resume with the same version. + */ + if(state->server_hello->version() != state->client_hello->version()) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Server resumed session but with wrong version"); + + state->keys = Session_Keys(state, + state->resume_master_secret, + true); + + if(state->server_hello->supports_session_ticket()) + state->set_expected_next(NEW_SESSION_TICKET); + else + state->set_expected_next(HANDSHAKE_CCS); + } + else + { + // new session + + if(state->version() > state->client_hello->version()) + { + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client: Server replied with bad version"); + } + + if(state->version() < policy.min_version()) + { + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client: Server is too old for specified policy"); + } + + if(state->suite.sig_algo() != "") + { + state->set_expected_next(CERTIFICATE); + } + else if(state->suite.kex_algo() == "PSK") + { + /* PSK is anonymous so no certificate/cert req message is + ever sent. The server may or may not send a server kex, + depending on if it has an identity hint for us. + + (EC)DHE_PSK always sends a server key exchange for the + DH exchange portion. + */ + + state->set_expected_next(SERVER_KEX); + state->set_expected_next(SERVER_HELLO_DONE); + } + else if(state->suite.kex_algo() != "RSA") + { + state->set_expected_next(SERVER_KEX); + } + else + { + state->set_expected_next(CERTIFICATE_REQUEST); // optional + state->set_expected_next(SERVER_HELLO_DONE); + } + } + } + else if(type == CERTIFICATE) + { + if(state->suite.kex_algo() != "RSA") + { + state->set_expected_next(SERVER_KEX); + } + else + { + state->set_expected_next(CERTIFICATE_REQUEST); // optional + state->set_expected_next(SERVER_HELLO_DONE); + } + + state->server_certs = new Certificate(contents); + + peer_certs = state->server_certs->cert_chain(); + if(peer_certs.size() == 0) + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Client: No certificates sent by server"); + + try + { + const std::string hostname = state->client_hello->sni_hostname(); + creds.verify_certificate_chain("tls-client", hostname, peer_certs); + } + catch(std::exception& e) + { + throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); + } + + std::auto_ptr<Public_Key> peer_key(peer_certs[0].subject_public_key()); + + if(peer_key->algo_name() != state->suite.sig_algo()) + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, + "Certificate key type did not match ciphersuite"); + } + else if(type == SERVER_KEX) + { + state->set_expected_next(CERTIFICATE_REQUEST); // optional + state->set_expected_next(SERVER_HELLO_DONE); + + state->server_kex = new Server_Key_Exchange(contents, + state->suite.kex_algo(), + state->suite.sig_algo(), + state->version()); + + if(state->suite.sig_algo() != "") + { + if(!state->server_kex->verify(peer_certs[0], state)) + { + throw TLS_Exception(Alert::DECRYPT_ERROR, + "Bad signature on server key exchange"); + } + } + } + else if(type == CERTIFICATE_REQUEST) + { + state->set_expected_next(SERVER_HELLO_DONE); + state->cert_req = new Certificate_Req(contents, state->version()); + } + else if(type == SERVER_HELLO_DONE) + { + state->server_hello_done = new Server_Hello_Done(contents); + + if(state->received_handshake_msg(CERTIFICATE_REQUEST)) + { + const std::vector<std::string>& types = + state->cert_req->acceptable_cert_types(); + + std::vector<X509_Certificate> client_certs = + creds.cert_chain(types, + "tls-client", + state->client_hello->sni_hostname()); + + state->client_certs = new Certificate(writer, + state->hash, + client_certs); + } + + state->client_kex = + new Client_Key_Exchange(writer, + state, + creds, + peer_certs, + state->client_hello->sni_hostname(), + rng); + + state->keys = Session_Keys(state, + state->client_kex->pre_master_secret(), + false); + + if(state->received_handshake_msg(CERTIFICATE_REQUEST) && + !state->client_certs->empty()) + { + Private_Key* private_key = + creds.private_key_for(state->client_certs->cert_chain()[0], + "tls-client", + state->client_hello->sni_hostname()); + + state->client_verify = new Certificate_Verify(writer, + state, + rng, + private_key); + } + + writer.send(CHANGE_CIPHER_SPEC, 1); + + writer.activate(CLIENT, state->suite, state->keys, + state->server_hello->compression_method()); + + if(state->server_hello->next_protocol_notification()) + { + const std::string protocol = + state->client_npn_cb(state->server_hello->next_protocols()); + + state->next_protocol = new Next_Protocol(writer, state->hash, protocol); + } + + state->client_finished = new Finished(writer, state, CLIENT); + + if(state->server_hello->supports_session_ticket()) + state->set_expected_next(NEW_SESSION_TICKET); + else + state->set_expected_next(HANDSHAKE_CCS); + } + else if(type == NEW_SESSION_TICKET) + { + state->new_session_ticket = new New_Session_Ticket(contents); + + state->set_expected_next(HANDSHAKE_CCS); + } + else if(type == HANDSHAKE_CCS) + { + state->set_expected_next(FINISHED); + + reader.activate(CLIENT, state->suite, state->keys, + state->server_hello->compression_method()); + } + else if(type == FINISHED) + { + state->set_expected_next(HELLO_REQUEST); + + state->server_finished = new Finished(contents); + + if(!state->server_finished->verify(state, SERVER)) + throw TLS_Exception(Alert::DECRYPT_ERROR, + "Finished message didn't verify"); + + state->hash.update(type, contents); + + if(!state->client_finished) // session resume case + { + writer.send(CHANGE_CIPHER_SPEC, 1); + + writer.activate(CLIENT, state->suite, state->keys, + state->server_hello->compression_method()); + + state->client_finished = new Finished(writer, state, CLIENT); + } + + secure_renegotiation.update(state->client_finished, state->server_finished); + + MemoryVector<byte> session_id = state->server_hello->session_id(); + + const MemoryRegion<byte>& session_ticket = state->session_ticket(); + + if(session_id.empty() && !session_ticket.empty()) + session_id = make_hello_random(rng); + + Session session_info( + session_id, + state->keys.master_secret(), + state->server_hello->version(), + state->server_hello->ciphersuite(), + state->server_hello->compression_method(), + CLIENT, + secure_renegotiation.supported(), + state->server_hello->fragment_size(), + peer_certs, + session_ticket, + state->client_hello->sni_hostname(), + "" + ); + + if(handshake_fn(session_info)) + session_manager.save(session_info); + else + session_manager.remove_entry(session_info.session_id()); + + delete state; + state = 0; + handshake_completed = 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..5c2692cd6 --- /dev/null +++ b/src/tls/tls_client.h @@ -0,0 +1,73 @@ +/* +* 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_channel.h> +#include <botan/tls_session_manager.h> +#include <botan/credentials_manager.h> +#include <vector> + +namespace Botan { + +namespace TLS { + +/** +* SSL/TLS Client +*/ +class BOTAN_DLL Client : public Channel + { + public: + /** + * Set up a new TLS client session + * @param socket_output_fn is called with data for the outbound socket + * @param proc_fn is called when new data (application or alerts) is received + * @param handshake_complete is called when a handshake is completed + * @param session_manager manages session state + * @param creds manages application/user credentials + * @param policy specifies other connection policy information + * @param rng a random number generator + * @param servername the server's DNS name, if known + * @param next_protocol allows the client to specify what the next + * protocol will be. For more information read + * http://technotes.googlecode.com/git/nextprotoneg.html. + * + * If the function is not empty, NPN will be negotiated + * and if the server supports NPN the function will be + * called with the list of protocols the server advertised; + * the client should return the protocol it would like to use. + */ + Client(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + const std::string& servername = "", + std::tr1::function<std::string (std::vector<std::string>)> next_protocol = + std::tr1::function<std::string (std::vector<std::string>)>()); + + void renegotiate(); + private: + void process_handshake_msg(Handshake_Type type, + const MemoryRegion<byte>& contents); + + void alert_notify(const Alert& alert); + + const Policy& policy; + RandomNumberGenerator& rng; + Session_Manager& session_manager; + Credentials_Manager& creds; + }; + +} + +} + +#endif diff --git a/src/ssl/tls_exceptn.h b/src/tls/tls_exceptn.h index 37b9c0d27..ad19c6c9d 100644 --- a/src/ssl/tls_exceptn.h +++ b/src/tls/tls_exceptn.h @@ -9,24 +9,26 @@ #define BOTAN_TLS_EXCEPTION_H__ #include <botan/exceptn.h> -#include <botan/tls_magic.h> +#include <botan/tls_alert.h> namespace Botan { +namespace TLS { + /** * Exception Base Class */ class BOTAN_DLL TLS_Exception : public Exception { public: - Alert_Type type() const throw() { return alert_type; } + Alert::Type type() const throw() { return alert_type; } - TLS_Exception(Alert_Type type, + TLS_Exception(Alert::Type type, const std::string& err_msg = "Unknown error") : Exception(err_msg), alert_type(type) {} private: - Alert_Type alert_type; + Alert::Type alert_type; }; /** @@ -35,9 +37,11 @@ class BOTAN_DLL TLS_Exception : public Exception struct BOTAN_DLL Unexpected_Message : public TLS_Exception { Unexpected_Message(const std::string& err) : - TLS_Exception(UNEXPECTED_MESSAGE, err) {} + TLS_Exception(Alert::UNEXPECTED_MESSAGE, err) {} }; } +} + #endif diff --git a/src/tls/tls_extensions.cpp b/src/tls/tls_extensions.cpp new file mode 100644 index 000000000..59ab64374 --- /dev/null +++ b/src/tls/tls_extensions.cpp @@ -0,0 +1,520 @@ +/* +* TLS Extensions +* (C) 2011,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_extensions.h> +#include <botan/internal/tls_reader.h> +#include <botan/tls_exceptn.h> + +namespace Botan { + +namespace TLS { + +namespace { + +Extension* make_extension(TLS_Data_Reader& reader, + u16bit code, + u16bit size) + { + switch(code) + { + case TLSEXT_SERVER_NAME_INDICATION: + return new Server_Name_Indicator(reader, size); + + case TLSEXT_MAX_FRAGMENT_LENGTH: + return new Maximum_Fragment_Length(reader, size); + + case TLSEXT_SRP_IDENTIFIER: + return new SRP_Identifier(reader, size); + + case TLSEXT_USABLE_ELLIPTIC_CURVES: + return new Supported_Elliptic_Curves(reader, size); + + case TLSEXT_SAFE_RENEGOTIATION: + return new Renegotation_Extension(reader, size); + + case TLSEXT_SIGNATURE_ALGORITHMS: + return new Signature_Algorithms(reader, size); + + case TLSEXT_NEXT_PROTOCOL: + return new Next_Protocol_Notification(reader, size); + + case TLSEXT_SESSION_TICKET: + return new Session_Ticket(reader, size); + + default: + return 0; // not known + } + } + +} + +Extensions::Extensions(TLS_Data_Reader& reader) + { + if(reader.has_remaining()) + { + const u16bit all_extn_size = reader.get_u16bit(); + + if(reader.remaining_bytes() != all_extn_size) + throw Decoding_Error("Bad extension size"); + + while(reader.has_remaining()) + { + const u16bit extension_code = reader.get_u16bit(); + const u16bit extension_size = reader.get_u16bit(); + + Extension* extn = make_extension(reader, + extension_code, + extension_size); + + if(extn) + this->add(extn); + else // unknown/unhandled extension + reader.discard_next(extension_size); + } + } + } + +MemoryVector<byte> Extensions::serialize() const + { + MemoryVector<byte> buf(2); // 2 bytes for length field + + for(std::map<Handshake_Extension_Type, Extension*>::const_iterator i = extensions.begin(); + i != extensions.end(); ++i) + { + if(i->second->empty()) + continue; + + const u16bit extn_code = i->second->type(); + + MemoryVector<byte> extn_val = i->second->serialize(); + + buf.push_back(get_byte(0, extn_code)); + buf.push_back(get_byte(1, extn_code)); + + buf.push_back(get_byte<u16bit>(0, extn_val.size())); + buf.push_back(get_byte<u16bit>(1, extn_val.size())); + + buf += extn_val; + } + + const u16bit extn_size = buf.size() - 2; + + buf[0] = get_byte(0, extn_size); + buf[1] = get_byte(1, extn_size); + + // avoid sending a completely empty extensions block + if(buf.size() == 2) + return MemoryVector<byte>(); + + return buf; + } + +Extensions::~Extensions() + { + for(std::map<Handshake_Extension_Type, Extension*>::const_iterator i = extensions.begin(); + i != extensions.end(); ++i) + { + delete i->second; + } + + extensions.clear(); + } + +Server_Name_Indicator::Server_Name_Indicator(TLS_Data_Reader& reader, + u16bit extension_size) + { + /* + * This is used by the server to confirm that it knew the name + */ + if(extension_size == 0) + return; + + u16bit name_bytes = reader.get_u16bit(); + + if(name_bytes + 2 != extension_size) + throw Decoding_Error("Bad encoding of SNI extension"); + + while(name_bytes) + { + byte name_type = reader.get_byte(); + name_bytes--; + + if(name_type == 0) // DNS + { + sni_host_name = reader.get_string(2, 1, 65535); + name_bytes -= (2 + sni_host_name.size()); + } + else // some other unknown name type + { + reader.discard_next(name_bytes); + name_bytes = 0; + } + } + } + +MemoryVector<byte> Server_Name_Indicator::serialize() const + { + MemoryVector<byte> buf; + + size_t name_len = sni_host_name.size(); + + buf.push_back(get_byte<u16bit>(0, name_len+3)); + buf.push_back(get_byte<u16bit>(1, name_len+3)); + buf.push_back(0); // DNS + + buf.push_back(get_byte<u16bit>(0, name_len)); + buf.push_back(get_byte<u16bit>(1, name_len)); + + buf += std::make_pair( + reinterpret_cast<const byte*>(sni_host_name.data()), + sni_host_name.size()); + + return buf; + } + +SRP_Identifier::SRP_Identifier(TLS_Data_Reader& reader, + u16bit extension_size) + { + srp_identifier = reader.get_string(1, 1, 255); + + if(srp_identifier.size() + 1 != extension_size) + throw Decoding_Error("Bad encoding for SRP identifier extension"); + } + +MemoryVector<byte> SRP_Identifier::serialize() const + { + MemoryVector<byte> buf; + + const byte* srp_bytes = + reinterpret_cast<const byte*>(srp_identifier.data()); + + append_tls_length_value(buf, srp_bytes, srp_identifier.size(), 1); + + return buf; + } + +Renegotation_Extension::Renegotation_Extension(TLS_Data_Reader& reader, + u16bit extension_size) + { + reneg_data = reader.get_range<byte>(1, 0, 255); + + if(reneg_data.size() + 1 != extension_size) + throw Decoding_Error("Bad encoding for secure renegotiation extn"); + } + +MemoryVector<byte> Renegotation_Extension::serialize() const + { + MemoryVector<byte> buf; + append_tls_length_value(buf, reneg_data, 1); + return buf; + } + +size_t Maximum_Fragment_Length::fragment_size() const + { + switch(val) + { + case 1: + return 512; + case 2: + return 1024; + case 3: + return 2048; + case 4: + return 4096; + default: + throw TLS_Exception(Alert::ILLEGAL_PARAMETER, + "Bad value in maximum fragment extension"); + } + } + +Maximum_Fragment_Length::Maximum_Fragment_Length(size_t max_fragment) + { + if(max_fragment == 512) + val = 1; + else if(max_fragment == 1024) + val = 2; + else if(max_fragment == 2048) + val = 3; + else if(max_fragment == 4096) + val = 4; + else + throw std::invalid_argument("Bad setting " + to_string(max_fragment) + + " for maximum fragment size"); + } + +Maximum_Fragment_Length::Maximum_Fragment_Length(TLS_Data_Reader& reader, + u16bit extension_size) + { + if(extension_size != 1) + throw Decoding_Error("Bad size for maximum fragment extension"); + val = reader.get_byte(); + } + +Next_Protocol_Notification::Next_Protocol_Notification(TLS_Data_Reader& reader, + u16bit extension_size) + { + if(extension_size == 0) + return; // empty extension + + size_t bytes_remaining = extension_size; + + while(bytes_remaining) + { + const std::string p = reader.get_string(1, 0, 255); + + if(bytes_remaining < p.size() + 1) + throw Decoding_Error("Bad encoding for next protocol extension"); + + bytes_remaining -= (p.size() + 1); + + m_protocols.push_back(p); + } + } + +MemoryVector<byte> Next_Protocol_Notification::serialize() const + { + MemoryVector<byte> buf; + + for(size_t i = 0; i != m_protocols.size(); ++i) + { + const std::string p = m_protocols[i]; + + if(p != "") + append_tls_length_value(buf, + reinterpret_cast<const byte*>(p.data()), + p.size(), + 1); + } + + return buf; + } + +std::string Supported_Elliptic_Curves::curve_id_to_name(u16bit id) + { + switch(id) + { + case 15: + return "secp160k1"; + case 16: + return "secp160r1"; + case 17: + return "secp160r2"; + case 18: + return "secp192k1"; + case 19: + return "secp192r1"; + case 20: + return "secp224k1"; + case 21: + return "secp224r1"; + case 22: + return "secp256k1"; + case 23: + return "secp256r1"; + case 24: + return "secp384r1"; + case 25: + return "secp521r1"; + default: + return ""; // something we don't know or support + } + } + +u16bit Supported_Elliptic_Curves::name_to_curve_id(const std::string& name) + { + if(name == "secp160k1") + return 15; + if(name == "secp160r1") + return 16; + if(name == "secp160r2") + return 17; + if(name == "secp192k1") + return 18; + if(name == "secp192r1") + return 19; + if(name == "secp224k1") + return 20; + if(name == "secp224r1") + return 21; + if(name == "secp256k1") + return 22; + if(name == "secp256r1") + return 23; + if(name == "secp384r1") + return 24; + if(name == "secp521r1") + return 25; + + throw Invalid_Argument("name_to_curve_id unknown name " + name); + } + +MemoryVector<byte> Supported_Elliptic_Curves::serialize() const + { + MemoryVector<byte> buf(2); + + for(size_t i = 0; i != m_curves.size(); ++i) + { + const u16bit id = name_to_curve_id(m_curves[i]); + buf.push_back(get_byte(0, id)); + buf.push_back(get_byte(1, id)); + } + + buf[0] = get_byte<u16bit>(0, buf.size()-2); + buf[1] = get_byte<u16bit>(1, buf.size()-2); + + return buf; + } + +Supported_Elliptic_Curves::Supported_Elliptic_Curves(TLS_Data_Reader& reader, + u16bit extension_size) + { + u16bit len = reader.get_u16bit(); + + if(len + 2 != extension_size) + throw Decoding_Error("Inconsistent length field in elliptic curve list"); + + if(len % 2 == 1) + throw Decoding_Error("Elliptic curve list of strange size"); + + len /= 2; + + for(size_t i = 0; i != len; ++i) + { + const u16bit id = reader.get_u16bit(); + const std::string name = curve_id_to_name(id); + + if(name != "") + m_curves.push_back(name); + } + } + +std::string Signature_Algorithms::hash_algo_name(byte code) + { + switch(code) + { + // code 1 is MD5 - ignore it + + case 2: + return "SHA-1"; + case 3: + return "SHA-224"; + case 4: + return "SHA-256"; + case 5: + return "SHA-384"; + case 6: + return "SHA-512"; + default: + return ""; + } + } + +byte Signature_Algorithms::hash_algo_code(const std::string& name) + { + if(name == "SHA-1") + return 2; + + if(name == "SHA-224") + return 3; + + if(name == "SHA-256") + return 4; + + if(name == "SHA-384") + return 5; + + if(name == "SHA-512") + return 6; + + throw Internal_Error("Unknown hash ID " + name + " for signature_algorithms"); + } + +std::string Signature_Algorithms::sig_algo_name(byte code) + { + switch(code) + { + case 1: + return "RSA"; + case 2: + return "DSA"; + case 3: + return "ECDSA"; + default: + return ""; + } + } + +byte Signature_Algorithms::sig_algo_code(const std::string& name) + { + if(name == "RSA") + return 1; + + if(name == "DSA") + return 2; + + if(name == "ECDSA") + return 3; + + throw Internal_Error("Unknown sig ID " + name + " for signature_algorithms"); + } + +MemoryVector<byte> Signature_Algorithms::serialize() const + { + MemoryVector<byte> buf(2); + + for(size_t i = 0; i != m_supported_algos.size(); ++i) + { + try + { + const byte hash_code = hash_algo_code(m_supported_algos[i].first); + const byte sig_code = sig_algo_code(m_supported_algos[i].second); + + buf.push_back(hash_code); + buf.push_back(sig_code); + } + catch(...) + {} + } + + buf[0] = get_byte<u16bit>(0, buf.size()-2); + buf[1] = get_byte<u16bit>(1, buf.size()-2); + + return buf; + } + +Signature_Algorithms::Signature_Algorithms(TLS_Data_Reader& reader, + u16bit extension_size) + { + u16bit len = reader.get_u16bit(); + + if(len + 2 != extension_size) + throw Decoding_Error("Bad encoding on signature algorithms extension"); + + while(len) + { + const std::string hash_code = hash_algo_name(reader.get_byte()); + const std::string sig_code = sig_algo_name(reader.get_byte()); + + len -= 2; + + // If not something we know, ignore it completely + if(hash_code == "" || sig_code == "") + continue; + + m_supported_algos.push_back(std::make_pair(hash_code, sig_code)); + } + } + +Session_Ticket::Session_Ticket(TLS_Data_Reader& reader, + u16bit extension_size) + { + m_ticket = reader.get_elem<byte, MemoryVector<byte> >(extension_size); + } + +} + +} diff --git a/src/tls/tls_extensions.h b/src/tls/tls_extensions.h new file mode 100644 index 000000000..6a97d2560 --- /dev/null +++ b/src/tls/tls_extensions.h @@ -0,0 +1,354 @@ +/* +* TLS Extensions +* (C) 2011-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_EXTENSIONS_H__ +#define BOTAN_TLS_EXTENSIONS_H__ + +#include <botan/secmem.h> +#include <botan/tls_magic.h> +#include <vector> +#include <string> +#include <map> + +namespace Botan { + +namespace TLS { + +class TLS_Data_Reader; + +enum 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_CERTIFICATE_TYPES = 9, + TLSEXT_USABLE_ELLIPTIC_CURVES = 10, + TLSEXT_EC_POINT_FORMATS = 11, + TLSEXT_SRP_IDENTIFIER = 12, + TLSEXT_SIGNATURE_ALGORITHMS = 13, + + TLSEXT_SESSION_TICKET = 35, + + TLSEXT_NEXT_PROTOCOL = 13172, + + TLSEXT_SAFE_RENEGOTIATION = 65281, +}; + +/** +* Base class representing a TLS extension of some kind +*/ +class Extension + { + public: + virtual Handshake_Extension_Type type() const = 0; + + virtual MemoryVector<byte> serialize() const = 0; + + virtual bool empty() const = 0; + + virtual ~Extension() {} + }; + +/** +* Server Name Indicator extension (RFC 3546) +*/ +class Server_Name_Indicator : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SERVER_NAME_INDICATION; } + + Handshake_Extension_Type type() const { return static_type(); } + + Server_Name_Indicator(const std::string& host_name) : + sni_host_name(host_name) {} + + Server_Name_Indicator(TLS_Data_Reader& reader, + u16bit extension_size); + + std::string host_name() const { return sni_host_name; } + + MemoryVector<byte> serialize() const; + + bool empty() const { return sni_host_name == ""; } + private: + std::string sni_host_name; + }; + +/** +* SRP identifier extension (RFC 5054) +*/ +class SRP_Identifier : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SRP_IDENTIFIER; } + + Handshake_Extension_Type type() const { return static_type(); } + + SRP_Identifier(const std::string& identifier) : + srp_identifier(identifier) {} + + SRP_Identifier(TLS_Data_Reader& reader, + u16bit extension_size); + + std::string identifier() const { return srp_identifier; } + + MemoryVector<byte> serialize() const; + + bool empty() const { return srp_identifier == ""; } + private: + std::string srp_identifier; + }; + +/** +* Renegotiation Indication Extension (RFC 5746) +*/ +class Renegotation_Extension : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SAFE_RENEGOTIATION; } + + Handshake_Extension_Type type() const { return static_type(); } + + Renegotation_Extension() {} + + Renegotation_Extension(const MemoryRegion<byte>& bits) : + reneg_data(bits) {} + + Renegotation_Extension(TLS_Data_Reader& reader, + u16bit extension_size); + + const MemoryVector<byte>& renegotiation_info() const + { return reneg_data; } + + MemoryVector<byte> serialize() const; + + bool empty() const { return false; } // always send this + private: + MemoryVector<byte> reneg_data; + }; + +/** +* Maximum Fragment Length Negotiation Extension (RFC 4366 sec 3.2) +*/ +class Maximum_Fragment_Length : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_MAX_FRAGMENT_LENGTH; } + + Handshake_Extension_Type type() const { return static_type(); } + + bool empty() const { return val != 0; } + + size_t fragment_size() const; + + MemoryVector<byte> serialize() const + { + return MemoryVector<byte>(&val, 1); + } + + /** + * @param max_fragment specifies what maximum fragment size to + * advertise. Currently must be one of 512, 1024, 2048, or + * 4096. + */ + Maximum_Fragment_Length(size_t max_fragment); + + Maximum_Fragment_Length(TLS_Data_Reader& reader, + u16bit extension_size); + + private: + byte val; + }; + +/** +* Next Protocol Negotiation +* http://technotes.googlecode.com/git/nextprotoneg.html +* +* This implementation requires the semantics defined in the Google +* spec (implemented in Chromium); the internet draft leaves the format +* unspecified. +*/ +class Next_Protocol_Notification : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_NEXT_PROTOCOL; } + + Handshake_Extension_Type type() const { return static_type(); } + + const std::vector<std::string>& protocols() const + { return m_protocols; } + + /** + * Empty extension, used by client + */ + Next_Protocol_Notification() {} + + /** + * List of protocols, used by server + */ + Next_Protocol_Notification(const std::vector<std::string>& protocols) : + m_protocols(protocols) {} + + Next_Protocol_Notification(TLS_Data_Reader& reader, + u16bit extension_size); + + MemoryVector<byte> serialize() const; + + bool empty() const { return false; } + private: + std::vector<std::string> m_protocols; + }; + +class Session_Ticket : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SESSION_TICKET; } + + Handshake_Extension_Type type() const { return static_type(); } + + const MemoryVector<byte>& contents() const { return m_ticket; } + + /** + * Create empty extension, used by both client and server + */ + Session_Ticket() {} + + /** + * Extension with ticket, used by client + */ + Session_Ticket(const MemoryRegion<byte>& session_ticket) : + m_ticket(session_ticket) {} + + /** + * Deserialize a session ticket + */ + Session_Ticket(TLS_Data_Reader& reader, u16bit extension_size); + + MemoryVector<byte> serialize() const { return m_ticket; } + + bool empty() const { return false; } + private: + MemoryVector<byte> m_ticket; + }; + +/** +* Supported Elliptic Curves Extension (RFC 4492) +*/ +class Supported_Elliptic_Curves : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_USABLE_ELLIPTIC_CURVES; } + + Handshake_Extension_Type type() const { return static_type(); } + + static std::string curve_id_to_name(u16bit id); + static u16bit name_to_curve_id(const std::string& name); + + const std::vector<std::string>& curves() const { return m_curves; } + + MemoryVector<byte> serialize() const; + + Supported_Elliptic_Curves(const std::vector<std::string>& curves) : + m_curves(curves) {} + + Supported_Elliptic_Curves(TLS_Data_Reader& reader, + u16bit extension_size); + + bool empty() const { return m_curves.empty(); } + private: + std::vector<std::string> m_curves; + }; + +/** +* Signature Algorithms Extension for TLS 1.2 (RFC 5246) +*/ +class Signature_Algorithms : public Extension + { + public: + static Handshake_Extension_Type static_type() + { return TLSEXT_SIGNATURE_ALGORITHMS; } + + Handshake_Extension_Type type() const { return static_type(); } + + static std::string hash_algo_name(byte code); + static byte hash_algo_code(const std::string& name); + + static std::string sig_algo_name(byte code); + static byte sig_algo_code(const std::string& name); + + std::vector<std::pair<std::string, std::string> > + supported_signature_algorthms() const + { + return m_supported_algos; + } + + MemoryVector<byte> serialize() const; + + bool empty() const { return false; } + + Signature_Algorithms(const std::vector<std::pair<std::string, std::string> >& algos) : + m_supported_algos(algos) {} + + Signature_Algorithms(TLS_Data_Reader& reader, + u16bit extension_size); + private: + std::vector<std::pair<std::string, std::string> > m_supported_algos; + }; + +/** +* Represents a block of extensions in a hello message +*/ +class Extensions + { + public: + template<typename T> + T* get() const + { + Handshake_Extension_Type type = T::static_type(); + + std::map<Handshake_Extension_Type, Extension*>::const_iterator i = + extensions.find(type); + + if(i != extensions.end()) + return dynamic_cast<T*>(i->second); + return 0; + } + + void add(Extension* extn) + { + delete extensions[extn->type()]; // or hard error if already exists? + extensions[extn->type()] = extn; + } + + MemoryVector<byte> serialize() const; + + Extensions() {} + + Extensions(TLS_Data_Reader& reader); // deserialize + + ~Extensions(); + private: + Extensions(const Extensions&) {} + Extensions& operator=(const Extensions&) { return (*this); } + + std::map<Handshake_Extension_Type, Extension*> extensions; + }; + +} + +} + +#endif diff --git a/src/tls/tls_handshake_hash.cpp b/src/tls/tls_handshake_hash.cpp new file mode 100644 index 000000000..d0c74136b --- /dev/null +++ b/src/tls/tls_handshake_hash.cpp @@ -0,0 +1,103 @@ +/* +* TLS Handshake Hash +* (C) 2004-2006,2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_handshake_hash.h> +#include <botan/tls_exceptn.h> +#include <botan/libstate.h> +#include <botan/hash.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +void Handshake_Hash::update(Handshake_Type handshake_type, + const MemoryRegion<byte>& handshake_msg) + { + update(static_cast<byte>(handshake_type)); + + const size_t record_length = handshake_msg.size(); + for(size_t i = 0; i != 3; i++) + update(get_byte<u32bit>(i+1, record_length)); + + update(handshake_msg); + } + +/** +* Return a TLS Handshake Hash +*/ +SecureVector<byte> Handshake_Hash::final(Protocol_Version version, + const std::string& mac_algo) + { + Algorithm_Factory& af = global_state().algorithm_factory(); + + std::auto_ptr<HashFunction> hash; + + if(version == Protocol_Version::TLS_V10 || version == Protocol_Version::TLS_V11) + { + hash.reset(af.make_hash_function("TLS.Digest.0")); + } + else if(version == Protocol_Version::TLS_V12) + { + if(mac_algo == "MD5" || mac_algo == "SHA-1" || mac_algo == "SHA-256") + hash.reset(af.make_hash_function("SHA-256")); + else + hash.reset(af.make_hash_function(mac_algo)); + } + else + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Unknown version for handshake hashes"); + + hash->update(data); + return hash->final(); + } + +/** +* Return a SSLv3 Handshake Hash +*/ +SecureVector<byte> Handshake_Hash::final_ssl3(const MemoryRegion<byte>& secret) + { + const byte PAD_INNER = 0x36, PAD_OUTER = 0x5C; + + Algorithm_Factory& af = global_state().algorithm_factory(); + + std::auto_ptr<HashFunction> md5(af.make_hash_function("MD5")); + std::auto_ptr<HashFunction> sha1(af.make_hash_function("SHA-1")); + + 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/ssl/tls_handshake_hash.h b/src/tls/tls_handshake_hash.h index cea612a71..c13f97aa8 100644 --- a/src/ssl/tls_handshake_hash.h +++ b/src/tls/tls_handshake_hash.h @@ -1,6 +1,6 @@ /* * TLS Handshake Hash -* (C) 2004-2006 Jack Lloyd +* (C) 2004-2006,2011 Jack Lloyd * * Released under the terms of the Botan license */ @@ -9,15 +9,19 @@ #define BOTAN_TLS_HANDSHAKE_HASH_H__ #include <botan/secmem.h> +#include <botan/tls_version.h> +#include <botan/tls_magic.h> namespace Botan { +namespace TLS { + using namespace Botan; /** * TLS Handshake Hash */ -class HandshakeHash +class Handshake_Hash { public: void update(const byte in[], size_t length) @@ -29,12 +33,23 @@ class HandshakeHash void update(byte in) { data.push_back(in); } - SecureVector<byte> final(); - SecureVector<byte> final_ssl3(const MemoryRegion<byte>&); + void update(Handshake_Type handshake_type, + const MemoryRegion<byte>& handshake_msg); + + SecureVector<byte> final(Protocol_Version version, + const std::string& mac_algo); + + SecureVector<byte> final_ssl3(const MemoryRegion<byte>& master_secret); + + const SecureVector<byte>& get_contents() const + { return data; } + private: SecureVector<byte> data; }; } +} + #endif diff --git a/src/tls/tls_handshake_reader.cpp b/src/tls/tls_handshake_reader.cpp new file mode 100644 index 000000000..8278a2296 --- /dev/null +++ b/src/tls/tls_handshake_reader.cpp @@ -0,0 +1,66 @@ +/* +* TLS Handshake Reader +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_handshake_reader.h> +#include <botan/exceptn.h> + +namespace Botan { + +namespace TLS { + +void Stream_Handshake_Reader::add_input(const byte record[], + size_t record_size) + { + m_queue.write(record, record_size); + } + +bool Stream_Handshake_Reader::empty() const + { + return m_queue.empty(); + } + +bool Stream_Handshake_Reader::have_full_record() const + { + if(m_queue.size() >= 4) + { + byte head[4] = { 0 }; + m_queue.peek(head, 4); + + const size_t length = make_u32bit(0, head[1], head[2], head[3]); + + return (m_queue.size() >= length + 4); + } + + return false; + } + +std::pair<Handshake_Type, MemoryVector<byte> > Stream_Handshake_Reader::get_next_record() + { + if(m_queue.size() >= 4) + { + byte head[4] = { 0 }; + m_queue.peek(head, 4); + + const size_t length = make_u32bit(0, head[1], head[2], head[3]); + + if(m_queue.size() >= length + 4) + { + Handshake_Type type = static_cast<Handshake_Type>(head[0]); + MemoryVector<byte> contents(length); + m_queue.read(head, 4); // discard + m_queue.read(&contents[0], contents.size()); + + return std::make_pair(type, contents); + } + } + + throw Internal_Error("Stream_Handshake_Reader::get_next_record called without a full record"); + } + +} + +} diff --git a/src/tls/tls_handshake_reader.h b/src/tls/tls_handshake_reader.h new file mode 100644 index 000000000..06a273ced --- /dev/null +++ b/src/tls/tls_handshake_reader.h @@ -0,0 +1,58 @@ +/* +* TLS Handshake Reader +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_READER_H__ +#define BOTAN_TLS_HANDSHAKE_READER_H__ + +#include <botan/tls_magic.h> +#include <botan/secqueue.h> +#include <botan/loadstor.h> +#include <utility> + +namespace Botan { + +namespace TLS { + +/** +* Handshake Reader Interface +*/ +class Handshake_Reader + { + public: + virtual void add_input(const byte record[], size_t record_size) = 0; + + virtual bool empty() const = 0; + + virtual bool have_full_record() const = 0; + + virtual std::pair<Handshake_Type, MemoryVector<byte> > get_next_record() = 0; + + virtual ~Handshake_Reader() {} + }; + +/** +* Reader of TLS handshake messages +*/ +class Stream_Handshake_Reader : public Handshake_Reader + { + public: + void add_input(const byte record[], size_t record_size); + + bool empty() const; + + bool have_full_record() const; + + std::pair<Handshake_Type, MemoryVector<byte> > get_next_record(); + private: + SecureQueue m_queue; + }; + +} + +} + +#endif diff --git a/src/tls/tls_handshake_state.cpp b/src/tls/tls_handshake_state.cpp new file mode 100644 index 000000000..48d9abbeb --- /dev/null +++ b/src/tls/tls_handshake_state.cpp @@ -0,0 +1,319 @@ +/* +* TLS Handshaking +* (C) 2004-2006,2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_handshake_state.h> +#include <botan/internal/tls_messages.h> +#include <botan/internal/assert.h> +#include <botan/lookup.h> + +namespace Botan { + +namespace TLS { + +namespace { + +u32bit bitmask_for_handshake_type(Handshake_Type type) + { + switch(type) + { + case HELLO_REQUEST: + return (1 << 0); + + /* + * Same code point for both client hello styles + */ + case CLIENT_HELLO: + case CLIENT_HELLO_SSLV2: + return (1 << 1); + + case SERVER_HELLO: + return (1 << 2); + + case CERTIFICATE: + return (1 << 3); + + case SERVER_KEX: + return (1 << 4); + + case CERTIFICATE_REQUEST: + return (1 << 5); + + case SERVER_HELLO_DONE: + return (1 << 6); + + case CERTIFICATE_VERIFY: + return (1 << 7); + + case CLIENT_KEX: + return (1 << 8); + + case NEXT_PROTOCOL: + return (1 << 9); + + case NEW_SESSION_TICKET: + return (1 << 10); + + case HANDSHAKE_CCS: + return (1 << 11); + + case FINISHED: + return (1 << 12); + + // allow explicitly disabling new handshakes + case HANDSHAKE_NONE: + return 0; + + default: + throw Internal_Error("Unknown handshake type " + to_string(type)); + } + + return 0; + } + +} + +/* +* Initialize the SSL/TLS Handshake State +*/ +Handshake_State::Handshake_State(Handshake_Reader* reader) + { + client_hello = 0; + server_hello = 0; + server_certs = 0; + server_kex = 0; + cert_req = 0; + server_hello_done = 0; + next_protocol = 0; + new_session_ticket = 0; + + client_certs = 0; + client_kex = 0; + client_verify = 0; + client_finished = 0; + server_finished = 0; + + m_handshake_reader = reader; + + server_rsa_kex_key = 0; + + m_version = Protocol_Version::SSL_V3; + + hand_expecting_mask = 0; + hand_received_mask = 0; + } + +void Handshake_State::set_version(const Protocol_Version& version) + { + m_version = version; + } + +void Handshake_State::confirm_transition_to(Handshake_Type handshake_msg) + { + const u32bit mask = bitmask_for_handshake_type(handshake_msg); + + hand_received_mask |= mask; + + const bool ok = (hand_expecting_mask & mask); // overlap? + + if(!ok) + throw Unexpected_Message("Unexpected state transition in handshake, got " + + to_string(handshake_msg) + " mask is " + + to_string(hand_expecting_mask)); + + /* We don't know what to expect next, so force a call to + set_expected_next; if it doesn't happen, the next transition + check will always fail which is what we want. + */ + hand_expecting_mask = 0; + } + +void Handshake_State::set_expected_next(Handshake_Type handshake_msg) + { + hand_expecting_mask |= bitmask_for_handshake_type(handshake_msg); + } + +bool Handshake_State::received_handshake_msg(Handshake_Type handshake_msg) const + { + const u32bit mask = bitmask_for_handshake_type(handshake_msg); + + return (hand_received_mask & mask); + } + +const MemoryRegion<byte>& Handshake_State::session_ticket() const + { + if(new_session_ticket && !new_session_ticket->ticket().empty()) + return new_session_ticket->ticket(); + + return client_hello->session_ticket(); + } + +KDF* Handshake_State::protocol_specific_prf() + { + if(version() == Protocol_Version::SSL_V3) + { + return get_kdf("SSL3-PRF"); + } + else if(version() == Protocol_Version::TLS_V10 || version() == Protocol_Version::TLS_V11) + { + return get_kdf("TLS-PRF"); + } + else if(version() == Protocol_Version::TLS_V12) + { + if(suite.mac_algo() == "MD5" || + suite.mac_algo() == "SHA-1" || + suite.mac_algo() == "SHA-256") + { + return get_kdf("TLS-12-PRF(SHA-256)"); + } + + return get_kdf("TLS-12-PRF(" + suite.mac_algo() + ")"); + } + + throw Internal_Error("Unknown version code " + version().to_string()); + } + +std::pair<std::string, Signature_Format> +Handshake_State::choose_sig_format(const Private_Key* key, + std::string& hash_algo_out, + std::string& sig_algo_out, + bool for_client_auth) + { + const std::string sig_algo = key->algo_name(); + + const std::vector<std::pair<std::string, std::string> > supported_algos = + (for_client_auth) ? cert_req->supported_algos() : client_hello->supported_algos(); + + std::string hash_algo; + + for(size_t i = 0; i != supported_algos.size(); ++i) + { + if(supported_algos[i].second == sig_algo) + { + hash_algo = supported_algos[i].first; + break; + } + } + + if(for_client_auth && this->version() == Protocol_Version::SSL_V3) + hash_algo = "Raw"; + + if(hash_algo == "" && this->version() == Protocol_Version::TLS_V12) + hash_algo = "SHA-1"; // TLS 1.2 but no compatible hashes set (?) + + BOTAN_ASSERT(hash_algo != "", "Couldn't figure out hash to use"); + + if(this->version() >= Protocol_Version::TLS_V12) + { + hash_algo_out = hash_algo; + sig_algo_out = sig_algo; + } + + if(sig_algo == "RSA") + { + const std::string padding = "EMSA3(" + hash_algo + ")"; + + return std::make_pair(padding, IEEE_1363); + } + else if(sig_algo == "DSA" || sig_algo == "ECDSA") + { + const std::string padding = "EMSA1(" + hash_algo + ")"; + + return std::make_pair(padding, DER_SEQUENCE); + } + + throw Invalid_Argument(sig_algo + " is invalid/unknown for TLS signatures"); + } + +std::pair<std::string, Signature_Format> +Handshake_State::understand_sig_format(const Public_Key* key, + std::string hash_algo, + std::string sig_algo, + bool for_client_auth) + { + const std::string algo_name = key->algo_name(); + + /* + FIXME: This should check what was sent against the client hello + preferences, or the certificate request, to ensure it was allowed + by those restrictions. + + Or not? + */ + + if(this->version() < Protocol_Version::TLS_V12) + { + if(hash_algo != "" || sig_algo != "") + throw Decoding_Error("Counterparty sent hash/sig IDs with old version"); + } + else + { + if(hash_algo == "") + throw Decoding_Error("Counterparty did not send hash/sig IDS"); + + if(sig_algo != algo_name) + throw Decoding_Error("Counterparty sent inconsistent key and sig types"); + } + + if(algo_name == "RSA") + { + if(for_client_auth && this->version() == Protocol_Version::SSL_V3) + { + hash_algo = "Raw"; + } + else if(this->version() < Protocol_Version::TLS_V12) + { + hash_algo = "TLS.Digest.0"; + } + + const std::string padding = "EMSA3(" + hash_algo + ")"; + return std::make_pair(padding, IEEE_1363); + } + else if(algo_name == "DSA" || algo_name == "ECDSA") + { + if(algo_name == "DSA" && for_client_auth && this->version() == Protocol_Version::SSL_V3) + { + hash_algo = "Raw"; + } + else if(this->version() < Protocol_Version::TLS_V12) + { + hash_algo = "SHA-1"; + } + + const std::string padding = "EMSA1(" + hash_algo + ")"; + + return std::make_pair(padding, DER_SEQUENCE); + } + + throw Invalid_Argument(algo_name + " is invalid/unknown for TLS signatures"); + } + +/* +* 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 next_protocol; + delete new_session_ticket; + + delete client_certs; + delete client_kex; + delete client_verify; + delete client_finished; + delete server_finished; + + delete m_handshake_reader; + } + +} + +} diff --git a/src/tls/tls_handshake_state.h b/src/tls/tls_handshake_state.h new file mode 100644 index 000000000..2a78d1d1e --- /dev/null +++ b/src/tls/tls_handshake_state.h @@ -0,0 +1,118 @@ +/* +* TLS Handshake State +* (C) 2004-2006 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_HANDSHAKE_STATE_H__ +#define BOTAN_TLS_HANDSHAKE_STATE_H__ + +#include <botan/internal/tls_handshake_hash.h> +#include <botan/internal/tls_handshake_reader.h> +#include <botan/internal/tls_session_key.h> +#include <botan/pk_keys.h> +#include <botan/pubkey.h> + +#include <utility> + +#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 { + +class KDF; + +namespace TLS { + +/** +* SSL/TLS Handshake State +*/ +class Handshake_State + { + public: + Handshake_State(Handshake_Reader* reader); + ~Handshake_State(); + + bool received_handshake_msg(Handshake_Type handshake_msg) const; + + void confirm_transition_to(Handshake_Type handshake_msg); + void set_expected_next(Handshake_Type handshake_msg); + + const MemoryRegion<byte>& session_ticket() const; + + std::pair<std::string, Signature_Format> + understand_sig_format(const Public_Key* key, + std::string hash_algo, + std::string sig_algo, + bool for_client_auth); + + std::pair<std::string, Signature_Format> + choose_sig_format(const Private_Key* key, + std::string& hash_algo, + std::string& sig_algo, + bool for_client_auth); + + KDF* protocol_specific_prf(); + + Protocol_Version version() const { return m_version; } + + void set_version(const Protocol_Version& version); + + class Client_Hello* client_hello; + class Server_Hello* server_hello; + class Certificate* server_certs; + class Server_Key_Exchange* server_kex; + class Certificate_Req* cert_req; + class Server_Hello_Done* server_hello_done; + + class Certificate* client_certs; + class Client_Key_Exchange* client_kex; + class Certificate_Verify* client_verify; + + class Next_Protocol* next_protocol; + class New_Session_Ticket* new_session_ticket; + + class Finished* client_finished; + class Finished* server_finished; + + // Used by the server only, in case of RSA key exchange + Private_Key* server_rsa_kex_key; + + Ciphersuite suite; + Session_Keys keys; + Handshake_Hash hash; + + /* + * Only used by clients for session resumption + */ + SecureVector<byte> resume_master_secret; + + /** + * Used by client using NPN + */ + std::tr1::function<std::string (std::vector<std::string>)> client_npn_cb; + + Handshake_Reader* handshake_reader() { return m_handshake_reader; } + private: + Handshake_Reader* m_handshake_reader; + u32bit hand_expecting_mask, hand_received_mask; + Protocol_Version m_version; + }; + +} + +} + +#endif diff --git a/src/tls/tls_magic.h b/src/tls/tls_magic.h new file mode 100644 index 000000000..6dd50ead2 --- /dev/null +++ b/src/tls/tls_magic.h @@ -0,0 +1,68 @@ +/* +* 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 { + +namespace TLS { + +/** +* Protocol Constants for SSL/TLS +*/ +enum Size_Limits { + TLS_HEADER_SIZE = 5, + MAX_PLAINTEXT_SIZE = 16*1024, + MAX_COMPRESSED_SIZE = MAX_PLAINTEXT_SIZE + 1024, + MAX_CIPHERTEXT_SIZE = MAX_COMPRESSED_SIZE + 1024, + + MAX_TLS_RECORD_SIZE = MAX_CIPHERTEXT_SIZE + TLS_HEADER_SIZE, +}; + +enum Connection_Side { CLIENT = 1, SERVER = 2 }; + +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 = 253, // Not a wire value + SERVER_HELLO = 2, + HELLO_VERIFY_REQUEST = 3, + NEW_SESSION_TICKET = 4, // RFC 5077 + CERTIFICATE = 11, + SERVER_KEX = 12, + CERTIFICATE_REQUEST = 13, + SERVER_HELLO_DONE = 14, + CERTIFICATE_VERIFY = 15, + CLIENT_KEX = 16, + FINISHED = 20, + + NEXT_PROTOCOL = 67, + + HANDSHAKE_CCS = 254, // Not a wire value + HANDSHAKE_NONE = 255 // Null value +}; + +enum Compression_Method { + NO_COMPRESSION = 0x00, + DEFLATE_COMPRESSION = 0x01 +}; + +} + +} + +#endif diff --git a/src/tls/tls_messages.h b/src/tls/tls_messages.h new file mode 100644 index 000000000..ff6ebda4d --- /dev/null +++ b/src/tls/tls_messages.h @@ -0,0 +1,487 @@ +/* +* TLS Messages +* (C) 2004-2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_MESSAGES_H__ +#define BOTAN_TLS_MESSAGES_H__ + +#include <botan/internal/tls_handshake_state.h> +#include <botan/tls_session.h> +#include <botan/tls_policy.h> +#include <botan/tls_magic.h> +#include <botan/tls_ciphersuite.h> +#include <botan/bigint.h> +#include <botan/pkcs8.h> +#include <botan/x509cert.h> +#include <vector> + +namespace Botan { + +class Credentials_Manager; + +namespace TLS { + +class Record_Writer; +class Record_Reader; + +/** +* TLS Handshake Message Base Class +*/ +class Handshake_Message + { + public: + virtual MemoryVector<byte> serialize() const = 0; + virtual Handshake_Type type() const = 0; + + Handshake_Message() {} + virtual ~Handshake_Message() {} + private: + Handshake_Message(const Handshake_Message&) {} + Handshake_Message& operator=(const Handshake_Message&) { return (*this); } + }; + +MemoryVector<byte> make_hello_random(RandomNumberGenerator& rng); + +/** +* DTLS Hello Verify Request +*/ +class Hello_Verify_Request : public Handshake_Message + { + public: + MemoryVector<byte> serialize() const; + Handshake_Type type() const { return HELLO_VERIFY_REQUEST; } + + MemoryVector<byte> cookie() const { return m_cookie; } + + Hello_Verify_Request(const MemoryRegion<byte>& buf); + + Hello_Verify_Request(const MemoryVector<byte>& client_hello_bits, + const std::string& client_identity, + const SymmetricKey& secret_key); + private: + MemoryVector<byte> m_cookie; + }; + +/** +* Client Hello Message +*/ +class Client_Hello : public Handshake_Message + { + public: + Handshake_Type type() const { return CLIENT_HELLO; } + Protocol_Version version() const { return m_version; } + const MemoryVector<byte>& session_id() const { return m_session_id; } + + std::vector<byte> session_id_vector() const + { + std::vector<byte> v; + v.insert(v.begin(), &m_session_id[0], &m_session_id[m_session_id.size()]); + return v; + } + + const std::vector<std::pair<std::string, std::string> >& supported_algos() const + { return m_supported_algos; } + + const std::vector<std::string>& supported_ecc_curves() const + { return m_supported_curves; } + + std::vector<u16bit> ciphersuites() const { return m_suites; } + std::vector<byte> compression_methods() const { return m_comp_methods; } + + const MemoryVector<byte>& random() const { return m_random; } + + std::string sni_hostname() const { return m_hostname; } + + std::string srp_identifier() const { return m_srp_identifier; } + + bool secure_renegotiation() const { return m_secure_renegotiation; } + + const MemoryVector<byte>& renegotiation_info() + { return m_renegotiation_info; } + + bool offered_suite(u16bit ciphersuite) const; + + bool next_protocol_notification() const { return m_next_protocol; } + + size_t fragment_size() const { return m_fragment_size; } + + bool supports_session_ticket() const { return m_supports_session_ticket; } + + const MemoryRegion<byte>& session_ticket() const + { return m_session_ticket; } + + Client_Hello(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + RandomNumberGenerator& rng, + const MemoryRegion<byte>& reneg_info, + bool next_protocol = false, + const std::string& hostname = "", + const std::string& srp_identifier = ""); + + Client_Hello(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + RandomNumberGenerator& rng, + const Session& resumed_session, + bool next_protocol = false); + + Client_Hello(const MemoryRegion<byte>& buf, + Handshake_Type type); + + private: + MemoryVector<byte> serialize() const; + void deserialize(const MemoryRegion<byte>& buf); + void deserialize_sslv2(const MemoryRegion<byte>& buf); + + Protocol_Version m_version; + MemoryVector<byte> m_session_id, m_random; + std::vector<u16bit> m_suites; + std::vector<byte> m_comp_methods; + std::string m_hostname; + std::string m_srp_identifier; + bool m_next_protocol; + + size_t m_fragment_size; + bool m_secure_renegotiation; + MemoryVector<byte> m_renegotiation_info; + + std::vector<std::pair<std::string, std::string> > m_supported_algos; + std::vector<std::string> m_supported_curves; + + bool m_supports_session_ticket; + MemoryVector<byte> m_session_ticket; + }; + +/** +* Server Hello Message +*/ +class Server_Hello : public Handshake_Message + { + public: + Handshake_Type type() const { return SERVER_HELLO; } + Protocol_Version version() { return m_version; } + const MemoryVector<byte>& session_id() const { return m_session_id; } + u16bit ciphersuite() const { return m_ciphersuite; } + byte compression_method() const { return m_comp_method; } + + std::vector<byte> session_id_vector() const + { + std::vector<byte> v; + v.insert(v.begin(), &m_session_id[0], &m_session_id[m_session_id.size()]); + return v; + } + + bool secure_renegotiation() const { return m_secure_renegotiation; } + + bool next_protocol_notification() const { return m_next_protocol; } + + bool supports_session_ticket() const { return m_supports_session_ticket; } + + const std::vector<std::string>& next_protocols() const + { return m_next_protocols; } + + size_t fragment_size() const { return m_fragment_size; } + + const MemoryVector<byte>& renegotiation_info() + { return m_renegotiation_info; } + + const MemoryVector<byte>& random() const { return m_random; } + + Server_Hello(Record_Writer& writer, + Handshake_Hash& hash, + const MemoryRegion<byte>& session_id, + Protocol_Version ver, + u16bit ciphersuite, + byte compression, + size_t max_fragment_size, + bool client_has_secure_renegotiation, + const MemoryRegion<byte>& reneg_info, + bool offer_session_ticket, + bool client_has_npn, + const std::vector<std::string>& next_protocols, + RandomNumberGenerator& rng); + + Server_Hello(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + + Protocol_Version m_version; + MemoryVector<byte> m_session_id, m_random; + u16bit m_ciphersuite; + byte m_comp_method; + + size_t m_fragment_size; + bool m_secure_renegotiation; + MemoryVector<byte> m_renegotiation_info; + + bool m_next_protocol; + std::vector<std::string> m_next_protocols; + bool m_supports_session_ticket; + }; + +/** +* Client Key Exchange Message +*/ +class Client_Key_Exchange : public Handshake_Message + { + public: + Handshake_Type type() const { return CLIENT_KEX; } + + const SecureVector<byte>& pre_master_secret() const + { return pre_master; } + + Client_Key_Exchange(Record_Writer& output, + Handshake_State* state, + Credentials_Manager& creds, + const std::vector<X509_Certificate>& peer_certs, + const std::string& hostname, + RandomNumberGenerator& rng); + + Client_Key_Exchange(const MemoryRegion<byte>& buf, + const Handshake_State* state, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng); + + private: + MemoryVector<byte> serialize() const { return key_material; } + + SecureVector<byte> key_material, pre_master; + }; + +/** +* Certificate Message +*/ +class Certificate : public Handshake_Message + { + public: + Handshake_Type type() const { return CERTIFICATE; } + const std::vector<X509_Certificate>& cert_chain() const { return m_certs; } + + size_t count() const { return m_certs.size(); } + bool empty() const { return m_certs.empty(); } + + Certificate(Record_Writer& writer, + Handshake_Hash& hash, + const std::vector<X509_Certificate>& certs); + + Certificate(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + + std::vector<X509_Certificate> m_certs; + }; + +/** +* Certificate Request Message +*/ +class Certificate_Req : public Handshake_Message + { + public: + Handshake_Type type() const { return CERTIFICATE_REQUEST; } + + const std::vector<std::string>& acceptable_cert_types() const + { return cert_key_types; } + + std::vector<X509_DN> acceptable_CAs() const { return names; } + + std::vector<std::pair<std::string, std::string> > supported_algos() const + { return m_supported_algos; } + + Certificate_Req(Record_Writer& writer, + Handshake_Hash& hash, + const Policy& policy, + const std::vector<X509_Certificate>& allowed_cas, + Protocol_Version version); + + Certificate_Req(const MemoryRegion<byte>& buf, + Protocol_Version version); + private: + MemoryVector<byte> serialize() const; + + std::vector<X509_DN> names; + std::vector<std::string> cert_key_types; + + std::vector<std::pair<std::string, std::string> > m_supported_algos; + }; + +/** +* Certificate Verify Message +*/ +class Certificate_Verify : public Handshake_Message + { + public: + Handshake_Type type() const { return CERTIFICATE_VERIFY; } + + /** + * Check the signature on a certificate verify message + * @param cert the purported certificate + * @param state the handshake state + */ + bool verify(const X509_Certificate& cert, + Handshake_State* state); + + Certificate_Verify(Record_Writer& writer, + Handshake_State* state, + RandomNumberGenerator& rng, + const Private_Key* key); + + Certificate_Verify(const MemoryRegion<byte>& buf, + Protocol_Version version); + private: + MemoryVector<byte> serialize() const; + + std::string sig_algo; // sig algo used to create signature + std::string hash_algo; // hash used to create signature + MemoryVector<byte> signature; + }; + +/** +* Finished Message +*/ +class Finished : public Handshake_Message + { + public: + Handshake_Type type() const { return FINISHED; } + + MemoryVector<byte> verify_data() const + { return verification_data; } + + bool verify(Handshake_State* state, + Connection_Side side); + + Finished(Record_Writer& writer, + Handshake_State* state, + Connection_Side side); + + Finished(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + + Connection_Side side; + MemoryVector<byte> verification_data; + }; + +/** +* Hello Request Message +*/ +class Hello_Request : public Handshake_Message + { + public: + Handshake_Type type() const { return HELLO_REQUEST; } + + Hello_Request(Record_Writer& writer); + Hello_Request(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + }; + +/** +* Server Key Exchange Message +*/ +class Server_Key_Exchange : public Handshake_Message + { + public: + Handshake_Type type() const { return SERVER_KEX; } + + const MemoryVector<byte>& params() const { return m_params; } + + bool verify(const X509_Certificate& cert, + Handshake_State* state) const; + + // Only valid for certain kex types + const Private_Key& server_kex_key() const; + + Server_Key_Exchange(Record_Writer& writer, + Handshake_State* state, + const Policy& policy, + Credentials_Manager& creds, + RandomNumberGenerator& rng, + const Private_Key* signing_key = 0); + + Server_Key_Exchange(const MemoryRegion<byte>& buf, + const std::string& kex_alg, + const std::string& sig_alg, + Protocol_Version version); + + ~Server_Key_Exchange() { delete m_kex_key; } + private: + MemoryVector<byte> serialize() const; + + Private_Key* m_kex_key; + + MemoryVector<byte> m_params; + + std::string m_sig_algo; // sig algo used to create signature + std::string m_hash_algo; // hash used to create signature + MemoryVector<byte> m_signature; + }; + +/** +* Server Hello Done Message +*/ +class Server_Hello_Done : public Handshake_Message + { + public: + Handshake_Type type() const { return SERVER_HELLO_DONE; } + + Server_Hello_Done(Record_Writer& writer, Handshake_Hash& hash); + Server_Hello_Done(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + }; + +/** +* Next Protocol Message +*/ +class Next_Protocol : public Handshake_Message + { + public: + Handshake_Type type() const { return NEXT_PROTOCOL; } + + std::string protocol() const { return m_protocol; } + + Next_Protocol(Record_Writer& writer, + Handshake_Hash& hash, + const std::string& protocol); + + Next_Protocol(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + + std::string m_protocol; + }; + +class New_Session_Ticket : public Handshake_Message + { + public: + Handshake_Type type() const { return NEW_SESSION_TICKET; } + + u32bit ticket_lifetime_hint() const { return m_ticket_lifetime_hint; } + const MemoryVector<byte>& ticket() const { return m_ticket; } + + New_Session_Ticket(Record_Writer& writer, + Handshake_Hash& hash, + const MemoryRegion<byte>& ticket, + u32bit lifetime); + + New_Session_Ticket(Record_Writer& writer, + Handshake_Hash& hash); + + New_Session_Ticket(const MemoryRegion<byte>& buf); + private: + MemoryVector<byte> serialize() const; + + u32bit m_ticket_lifetime_hint; + MemoryVector<byte> m_ticket; + }; + +} + +} + +#endif diff --git a/src/tls/tls_policy.cpp b/src/tls/tls_policy.cpp new file mode 100644 index 000000000..de3c6f674 --- /dev/null +++ b/src/tls/tls_policy.cpp @@ -0,0 +1,266 @@ +/* +* Policies for TLS +* (C) 2004-2010,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_policy.h> +#include <botan/tls_ciphersuite.h> +#include <botan/tls_magic.h> +#include <botan/tls_exceptn.h> +#include <botan/internal/stl_util.h> + +namespace Botan { + +namespace TLS { + +std::vector<std::string> Policy::allowed_ciphers() const + { + std::vector<std::string> allowed; + + allowed.push_back("AES-256"); + allowed.push_back("AES-128"); + allowed.push_back("3DES"); + allowed.push_back("ARC4"); + //allowed.push_back("Camellia"); + //allowed.push_back("SEED"); + + return allowed; + } + +std::vector<std::string> Policy::allowed_hashes() const + { + std::vector<std::string> allowed; + + allowed.push_back("SHA-512"); + allowed.push_back("SHA-384"); + allowed.push_back("SHA-256"); + allowed.push_back("SHA-224"); + allowed.push_back("SHA-1"); + //allowed.push_back("MD5"); + + return allowed; + } + +std::vector<std::string> Policy::allowed_key_exchange_methods() const + { + std::vector<std::string> allowed; + + //allowed.push_back("SRP_SHA"); + //allowed.push_back("ECDHE_PSK"); + //allowed.push_back("DHE_PSK"); + //allowed.push_back("PSK"); + + allowed.push_back("ECDH"); + allowed.push_back("DH"); + allowed.push_back("RSA"); + + return allowed; + } + +std::vector<std::string> Policy::allowed_signature_methods() const + { + std::vector<std::string> allowed; + + allowed.push_back("ECDSA"); + allowed.push_back("RSA"); + allowed.push_back("DSA"); + //allowed.push_back(""); + + return allowed; + } + +std::vector<std::string> Policy::allowed_ecc_curves() const + { + std::vector<std::string> curves; + curves.push_back("secp521r1"); + curves.push_back("secp384r1"); + curves.push_back("secp256r1"); + curves.push_back("secp256k1"); + curves.push_back("secp224r1"); + curves.push_back("secp224k1"); + curves.push_back("secp192r1"); + curves.push_back("secp192k1"); + curves.push_back("secp160r2"); + curves.push_back("secp160r1"); + curves.push_back("secp160k1"); + return curves; + } + +/* +* Choose an ECC curve to use +*/ +std::string Policy::choose_curve(const std::vector<std::string>& curve_names) const + { + const std::vector<std::string> our_curves = allowed_ecc_curves(); + + for(size_t i = 0; i != our_curves.size(); ++i) + if(value_exists(curve_names, our_curves[i])) + return our_curves[i]; + + return ""; // no shared curve + } + +/* +* Return allowed compression algorithms +*/ +std::vector<byte> Policy::compression() const + { + std::vector<byte> algs; + algs.push_back(NO_COMPRESSION); + return algs; + } + +u32bit Policy::session_ticket_lifetime() const + { + return 86400; // 1 day + } + +Protocol_Version Policy::min_version() const + { + return Protocol_Version::SSL_V3; + } + +Protocol_Version Policy::pref_version() const + { + return Protocol_Version::TLS_V12; + } + +namespace { + +class Ciphersuite_Preference_Ordering + { + public: + Ciphersuite_Preference_Ordering(const std::vector<std::string>& ciphers, + const std::vector<std::string>& hashes, + const std::vector<std::string>& kex, + const std::vector<std::string>& sigs) : + m_ciphers(ciphers), m_hashes(hashes), m_kex(kex), m_sigs(sigs) {} + + bool operator()(const Ciphersuite& a, const Ciphersuite& b) const + { + if(a.kex_algo() != b.kex_algo()) + { + for(size_t i = 0; i != m_kex.size(); ++i) + { + if(a.kex_algo() == m_kex[i]) + return true; + if(b.kex_algo() == m_kex[i]) + return false; + } + } + + if(a.cipher_algo() != b.cipher_algo()) + { + for(size_t i = 0; i != m_ciphers.size(); ++i) + { + if(a.cipher_algo() == m_ciphers[i]) + return true; + if(b.cipher_algo() == m_ciphers[i]) + return false; + } + } + + if(a.cipher_keylen() != b.cipher_keylen()) + { + if(a.cipher_keylen() < b.cipher_keylen()) + return false; + if(a.cipher_keylen() > b.cipher_keylen()) + return true; + } + + if(a.sig_algo() != b.sig_algo()) + { + for(size_t i = 0; i != m_sigs.size(); ++i) + { + if(a.sig_algo() == m_sigs[i]) + return true; + if(b.sig_algo() == m_sigs[i]) + return false; + } + } + + if(a.mac_algo() != b.mac_algo()) + { + for(size_t i = 0; i != m_hashes.size(); ++i) + { + if(a.mac_algo() == m_hashes[i]) + return true; + if(b.mac_algo() == m_hashes[i]) + return false; + } + } + + return false; // equal (?!?) + } + private: + std::vector<std::string> m_ciphers, m_hashes, m_kex, m_sigs; + + }; + +} + +std::vector<u16bit> ciphersuite_list(const Policy& policy, + bool have_srp) + { + std::vector<std::string> ciphers = policy.allowed_ciphers(); + std::vector<std::string> hashes = policy.allowed_hashes(); + std::vector<std::string> kex = policy.allowed_key_exchange_methods(); + std::vector<std::string> sigs = policy.allowed_signature_methods(); + + if(!have_srp) + { + std::vector<std::string>::iterator i = + std::find(kex.begin(), kex.end(), "SRP_SHA"); + + if(i != kex.end()) + kex.erase(i); + } + + Ciphersuite_Preference_Ordering order(ciphers, hashes, kex, sigs); + + std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering> + ciphersuites(order); + + for(size_t i = 0; i != 65536; ++i) + { + Ciphersuite suite = Ciphersuite::by_id(i); + + if(!suite.valid()) + continue; // not a ciphersuite we know, skip + + if(!value_exists(kex, suite.kex_algo())) + continue; // unsupported key exchange + + if(!value_exists(ciphers, suite.cipher_algo())) + continue; // unsupported cipher + + if(!value_exists(hashes, suite.mac_algo())) + continue; // unsupported MAC algo + + if(!value_exists(sigs, suite.sig_algo())) + { + // allow if it's an empty sig algo and we want to use PSK + if(suite.sig_algo() != "" || !suite.psk_ciphersuite()) + continue; + } + + // OK, allow it: + ciphersuites[suite] = i; + } + + std::vector<u16bit> ciphersuite_codes; + + for(std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering>::iterator i = ciphersuites.begin(); + i != ciphersuites.end(); ++i) + { + ciphersuite_codes.push_back(i->second); + } + + return ciphersuite_codes; + } + +} + +} diff --git a/src/tls/tls_policy.h b/src/tls/tls_policy.h new file mode 100644 index 000000000..288be62bd --- /dev/null +++ b/src/tls/tls_policy.h @@ -0,0 +1,127 @@ +/* +* 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_version.h> +#include <botan/x509cert.h> +#include <botan/dl_group.h> +#include <vector> + +namespace Botan { + +namespace TLS { + +/** +* TLS Policy Base Class +* Inherit and overload as desired to suit local policy concerns +*/ +class BOTAN_DLL Policy + { + public: + + /** + * Returns a list of ciphers we are willing to negotiate, in + * order of preference. Allowed values: any block cipher name, or + * ARC4. + */ + virtual std::vector<std::string> allowed_ciphers() const; + + /** + * Returns a list of hash algorithms we are willing to use, in + * order of preference. This is used for both MACs and signatures. + * Allowed values: any hash name, though currently only MD5, + * SHA-1, and the SHA-2 variants are used. + */ + virtual std::vector<std::string> allowed_hashes() const; + + /** + * Returns a list of key exchange algorithms we are willing to + * use, in order of preference. Allowed values: DH, empty string + * (representing RSA using server certificate key) + */ + virtual std::vector<std::string> allowed_key_exchange_methods() const; + + /** + * Returns a list of signature algorithms we are willing to + * use, in order of preference. Allowed values RSA and DSA. + */ + virtual std::vector<std::string> allowed_signature_methods() const; + + /** + * Return list of ECC curves we are willing to use in order of preference + */ + virtual std::vector<std::string> allowed_ecc_curves() const; + + /** + * Returns a list of signature algorithms we are willing to use, + * in order of preference. Allowed values any value of + * Compression_Method. + */ + virtual std::vector<byte> compression() const; + + /** + * Choose an elliptic curve to use + */ + virtual std::string choose_curve(const std::vector<std::string>& curve_names) const; + + /** + * Require support for RFC 5746 extensions to enable + * renegotiation. + * + * @warning Changing this to false exposes you to injected + * plaintext attacks. Read the RFC for background. + */ + virtual bool require_secure_renegotiation() const { return true; } + + /** + * Return the group to use for ephemeral Diffie-Hellman key agreement + */ + virtual DL_Group dh_group() const { return DL_Group("modp/ietf/1536"); } + + /** + * If this function returns false, unknown SRP/PSK identifiers + * will be rejected with an unknown_psk_identifier alert as soon + * as the non-existence is identified. Otherwise, a false + * identifier value will be used and the protocol allowed to + * proceed, causing the handshake to eventually fail without + * revealing that the username does not exist on this system. + */ + virtual bool hide_unknown_users() const { return false; } + + /** + * Return the allowed lifetime of a session ticket. If 0, session + * tickets do not expire until the session ticket key rolls over. + * Expired session tickets cannot be used to resume a session. + */ + virtual u32bit session_ticket_lifetime() const; + + /** + * @return the minimum version that we are willing to negotiate + */ + virtual Protocol_Version min_version() const; + + /** + * @return the version we would prefer to negotiate + */ + virtual Protocol_Version pref_version() const; + + virtual ~Policy() {} + }; + +/** +* Return allowed ciphersuites, in order of preference +*/ +std::vector<u16bit> ciphersuite_list(const Policy& policy, + bool have_srp); + +} + +} + +#endif diff --git a/src/ssl/tls_reader.h b/src/tls/tls_reader.h index 3a45235b5..f6b0d4088 100644 --- a/src/ssl/tls_reader.h +++ b/src/tls/tls_reader.h @@ -1,6 +1,6 @@ /* * TLS Data Reader -* (C) 2010 Jack Lloyd +* (C) 2010-2011 Jack Lloyd * * Released under the terms of the Botan license */ @@ -8,11 +8,17 @@ #ifndef BOTAN_TLS_READER_H__ #define BOTAN_TLS_READER_H__ +#include <botan/exceptn.h> #include <botan/secmem.h> #include <botan/loadstor.h> +#include <string> +#include <vector> +#include <stdexcept> namespace Botan { +namespace TLS { + /** * Helper class for decoding TLS protocol messages */ @@ -22,6 +28,12 @@ class TLS_Data_Reader TLS_Data_Reader(const MemoryRegion<byte>& buf_in) : buf(buf_in), offset(0) {} + void assert_done() const + { + if(has_remaining()) + throw Decoding_Error("Extra bytes at end of message"); + } + size_t remaining_bytes() const { return buf.size() - offset; @@ -38,6 +50,15 @@ class TLS_Data_Reader offset += bytes; } + u16bit get_u32bit() + { + assert_at_least(4); + u16bit result = make_u32bit(buf[offset ], buf[offset+1], + buf[offset+2], buf[offset+3]); + offset += 4; + return result; + } + u16bit get_u16bit() { assert_at_least(2); @@ -91,6 +112,16 @@ class TLS_Data_Reader return get_elem<T, std::vector<T> >(num_elems); } + std::string get_string(size_t len_bytes, + size_t min_bytes, + size_t max_bytes) + { + std::vector<byte> v = + get_range_vector<byte>(len_bytes, min_bytes, max_bytes); + + return std::string(reinterpret_cast<char*>(&v[0]), v.size()); + } + template<typename T> SecureVector<T> get_fixed(size_t size) { @@ -131,7 +162,11 @@ class TLS_Data_Reader void assert_at_least(size_t n) const { if(buf.size() - offset < n) - throw Decoding_Error("TLS_Data_Reader: Corrupt packet"); + { + throw Decoding_Error("TLS_Data_Reader: Expected " + to_string(n) + + " bytes remaining, only " + to_string(buf.size()-offset) + + " left"); + } } const MemoryRegion<byte>& buf; @@ -181,6 +216,18 @@ void append_tls_length_value(MemoryRegion<byte>& buf, append_tls_length_value(buf, &vals[0], vals.size(), tag_size); } +inline void append_tls_length_value(MemoryRegion<byte>& buf, + const std::string& str, + size_t tag_size) + { + append_tls_length_value(buf, + reinterpret_cast<const byte*>(&str[0]), + str.size(), + tag_size); + } + +} + } #endif diff --git a/src/tls/tls_record.h b/src/tls/tls_record.h new file mode 100644 index 000000000..fb27db5e2 --- /dev/null +++ b/src/tls/tls_record.h @@ -0,0 +1,146 @@ +/* +* TLS Record Handling +* (C) 2004-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_RECORDS_H__ +#define BOTAN_TLS_RECORDS_H__ + +#include <botan/tls_ciphersuite.h> +#include <botan/tls_alert.h> +#include <botan/tls_magic.h> +#include <botan/tls_version.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 { + +namespace TLS { + +class Session_Keys; + +/** +* 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); } + + MemoryVector<byte> send(class Handshake_Message& msg); + + void send_alert(const Alert& alert); + + void activate(Connection_Side side, + const Ciphersuite& suite, + const Session_Keys& keys, + byte compression_method); + + void set_version(Protocol_Version version); + + void reset(); + + void set_maximum_fragment_size(size_t max_fragment); + + Record_Writer(std::tr1::function<void (const byte[], size_t)> output_fn); + + ~Record_Writer() { delete m_mac; } + private: + Record_Writer(const Record_Writer&) {} + Record_Writer& operator=(const Record_Writer&) { return (*this); } + + void send_record(byte type, const byte input[], size_t length); + + std::tr1::function<void (const byte[], size_t)> m_output_fn; + + MemoryVector<byte> m_writebuf; + + Pipe m_cipher; + MessageAuthenticationCode* m_mac; + + size_t m_block_size, m_mac_size, m_iv_size, m_max_fragment; + + u64bit m_seq_no; + Protocol_Version m_version; + }; + +/** +* TLS Record Reader +*/ +class BOTAN_DLL Record_Reader + { + public: + + /** + * @param input new input data (may be NULL if input_size == 0) + * @param input_size size of input in bytes + * @param input_consumed is set to the number of bytes of input + * that were consumed + * @param msg_type is set to the type of the message just read if + * this function returns 0 + * @param msg is set to the contents of the record + * @return number of bytes still needed (minimum), or 0 if success + */ + size_t add_input(const byte input[], size_t input_size, + size_t& input_consumed, + byte& msg_type, + MemoryVector<byte>& msg); + + void activate(Connection_Side side, + const Ciphersuite& suite, + const Session_Keys& keys, + byte compression_method); + + void set_version(Protocol_Version version); + + void reset(); + + void set_maximum_fragment_size(size_t max_fragment); + + Record_Reader(); + + ~Record_Reader() { delete m_mac; } + private: + Record_Reader(const Record_Reader&) {} + Record_Reader& operator=(const Record_Reader&) { return (*this); } + + size_t fill_buffer_to(const byte*& input, + size_t& input_size, + size_t& input_consumed, + size_t desired); + + MemoryVector<byte> m_readbuf; + MemoryVector<byte> m_macbuf; + size_t m_readbuf_pos; + + Pipe m_cipher; + MessageAuthenticationCode* m_mac; + size_t m_block_size, m_iv_size, m_max_fragment; + u64bit m_seq_no; + Protocol_Version m_version; + }; + +} + +} + +#endif diff --git a/src/tls/tls_server.cpp b/src/tls/tls_server.cpp new file mode 100644 index 000000000..f5b4efc30 --- /dev/null +++ b/src/tls/tls_server.cpp @@ -0,0 +1,598 @@ +/* +* TLS Server +* (C) 2004-2011,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_server.h> +#include <botan/internal/tls_handshake_state.h> +#include <botan/internal/tls_messages.h> +#include <botan/internal/stl_util.h> +#include <botan/internal/assert.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +namespace { + +bool check_for_resume(Session& session_info, + Session_Manager& session_manager, + Credentials_Manager& credentials, + Client_Hello* client_hello, + u32bit session_ticket_lifetime) + { + const MemoryVector<byte>& client_session_id = client_hello->session_id(); + const MemoryVector<byte>& session_ticket = client_hello->session_ticket(); + + if(session_ticket.empty()) + { + if(client_session_id.empty()) // not resuming + return false; + + // not found + if(!session_manager.load_from_session_id(client_session_id, session_info)) + return false; + } + else + { + // If a session ticket was sent, ignore client session ID + try + { + session_info = Session::decrypt( + session_ticket, + credentials.psk("tls-server", "session-ticket", "")); + + if(session_ticket_lifetime && + session_info.session_age() > session_ticket_lifetime) + return false; // ticket has expired + } + catch(...) + { + return false; + } + } + + // wrong version + if(client_hello->version() != session_info.version()) + return false; + + // client didn't send original ciphersuite + if(!value_exists(client_hello->ciphersuites(), + session_info.ciphersuite_code())) + return false; + + // client didn't send original compression method + if(!value_exists(client_hello->compression_methods(), + session_info.compression_method())) + return false; + + // client sent a different SRP identity + if(client_hello->srp_identifier() != "") + { + if(client_hello->srp_identifier() != session_info.srp_identifier()) + return false; + } + + // client sent a different SNI hostname + if(client_hello->sni_hostname() != "") + { + if(client_hello->sni_hostname() != session_info.sni_hostname()) + return false; + } + + return true; + } + +/* +* Choose which ciphersuite to use +*/ +u16bit choose_ciphersuite( + const Policy& policy, + const std::map<std::string, std::vector<X509_Certificate> >& cert_chains, + const Client_Hello* client_hello) + { + const std::vector<u16bit> client_suites = client_hello->ciphersuites(); + const std::vector<u16bit> server_suites = ciphersuite_list(policy, false); + + const bool have_shared_ecc_curve = + (policy.choose_curve(client_hello->supported_ecc_curves()) != ""); + + // Ordering by our preferences rather than by clients + for(size_t i = 0; i != server_suites.size(); ++i) + { + const u16bit suite_id = server_suites[i]; + + if(!value_exists(client_suites, suite_id)) + continue; + + Ciphersuite suite = Ciphersuite::by_id(suite_id); + + if(!have_shared_ecc_curve && suite.ecc_ciphersuite()) + continue; + + if(cert_chains.count(suite.sig_algo()) == 0) + continue; + + return suite_id; + } + + throw TLS_Exception(Alert::HANDSHAKE_FAILURE, + "Can't agree on a ciphersuite with client"); + } + + +/* +* Choose which compression algorithm to use +*/ +byte choose_compression(const Policy& policy, + const std::vector<byte>& c_comp) + { + std::vector<byte> s_comp = policy.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; + } + +std::map<std::string, std::vector<X509_Certificate> > +get_server_certs(const std::string& hostname, + Credentials_Manager& creds) + { + const char* cert_types[] = { "RSA", "DSA", "ECDSA", 0 }; + + std::map<std::string, std::vector<X509_Certificate> > cert_chains; + + for(size_t i = 0; cert_types[i]; ++i) + { + std::vector<X509_Certificate> certs = + creds.cert_chain_single_type(cert_types[i], "tls-server", hostname); + + if(!certs.empty()) + cert_chains[cert_types[i]] = certs; + } + + return cert_chains; + } + +} + +/* +* TLS Server Constructor +*/ +Server::Server(std::tr1::function<void (const byte[], size_t)> output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_fn, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + const std::vector<std::string>& next_protocols) : + Channel(output_fn, proc_fn, handshake_fn), + policy(policy), + rng(rng), + session_manager(session_manager), + creds(creds), + m_possible_protocols(next_protocols) + { + } + +/* +* Send a hello request to the client +*/ +void Server::renegotiate() + { + if(state) + return; // currently in handshake + + state = new Handshake_State(new Stream_Handshake_Reader); + state->set_expected_next(CLIENT_HELLO); + Hello_Request hello_req(writer); + } + +void Server::alert_notify(const Alert& alert) + { + if(alert.type() == Alert::NO_RENEGOTIATION) + { + if(handshake_completed && state) + { + delete state; + state = 0; + } + } + } + +/* +* Split up and process handshake messages +*/ +void Server::read_handshake(byte rec_type, + const MemoryRegion<byte>& rec_buf) + { + if(rec_type == HANDSHAKE && !state) + { + state = new Handshake_State(new Stream_Handshake_Reader); + state->set_expected_next(CLIENT_HELLO); + } + + Channel::read_handshake(rec_type, rec_buf); + } + +/* +* Process a handshake message +*/ +void Server::process_handshake_msg(Handshake_Type type, + const MemoryRegion<byte>& contents) + { + if(state == 0) + throw Unexpected_Message("Unexpected handshake message from client"); + + state->confirm_transition_to(type); + + /* + * The change cipher spec message isn't technically a handshake + * message so it's not included in the hash. The finished and + * certificate verify messages are verified based on the current + * state of the hash *before* this message so we delay adding them + * to the hash computation until we've processed them below. + */ + if(type != HANDSHAKE_CCS && type != FINISHED && type != CERTIFICATE_VERIFY) + { + if(type == CLIENT_HELLO_SSLV2) + state->hash.update(contents); + else + state->hash.update(type, contents); + } + + if(type == CLIENT_HELLO || type == CLIENT_HELLO_SSLV2) + { + state->client_hello = new Client_Hello(contents, type); + + m_hostname = state->client_hello->sni_hostname(); + + Protocol_Version client_version = state->client_hello->version(); + + if(client_version < policy.min_version()) + throw TLS_Exception(Alert::PROTOCOL_VERSION, + "Client version is unacceptable by policy"); + + if(client_version <= policy.pref_version()) + state->set_version(client_version); + else + state->set_version(policy.pref_version()); + + secure_renegotiation.update(state->client_hello); + + writer.set_version(state->version()); + reader.set_version(state->version()); + + Session session_info; + const bool resuming = check_for_resume(session_info, + session_manager, + creds, + state->client_hello, + policy.session_ticket_lifetime()); + + bool have_session_ticket_key = false; + + try + { + have_session_ticket_key = + creds.psk("tls-server", "session-ticket", "").length() > 0; + } + catch(...) {} + + if(resuming) + { + // resume session + + state->server_hello = new Server_Hello( + writer, + state->hash, + state->client_hello->session_id(), + Protocol_Version(session_info.version()), + session_info.ciphersuite_code(), + session_info.compression_method(), + session_info.fragment_size(), + secure_renegotiation.supported(), + secure_renegotiation.for_server_hello(), + (state->client_hello->supports_session_ticket() && + state->client_hello->session_ticket().empty() && + have_session_ticket_key), + state->client_hello->next_protocol_notification(), + m_possible_protocols, + rng); + + if(session_info.fragment_size()) + { + reader.set_maximum_fragment_size(session_info.fragment_size()); + writer.set_maximum_fragment_size(session_info.fragment_size()); + } + + state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite()); + + state->keys = Session_Keys(state, session_info.master_secret(), true); + + if(!handshake_fn(session_info)) + { + session_manager.remove_entry(session_info.session_id()); + + if(state->server_hello->supports_session_ticket()) // send an empty ticket + state->new_session_ticket = new New_Session_Ticket(writer, state->hash); + } + + if(state->server_hello->supports_session_ticket() && !state->new_session_ticket) + { + try + { + const SymmetricKey ticket_key = creds.psk("tls-server", "session-ticket", ""); + + state->new_session_ticket = + new New_Session_Ticket(writer, state->hash, + session_info.encrypt(ticket_key, rng), + policy.session_ticket_lifetime()); + } + catch(...) {} + + if(!state->new_session_ticket) + state->new_session_ticket = new New_Session_Ticket(writer, state->hash); + } + + writer.send(CHANGE_CIPHER_SPEC, 1); + + writer.activate(SERVER, state->suite, state->keys, + state->server_hello->compression_method()); + + state->server_finished = new Finished(writer, state, SERVER); + + state->set_expected_next(HANDSHAKE_CCS); + } + else // new session + { + std::map<std::string, std::vector<X509_Certificate> > cert_chains; + + cert_chains = get_server_certs(m_hostname, creds); + + if(m_hostname != "" && cert_chains.empty()) + { + send_alert(Alert(Alert::UNRECOGNIZED_NAME)); + cert_chains = get_server_certs("", creds); + } + + state->server_hello = new Server_Hello( + writer, + state->hash, + rng.random_vec(32), // new session ID + state->version(), + choose_ciphersuite(policy, cert_chains, state->client_hello), + choose_compression(policy, state->client_hello->compression_methods()), + state->client_hello->fragment_size(), + secure_renegotiation.supported(), + secure_renegotiation.for_server_hello(), + state->client_hello->supports_session_ticket() && have_session_ticket_key, + state->client_hello->next_protocol_notification(), + m_possible_protocols, + rng); + + if(state->client_hello->fragment_size()) + { + reader.set_maximum_fragment_size(state->client_hello->fragment_size()); + writer.set_maximum_fragment_size(state->client_hello->fragment_size()); + } + + state->suite = Ciphersuite::by_id(state->server_hello->ciphersuite()); + + const std::string sig_algo = state->suite.sig_algo(); + const std::string kex_algo = state->suite.kex_algo(); + + if(sig_algo != "") + { + BOTAN_ASSERT(!cert_chains[sig_algo].empty(), + "Attempting to send empty certificate chain"); + + state->server_certs = new Certificate(writer, + state->hash, + cert_chains[sig_algo]); + } + + Private_Key* private_key = 0; + + if(kex_algo == "RSA" || sig_algo != "") + { + private_key = creds.private_key_for(state->server_certs->cert_chain()[0], + "tls-server", + m_hostname); + + if(!private_key) + throw Internal_Error("No private key located for associated server cert"); + } + + if(kex_algo == "RSA") + { + state->server_rsa_kex_key = private_key; + } + else + { + state->server_kex = + new Server_Key_Exchange(writer, state, policy, creds, rng, private_key); + } + + std::vector<X509_Certificate> client_auth_CAs = + creds.trusted_certificate_authorities("tls-server", m_hostname); + + if(!client_auth_CAs.empty() && state->suite.sig_algo() != "") + { + state->cert_req = new Certificate_Req(writer, + state->hash, + policy, + client_auth_CAs, + state->version()); + + state->set_expected_next(CERTIFICATE); + } + + secure_renegotiation.update(state->server_hello); + + /* + * If the client doesn't have a cert they want to use they are + * allowed to send either an empty cert message or proceed + * directly to the client key exchange, so allow either case. + */ + state->set_expected_next(CLIENT_KEX); + + state->server_hello_done = new Server_Hello_Done(writer, state->hash); + } + } + else if(type == CERTIFICATE) + { + state->client_certs = new Certificate(contents); + + // Is this allowed by the protocol? + if(state->client_certs->count() > 1) + throw TLS_Exception(Alert::CERTIFICATE_UNKNOWN, + "Client sent more than one certificate"); + + state->set_expected_next(CLIENT_KEX); + } + else if(type == CLIENT_KEX) + { + if(state->received_handshake_msg(CERTIFICATE) && !state->client_certs->empty()) + state->set_expected_next(CERTIFICATE_VERIFY); + else + state->set_expected_next(HANDSHAKE_CCS); + + state->client_kex = new Client_Key_Exchange(contents, state, creds, policy, rng); + + state->keys = Session_Keys(state, state->client_kex->pre_master_secret(), false); + } + else if(type == CERTIFICATE_VERIFY) + { + state->client_verify = new Certificate_Verify(contents, state->version()); + + const std::vector<X509_Certificate>& client_certs = + state->client_certs->cert_chain(); + + const bool sig_valid = + state->client_verify->verify(client_certs[0], state); + + state->hash.update(type, contents); + + /* + * Using DECRYPT_ERROR looks weird here, but per RFC 4346 is for + * "A handshake cryptographic operation failed, including being + * unable to correctly verify a signature, ..." + */ + if(!sig_valid) + throw TLS_Exception(Alert::DECRYPT_ERROR, "Client cert verify failed"); + + try + { + creds.verify_certificate_chain("tls-server", "", client_certs); + } + catch(std::exception& e) + { + throw TLS_Exception(Alert::BAD_CERTIFICATE, e.what()); + } + + state->set_expected_next(HANDSHAKE_CCS); + } + else if(type == HANDSHAKE_CCS) + { + if(state->server_hello->next_protocol_notification()) + state->set_expected_next(NEXT_PROTOCOL); + else + state->set_expected_next(FINISHED); + + reader.activate(SERVER, state->suite, state->keys, + state->server_hello->compression_method()); + } + else if(type == NEXT_PROTOCOL) + { + state->set_expected_next(FINISHED); + + state->next_protocol = new Next_Protocol(contents); + + m_next_protocol = state->next_protocol->protocol(); + } + else if(type == FINISHED) + { + state->set_expected_next(HANDSHAKE_NONE); + + state->client_finished = new Finished(contents); + + if(!state->client_finished->verify(state, CLIENT)) + throw TLS_Exception(Alert::DECRYPT_ERROR, + "Finished message didn't verify"); + + if(!state->server_finished) + { + // already sent finished if resuming, so this is a new session + + state->hash.update(type, contents); + + Session session_info( + state->server_hello->session_id(), + state->keys.master_secret(), + state->server_hello->version(), + state->server_hello->ciphersuite(), + state->server_hello->compression_method(), + SERVER, + secure_renegotiation.supported(), + state->server_hello->fragment_size(), + peer_certs, + MemoryVector<byte>(), + m_hostname, + "" + ); + + if(handshake_fn(session_info)) + { + if(state->server_hello->supports_session_ticket()) + { + try + { + const SymmetricKey ticket_key = creds.psk("tls-server", "session-ticket", ""); + + state->new_session_ticket = + new New_Session_Ticket(writer, state->hash, + session_info.encrypt(ticket_key, rng), + policy.session_ticket_lifetime()); + } + catch(...) {} + } + else + session_manager.save(session_info); + } + + if(state->server_hello->supports_session_ticket() && !state->new_session_ticket) + state->new_session_ticket = new New_Session_Ticket(writer, state->hash); + + writer.send(CHANGE_CIPHER_SPEC, 1); + + writer.activate(SERVER, state->suite, state->keys, + state->server_hello->compression_method()); + + state->server_finished = new Finished(writer, state, SERVER); + + if(state->client_certs && state->client_verify) + peer_certs = state->client_certs->cert_chain(); + } + + secure_renegotiation.update(state->client_finished, + state->server_finished); + + delete state; + state = 0; + handshake_completed = true; + } + else + throw Unexpected_Message("Unknown handshake message received"); + } + +} + +} diff --git a/src/tls/tls_server.h b/src/tls/tls_server.h new file mode 100644 index 000000000..025bbf3ec --- /dev/null +++ b/src/tls/tls_server.h @@ -0,0 +1,74 @@ +/* +* 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_channel.h> +#include <botan/tls_session_manager.h> +#include <botan/credentials_manager.h> +#include <vector> + +namespace Botan { + +namespace TLS { + +/** +* TLS Server +*/ +class BOTAN_DLL Server : public Channel + { + public: + /** + * Server initialization + */ + Server(std::tr1::function<void (const byte[], size_t)> socket_output_fn, + std::tr1::function<void (const byte[], size_t, Alert)> proc_fn, + std::tr1::function<bool (const Session&)> handshake_complete, + Session_Manager& session_manager, + Credentials_Manager& creds, + const Policy& policy, + RandomNumberGenerator& rng, + const std::vector<std::string>& protocols = + std::vector<std::string>()); + + void renegotiate(); + + /** + * Return the server name indicator, if sent by the client + */ + std::string server_name_indicator() const + { return m_hostname; } + + /** + * Return the protocol negotiated with NPN extension + */ + std::string next_protocol() const + { return m_next_protocol; } + + private: + void read_handshake(byte, const MemoryRegion<byte>&); + + void process_handshake_msg(Handshake_Type, const MemoryRegion<byte>&); + + void alert_notify(const Alert& alert); + + const Policy& policy; + RandomNumberGenerator& rng; + Session_Manager& session_manager; + Credentials_Manager& creds; + + std::vector<std::string> m_possible_protocols; + std::string m_hostname; + std::string m_next_protocol; + }; + +} + +} + +#endif diff --git a/src/tls/tls_session.cpp b/src/tls/tls_session.cpp new file mode 100644 index 000000000..0e8bf3051 --- /dev/null +++ b/src/tls/tls_session.cpp @@ -0,0 +1,248 @@ +/* +* TLS Session State +* (C) 2011-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_session.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/asn1_str.h> +#include <botan/pem.h> +#include <botan/time.h> +#include <botan/lookup.h> +#include <botan/loadstor.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +Session::Session(const MemoryRegion<byte>& session_identifier, + const MemoryRegion<byte>& master_secret, + Protocol_Version version, + u16bit ciphersuite, + byte compression_method, + Connection_Side side, + bool secure_renegotiation_supported, + size_t fragment_size, + const std::vector<X509_Certificate>& certs, + const MemoryRegion<byte>& ticket, + const std::string& sni_hostname, + const std::string& srp_identifier) : + m_start_time(system_time()), + m_identifier(session_identifier), + m_session_ticket(ticket), + m_master_secret(master_secret), + m_version(version), + m_ciphersuite(ciphersuite), + m_compression_method(compression_method), + m_connection_side(side), + m_secure_renegotiation_supported(secure_renegotiation_supported), + m_fragment_size(fragment_size), + m_peer_certs(certs), + m_sni_hostname(sni_hostname), + m_srp_identifier(srp_identifier) + { + } + +Session::Session(const std::string& pem) + { + SecureVector<byte> der = PEM_Code::decode_check_label(pem, "SSL SESSION"); + + *this = Session(&der[0], der.size()); + } + +Session::Session(const byte ber[], size_t ber_len) + { + byte side_code = 0; + ASN1_String sni_hostname_str; + ASN1_String srp_identifier_str; + + byte major_version = 0, minor_version = 0; + + MemoryVector<byte> peer_cert_bits; + + BER_Decoder(ber, ber_len) + .start_cons(SEQUENCE) + .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION), + "Unknown version in session structure") + .decode_integer_type(m_start_time) + .decode_integer_type(major_version) + .decode_integer_type(minor_version) + .decode(m_identifier, OCTET_STRING) + .decode(m_session_ticket, OCTET_STRING) + .decode_integer_type(m_ciphersuite) + .decode_integer_type(m_compression_method) + .decode_integer_type(side_code) + .decode_integer_type(m_fragment_size) + .decode(m_secure_renegotiation_supported) + .decode(m_master_secret, OCTET_STRING) + .decode(peer_cert_bits, OCTET_STRING) + .decode(sni_hostname_str) + .decode(srp_identifier_str) + .end_cons() + .verify_end(); + + m_version = Protocol_Version(major_version, minor_version); + m_sni_hostname = sni_hostname_str.value(); + m_srp_identifier = srp_identifier_str.value(); + m_connection_side = static_cast<Connection_Side>(side_code); + + if(!peer_cert_bits.empty()) + { + DataSource_Memory certs(peer_cert_bits); + + while(!certs.end_of_data()) + m_peer_certs.push_back(X509_Certificate(certs)); + } + } + +SecureVector<byte> Session::DER_encode() const + { + MemoryVector<byte> peer_cert_bits; + for(size_t i = 0; i != m_peer_certs.size(); ++i) + peer_cert_bits += m_peer_certs[i].BER_encode(); + + return DER_Encoder() + .start_cons(SEQUENCE) + .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION)) + .encode(static_cast<size_t>(m_start_time)) + .encode(static_cast<size_t>(m_version.major_version())) + .encode(static_cast<size_t>(m_version.minor_version())) + .encode(m_identifier, OCTET_STRING) + .encode(m_session_ticket, OCTET_STRING) + .encode(static_cast<size_t>(m_ciphersuite)) + .encode(static_cast<size_t>(m_compression_method)) + .encode(static_cast<size_t>(m_connection_side)) + .encode(static_cast<size_t>(m_fragment_size)) + .encode(m_secure_renegotiation_supported) + .encode(m_master_secret, OCTET_STRING) + .encode(peer_cert_bits, OCTET_STRING) + .encode(ASN1_String(m_sni_hostname, UTF8_STRING)) + .encode(ASN1_String(m_srp_identifier, UTF8_STRING)) + .end_cons() + .get_contents(); + } + +std::string Session::PEM_encode() const + { + return PEM_Code::encode(this->DER_encode(), "SSL SESSION"); + } + +u32bit Session::session_age() const + { + return (system_time() - m_start_time); + } + +namespace { + +const u32bit SESSION_CRYPTO_MAGIC = 0x571B0E4E; +const std::string SESSION_CRYPTO_CIPHER = "AES-256/CBC"; +const std::string SESSION_CRYPTO_MAC = "HMAC(SHA-256)"; +const std::string SESSION_CRYPTO_KDF = "KDF2(SHA-256)"; + +const size_t MAGIC_LENGTH = 4; +const size_t MAC_KEY_LENGTH = 32; +const size_t CIPHER_KEY_LENGTH = 32; +const size_t CIPHER_IV_LENGTH = 16; +const size_t MAC_OUTPUT_LENGTH = 32; + +} + +MemoryVector<byte> +Session::encrypt(const SymmetricKey& master_key, + RandomNumberGenerator& rng) const + { + std::auto_ptr<KDF> kdf(get_kdf(SESSION_CRYPTO_KDF)); + + SymmetricKey cipher_key = + kdf->derive_key(CIPHER_KEY_LENGTH, + master_key.bits_of(), + "tls.session.cipher-key"); + + SymmetricKey mac_key = + kdf->derive_key(MAC_KEY_LENGTH, + master_key.bits_of(), + "tls.session.mac-key"); + + InitializationVector cipher_iv(rng, 16); + + std::auto_ptr<MessageAuthenticationCode> mac(get_mac(SESSION_CRYPTO_MAC)); + mac->set_key(mac_key); + + Pipe pipe(get_cipher(SESSION_CRYPTO_CIPHER, cipher_key, cipher_iv, ENCRYPTION)); + pipe.process_msg(this->DER_encode()); + MemoryVector<byte> ctext = pipe.read_all(0); + + MemoryVector<byte> out(MAGIC_LENGTH); + store_be(SESSION_CRYPTO_MAGIC, &out[0]); + out += cipher_iv.bits_of(); + out += ctext; + + mac->update(out); + + out += mac->final(); + return out; + } + +Session Session::decrypt(const byte buf[], size_t buf_len, + const SymmetricKey& master_key) + { + try + { + const size_t MIN_CTEXT_SIZE = 4 * 16; // due to 48 byte master secret + + if(buf_len < (MAGIC_LENGTH + + CIPHER_IV_LENGTH + + MIN_CTEXT_SIZE + + MAC_OUTPUT_LENGTH)) + throw Decoding_Error("Encrypted TLS session too short to be valid"); + + if(load_be<u32bit>(buf, 0) != SESSION_CRYPTO_MAGIC) + throw Decoding_Error("Unknown header value in encrypted session"); + + std::auto_ptr<KDF> kdf(get_kdf(SESSION_CRYPTO_KDF)); + + SymmetricKey mac_key = + kdf->derive_key(MAC_KEY_LENGTH, + master_key.bits_of(), + "tls.session.mac-key"); + + std::auto_ptr<MessageAuthenticationCode> mac(get_mac(SESSION_CRYPTO_MAC)); + mac->set_key(mac_key); + + mac->update(&buf[0], buf_len - MAC_OUTPUT_LENGTH); + MemoryVector<byte> computed_mac = mac->final(); + + if(!same_mem(&buf[buf_len - MAC_OUTPUT_LENGTH], &computed_mac[0], computed_mac.size())) + throw Decoding_Error("MAC verification failed for encrypted session"); + + SymmetricKey cipher_key = + kdf->derive_key(CIPHER_KEY_LENGTH, + master_key.bits_of(), + "tls.session.cipher-key"); + + InitializationVector cipher_iv(&buf[MAGIC_LENGTH], CIPHER_IV_LENGTH); + + const size_t CTEXT_OFFSET = MAGIC_LENGTH + CIPHER_IV_LENGTH; + + Pipe pipe(get_cipher(SESSION_CRYPTO_CIPHER, cipher_key, cipher_iv, DECRYPTION)); + pipe.process_msg(&buf[CTEXT_OFFSET], + buf_len - (MAC_OUTPUT_LENGTH + CTEXT_OFFSET)); + SecureVector<byte> ber = pipe.read_all(); + + return Session(&ber[0], ber.size()); + } + catch(std::exception& e) + { + throw Decoding_Error("Failed to decrypt encrypted session -" + + std::string(e.what())); + } + } + +} + +} + diff --git a/src/tls/tls_session.h b/src/tls/tls_session.h new file mode 100644 index 000000000..9a3c242a8 --- /dev/null +++ b/src/tls/tls_session.h @@ -0,0 +1,215 @@ +/* +* TLS Session +* (C) 2011-2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef TLS_SESSION_STATE_H__ +#define TLS_SESSION_STATE_H__ + +#include <botan/x509cert.h> +#include <botan/tls_version.h> +#include <botan/tls_ciphersuite.h> +#include <botan/tls_magic.h> +#include <botan/secmem.h> +#include <botan/symkey.h> + +namespace Botan { + +namespace TLS { + +/** +* Class representing a TLS session state +*/ +class BOTAN_DLL Session + { + public: + + /** + * Uninitialized session + */ + Session() : + m_start_time(0), + m_version(), + m_ciphersuite(0), + m_compression_method(0), + m_connection_side(static_cast<Connection_Side>(0)), + m_secure_renegotiation_supported(false), + m_fragment_size(0) + {} + + /** + * New session (sets session start time) + */ + Session(const MemoryRegion<byte>& session_id, + const MemoryRegion<byte>& master_secret, + Protocol_Version version, + u16bit ciphersuite, + byte compression_method, + Connection_Side side, + bool secure_renegotiation_supported, + size_t fragment_size, + const std::vector<X509_Certificate>& peer_certs, + const MemoryRegion<byte>& session_ticket, + const std::string& sni_hostname = "", + const std::string& srp_identifier = ""); + + /** + * Load a session from DER representation (created by DER_encode) + */ + Session(const byte ber[], size_t ber_len); + + /** + * Load a session from PEM representation (created by PEM_encode) + */ + Session(const std::string& pem); + + /** + * Encode this session data for storage + * @warning if the master secret is compromised so is the + * session traffic + */ + SecureVector<byte> DER_encode() const; + + /** + * Encrypt a session (useful for serialization or session tickets) + */ + MemoryVector<byte> encrypt(const SymmetricKey& key, + RandomNumberGenerator& rng) const; + + + /** + * Decrypt a session created by encrypt + * @param ctext the ciphertext returned by encrypt + * @param ctext_size the size of ctext in bytes + * @param key the same key used by the encrypting side + */ + static Session decrypt(const byte ctext[], + size_t ctext_size, + const SymmetricKey& key); + + /** + * Decrypt a session created by encrypt + * @param ctext the ciphertext returned by encrypt + * @param key the same key used by the encrypting side + */ + static inline Session decrypt(const MemoryRegion<byte>& ctext, + const SymmetricKey& key) + { + return Session::decrypt(&ctext[0], ctext.size(), key); + } + + /** + * Encode this session data for storage + * @warning if the master secret is compromised so is the + * session traffic + */ + std::string PEM_encode() const; + + /** + * Get the version of the saved session + */ + Protocol_Version version() const { return m_version; } + + /** + * Get the ciphersuite code of the saved session + */ + u16bit ciphersuite_code() const { return m_ciphersuite; } + + /** + * Get the ciphersuite info of the saved session + */ + Ciphersuite ciphersuite() const { return Ciphersuite::by_id(m_ciphersuite); } + + /** + * Get the compression method used in the saved session + */ + byte compression_method() const { return m_compression_method; } + + /** + * Get which side of the connection the resumed session we are/were + * acting as. + */ + Connection_Side side() const { return m_connection_side; } + + /** + * Get the SNI hostname (if sent by the client in the initial handshake) + */ + std::string sni_hostname() const { return m_sni_hostname; } + + /** + * Get the SRP identity (if sent by the client in the initial handshake) + */ + std::string srp_identifier() const { return m_srp_identifier; } + + /** + * Get the saved master secret + */ + const SecureVector<byte>& master_secret() const + { return m_master_secret; } + + /** + * Get the session identifier + */ + const MemoryVector<byte>& session_id() const + { return m_identifier; } + + /** + * Get the negotiated maximum fragment size (or 0 if default) + */ + size_t fragment_size() const { return m_fragment_size; } + + /** + * Is secure renegotiation supported? + */ + bool secure_renegotiation() const + { return m_secure_renegotiation_supported; } + + /** + * Return the certificate chain of the peer (possibly empty) + */ + std::vector<X509_Certificate> peer_certs() const { return m_peer_certs; } + + /** + * Get the time this session began (seconds since Epoch) + */ + u64bit start_time() const { return m_start_time; } + + /** + * Return how long this session has existed (in seconds) + */ + u32bit session_age() const; + + /** + * Return the session ticket the server gave us + */ + const MemoryVector<byte>& session_ticket() const { return m_session_ticket; } + + private: + enum { TLS_SESSION_PARAM_STRUCT_VERSION = 0x2994e300 }; + + u64bit m_start_time; + + MemoryVector<byte> m_identifier; + MemoryVector<byte> m_session_ticket; // only used by client side + SecureVector<byte> m_master_secret; + + Protocol_Version m_version; + u16bit m_ciphersuite; + byte m_compression_method; + Connection_Side m_connection_side; + + bool m_secure_renegotiation_supported; + size_t m_fragment_size; + + std::vector<X509_Certificate> m_peer_certs; + std::string m_sni_hostname; // optional + std::string m_srp_identifier; // optional + }; + +} + +} + +#endif diff --git a/src/tls/tls_session_key.cpp b/src/tls/tls_session_key.cpp new file mode 100644 index 000000000..edd0617bc --- /dev/null +++ b/src/tls/tls_session_key.cpp @@ -0,0 +1,89 @@ +/* +* TLS Session Key +* (C) 2004-2006,2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/internal/tls_session_key.h> +#include <botan/internal/tls_handshake_state.h> +#include <botan/internal/tls_messages.h> +#include <botan/lookup.h> +#include <memory> + +namespace Botan { + +namespace TLS { + +/** +* Session_Keys Constructor +*/ +Session_Keys::Session_Keys(Handshake_State* state, + const MemoryRegion<byte>& pre_master_secret, + bool resuming) + { + const size_t mac_keylen = output_length_of(state->suite.mac_algo()); + const size_t cipher_keylen = state->suite.cipher_keylen(); + + size_t cipher_ivlen = 0; + if(have_block_cipher(state->suite.cipher_algo())) + cipher_ivlen = block_size_of(state->suite.cipher_algo()); + + const size_t prf_gen = 2 * (mac_keylen + cipher_keylen + cipher_ivlen); + + 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 }; + + std::auto_ptr<KDF> prf(state->protocol_specific_prf()); + + if(resuming) + { + master_sec = pre_master_secret; + } + else + { + SecureVector<byte> salt; + + if(state->version() != Protocol_Version::SSL_V3) + salt += std::make_pair(MASTER_SECRET_MAGIC, sizeof(MASTER_SECRET_MAGIC)); + + salt += state->client_hello->random(); + salt += state->server_hello->random(); + + master_sec = prf->derive_key(48, pre_master_secret, salt); + } + + SecureVector<byte> salt; + if(state->version() != Protocol_Version::SSL_V3) + salt += std::make_pair(KEY_GEN_MAGIC, sizeof(KEY_GEN_MAGIC)); + salt += state->server_hello->random(); + salt += state->client_hello->random(); + + SymmetricKey keyblock = prf->derive_key(prf_gen, master_sec, salt); + + 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..25de56aea --- /dev/null +++ b/src/tls/tls_session_key.h @@ -0,0 +1,52 @@ +/* +* TLS Session Key +* (C) 2004-2006,2011 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_ciphersuite.h> +#include <botan/tls_exceptn.h> +#include <botan/symkey.h> + +namespace Botan { + +namespace TLS { + +/** +* TLS Session Keys +*/ +class Session_Keys + { + public: + SymmetricKey client_cipher_key() const { return c_cipher; } + SymmetricKey server_cipher_key() const { return s_cipher; } + + SymmetricKey client_mac_key() const { return c_mac; } + SymmetricKey server_mac_key() const { return s_mac; } + + InitializationVector client_iv() const { return c_iv; } + InitializationVector server_iv() const { return s_iv; } + + const SecureVector<byte>& master_secret() const { return master_sec; } + + Session_Keys() {} + + Session_Keys(class Handshake_State* state, + const MemoryRegion<byte>& pre_master, + bool resuming); + + private: + 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_session_manager.cpp b/src/tls/tls_session_manager.cpp new file mode 100644 index 000000000..812525d69 --- /dev/null +++ b/src/tls/tls_session_manager.cpp @@ -0,0 +1,96 @@ +/* +* TLS Session Management +* (C) 2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_session_manager.h> +#include <botan/hex.h> +#include <botan/time.h> + +namespace Botan { + +namespace TLS { + +bool Session_Manager_In_Memory::load_from_session_str( + const std::string& session_str, Session& session) + { + std::map<std::string, Session>::iterator i = m_sessions.find(session_str); + + if(i == m_sessions.end()) + return false; + + // session has expired, remove it + const u64bit now = system_time(); + if(i->second.start_time() + session_lifetime() < now) + { + m_sessions.erase(i); + return false; + } + + session = i->second; + return true; + } + +bool Session_Manager_In_Memory::load_from_session_id( + const MemoryRegion<byte>& session_id, Session& session) + { + return load_from_session_str(hex_encode(session_id), session); + } + +bool Session_Manager_In_Memory::load_from_host_info( + const std::string& hostname, u16bit port, Session& session) + { + std::map<std::string, std::string>::iterator i; + + if(port > 0) + i = m_host_sessions.find(hostname + ":" + to_string(port)); + else + i = m_host_sessions.find(hostname); + + if(i == m_host_sessions.end()) + return false; + + if(load_from_session_str(i->second, session)) + return true; + + // was removed from m_sessions map, remove m_host_sessions entry + m_host_sessions.erase(i); + + return false; + } + +void Session_Manager_In_Memory::remove_entry( + const MemoryRegion<byte>& session_id) + { + std::map<std::string, Session>::iterator i = + m_sessions.find(hex_encode(session_id)); + + if(i != m_sessions.end()) + m_sessions.erase(i); + } + +void Session_Manager_In_Memory::save(const Session& session) + { + if(m_max_sessions != 0) + { + /* + This removes randomly based on ordering of session ids. + Instead, remove oldest first? + */ + while(m_sessions.size() >= m_max_sessions) + m_sessions.erase(m_sessions.begin()); + } + + const std::string session_id_str = hex_encode(session.session_id()); + + m_sessions[session_id_str] = session; + + if(session.side() == CLIENT && session.sni_hostname() != "") + m_host_sessions[session.sni_hostname()] = session_id_str; + } + +} + +} diff --git a/src/tls/tls_session_manager.h b/src/tls/tls_session_manager.h new file mode 100644 index 000000000..3a39bf50e --- /dev/null +++ b/src/tls/tls_session_manager.h @@ -0,0 +1,125 @@ +/* +* TLS Session Manager +* (C) 2011 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef TLS_SESSION_MANAGER_H__ +#define TLS_SESSION_MANAGER_H__ + +#include <botan/tls_session.h> +#include <map> + +namespace Botan { + +namespace TLS { + +/** +* Session_Manager is an interface to systems which can save +* session parameters for supporting session resumption. +* +* Saving sessions is done on a best-effort basis; an implementation is +* allowed to drop sessions due to space constraints. +* +* Implementations should strive to be thread safe +*/ +class BOTAN_DLL Session_Manager + { + public: + /** + * Try to load a saved session (server side) + * @param session_id the session identifier we are trying to resume + * @param session will be set to the saved session data (if found), + or not modified if not found + * @return true if session was modified + */ + virtual bool load_from_session_id(const MemoryRegion<byte>& session_id, + Session& session) = 0; + + /** + * Try to load a saved session (client side) + * @param hostname of the host we are connecting to + * @param port the port number if we know it, or 0 if unknown + * @param session will be set to the saved session data (if found), + or not modified if not found + * @return true if session was modified + */ + virtual bool load_from_host_info(const std::string& hostname, u16bit port, + Session& session) = 0; + + /** + * Remove this session id from the cache, if it exists + */ + virtual void remove_entry(const MemoryRegion<byte>& session_id) = 0; + + /** + * Save a session on a best effort basis; the manager may not in + * fact be able to save the session for whatever reason; this is + * not an error. Caller cannot assume that calling save followed + * immediately by load_from_* will result in a successful lookup. + * + * @param session to save + */ + virtual void save(const Session& session) = 0; + + /** + * Return the allowed lifetime of a session; beyond this time, + * sessions are not resumed. Returns 0 if unknown/no explicit + * expiration policy. + */ + virtual u32bit session_lifetime() const = 0; + + virtual ~Session_Manager() {} + }; + +/** +* A simple implementation of Session_Manager that just saves +* values in memory, with no persistance abilities +* +* @todo add locking +*/ +class BOTAN_DLL Session_Manager_In_Memory : public Session_Manager + { + public: + /** + * @param max_sessions a hint on the maximum number of sessions + * to keep in memory at any one time. (If zero, don't cap) + * @param session_lifetime sessions are expired after this many + * seconds have elapsed from initial handshake. + */ + Session_Manager_In_Memory(size_t max_sessions = 1000, + u32bit session_lifetime = 7200) : + m_max_sessions(max_sessions), + m_session_lifetime(session_lifetime) + {} + + bool load_from_session_id(const MemoryRegion<byte>& session_id, + Session& session); + + bool load_from_host_info(const std::string& hostname, u16bit port, + Session& session); + + void remove_entry(const MemoryRegion<byte>& session_id); + + void save(const Session& session_data); + + u32bit session_lifetime() const { return m_session_lifetime; } + + private: + bool load_from_session_str(const std::string& session_str, + Session& session); + + size_t m_max_sessions; + + u32bit m_session_lifetime; + + std::map<std::string, Session> m_sessions; // hex(session_id) -> session + std::map<std::string, std::string> m_host_sessions; + }; + +} + +} + +#endif diff --git a/src/tls/tls_suite_info.cpp b/src/tls/tls_suite_info.cpp new file mode 100644 index 000000000..0b76842af --- /dev/null +++ b/src/tls/tls_suite_info.cpp @@ -0,0 +1,317 @@ +/* +* TLS Cipher Suite +* (C) 2004-2010,2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_ciphersuite.h> + +namespace Botan { + +namespace TLS { + +Ciphersuite Ciphersuite::by_id(u16bit suite) + { + // Automatically generated by a Python script from the IANA values + + switch(suite) + { + case 0x0013: // DHE_DSS_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "3DES", 24); + + case 0x0032: // DHE_DSS_WITH_AES_128_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "AES-128", 16); + + case 0x0040: // DHE_DSS_WITH_AES_128_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "AES-128", 16); + + case 0x0038: // DHE_DSS_WITH_AES_256_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "AES-256", 32); + + case 0x006A: // DHE_DSS_WITH_AES_256_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "AES-256", 32); + + case 0x0044: // DHE_DSS_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "Camellia", 16); + + case 0x00BD: // DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "Camellia", 16); + + case 0x0087: // DHE_DSS_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "Camellia", 32); + + case 0x00C3: // DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("DSA", "DH", "SHA-256", "Camellia", 32); + + case 0x0066: // DHE_DSS_WITH_RC4_128_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "ARC4", 16); + + case 0x0099: // DHE_DSS_WITH_SEED_CBC_SHA + return Ciphersuite("DSA", "DH", "SHA-1", "SEED", 16); + + case 0x008F: // DHE_PSK_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "3DES", 24); + + case 0x0090: // DHE_PSK_WITH_AES_128_CBC_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-128", 16); + + case 0x00B2: // DHE_PSK_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "DHE_PSK", "SHA-256", "AES-128", 16); + + case 0x0091: // DHE_PSK_WITH_AES_256_CBC_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "AES-256", 32); + + case 0x00B3: // DHE_PSK_WITH_AES_256_CBC_SHA384 + return Ciphersuite("", "DHE_PSK", "SHA-384", "AES-256", 32); + + case 0xC096: // DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "DHE_PSK", "SHA-256", "Camellia", 16); + + case 0xC097: // DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("", "DHE_PSK", "SHA-384", "Camellia", 32); + + case 0x008E: // DHE_PSK_WITH_RC4_128_SHA + return Ciphersuite("", "DHE_PSK", "SHA-1", "ARC4", 16); + + case 0x0016: // DHE_RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "3DES", 24); + + case 0x0033: // DHE_RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "AES-128", 16); + + case 0x0067: // DHE_RSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "AES-128", 16); + + case 0x0039: // DHE_RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "AES-256", 32); + + case 0x006B: // DHE_RSA_WITH_AES_256_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "AES-256", 32); + + case 0x0045: // DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "Camellia", 16); + + case 0x00BE: // DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "Camellia", 16); + + case 0x0088: // DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "Camellia", 32); + + case 0x00C4: // DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("RSA", "DH", "SHA-256", "Camellia", 32); + + case 0x009A: // DHE_RSA_WITH_SEED_CBC_SHA + return Ciphersuite("RSA", "DH", "SHA-1", "SEED", 16); + + case 0x001B: // DH_anon_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "3DES", 24); + + case 0x0034: // DH_anon_WITH_AES_128_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "AES-128", 16); + + case 0x006C: // DH_anon_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "AES-128", 16); + + case 0x003A: // DH_anon_WITH_AES_256_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "AES-256", 32); + + case 0x006D: // DH_anon_WITH_AES_256_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "AES-256", 32); + + case 0x0046: // DH_anon_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "Camellia", 16); + + case 0x00BF: // DH_anon_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "Camellia", 16); + + case 0x0089: // DH_anon_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "Camellia", 32); + + case 0x00C5: // DH_anon_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("", "DH", "SHA-256", "Camellia", 32); + + case 0x0018: // DH_anon_WITH_RC4_128_MD5 + return Ciphersuite("", "DH", "MD5", "ARC4", 16); + + case 0x009B: // DH_anon_WITH_SEED_CBC_SHA + return Ciphersuite("", "DH", "SHA-1", "SEED", 16); + + case 0xC008: // ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "3DES", 24); + + case 0xC009: // ECDHE_ECDSA_WITH_AES_128_CBC_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "AES-128", 16); + + case 0xC023: // ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("ECDSA", "ECDH", "SHA-256", "AES-128", 16); + + case 0xC00A: // ECDHE_ECDSA_WITH_AES_256_CBC_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "AES-256", 32); + + case 0xC024: // ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + return Ciphersuite("ECDSA", "ECDH", "SHA-384", "AES-256", 32); + + case 0xC072: // ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("ECDSA", "ECDH", "SHA-256", "Camellia", 16); + + case 0xC073: // ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("ECDSA", "ECDH", "SHA-384", "Camellia", 32); + + case 0xC007: // ECDHE_ECDSA_WITH_RC4_128_SHA + return Ciphersuite("ECDSA", "ECDH", "SHA-1", "ARC4", 16); + + case 0xC034: // ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "3DES", 24); + + case 0xC035: // ECDHE_PSK_WITH_AES_128_CBC_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "AES-128", 16); + + case 0xC037: // ECDHE_PSK_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "ECDHE_PSK", "SHA-256", "AES-128", 16); + + case 0xC036: // ECDHE_PSK_WITH_AES_256_CBC_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "AES-256", 32); + + case 0xC038: // ECDHE_PSK_WITH_AES_256_CBC_SHA384 + return Ciphersuite("", "ECDHE_PSK", "SHA-384", "AES-256", 32); + + case 0xC09A: // ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "ECDHE_PSK", "SHA-256", "Camellia", 16); + + case 0xC09B: // ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("", "ECDHE_PSK", "SHA-384", "Camellia", 32); + + case 0xC033: // ECDHE_PSK_WITH_RC4_128_SHA + return Ciphersuite("", "ECDHE_PSK", "SHA-1", "ARC4", 16); + + case 0xC012: // ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "3DES", 24); + + case 0xC013: // ECDHE_RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "AES-128", 16); + + case 0xC027: // ECDHE_RSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("RSA", "ECDH", "SHA-256", "AES-128", 16); + + case 0xC014: // ECDHE_RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "AES-256", 32); + + case 0xC028: // ECDHE_RSA_WITH_AES_256_CBC_SHA384 + return Ciphersuite("RSA", "ECDH", "SHA-384", "AES-256", 32); + + case 0xC076: // ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("RSA", "ECDH", "SHA-256", "Camellia", 16); + + case 0xC077: // ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("RSA", "ECDH", "SHA-384", "Camellia", 32); + + case 0xC011: // ECDHE_RSA_WITH_RC4_128_SHA + return Ciphersuite("RSA", "ECDH", "SHA-1", "ARC4", 16); + + case 0xC017: // ECDH_anon_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "ECDH", "SHA-1", "3DES", 24); + + case 0xC018: // ECDH_anon_WITH_AES_128_CBC_SHA + return Ciphersuite("", "ECDH", "SHA-1", "AES-128", 16); + + case 0xC019: // ECDH_anon_WITH_AES_256_CBC_SHA + return Ciphersuite("", "ECDH", "SHA-1", "AES-256", 32); + + case 0xC016: // ECDH_anon_WITH_RC4_128_SHA + return Ciphersuite("", "ECDH", "SHA-1", "ARC4", 16); + + case 0x008B: // PSK_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "PSK", "SHA-1", "3DES", 24); + + case 0x008C: // PSK_WITH_AES_128_CBC_SHA + return Ciphersuite("", "PSK", "SHA-1", "AES-128", 16); + + case 0x00AE: // PSK_WITH_AES_128_CBC_SHA256 + return Ciphersuite("", "PSK", "SHA-256", "AES-128", 16); + + case 0x008D: // PSK_WITH_AES_256_CBC_SHA + return Ciphersuite("", "PSK", "SHA-1", "AES-256", 32); + + case 0x00AF: // PSK_WITH_AES_256_CBC_SHA384 + return Ciphersuite("", "PSK", "SHA-384", "AES-256", 32); + + case 0xC094: // PSK_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("", "PSK", "SHA-256", "Camellia", 16); + + case 0xC095: // PSK_WITH_CAMELLIA_256_CBC_SHA384 + return Ciphersuite("", "PSK", "SHA-384", "Camellia", 32); + + case 0x008A: // PSK_WITH_RC4_128_SHA + return Ciphersuite("", "PSK", "SHA-1", "ARC4", 16); + + case 0x000A: // RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "3DES", 24); + + case 0x002F: // RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "AES-128", 16); + + case 0x003C: // RSA_WITH_AES_128_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "AES-128", 16); + + case 0x0035: // RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "AES-256", 32); + + case 0x003D: // RSA_WITH_AES_256_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "AES-256", 32); + + case 0x0041: // RSA_WITH_CAMELLIA_128_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "Camellia", 16); + + case 0x00BA: // RSA_WITH_CAMELLIA_128_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "Camellia", 16); + + case 0x0084: // RSA_WITH_CAMELLIA_256_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "Camellia", 32); + + case 0x00C0: // RSA_WITH_CAMELLIA_256_CBC_SHA256 + return Ciphersuite("RSA", "RSA", "SHA-256", "Camellia", 32); + + case 0x0004: // RSA_WITH_RC4_128_MD5 + return Ciphersuite("RSA", "RSA", "MD5", "ARC4", 16); + + case 0x0005: // RSA_WITH_RC4_128_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "ARC4", 16); + + case 0x0096: // RSA_WITH_SEED_CBC_SHA + return Ciphersuite("RSA", "RSA", "SHA-1", "SEED", 16); + + case 0xC01C: // SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("DSA", "SRP_SHA", "SHA-1", "3DES", 24); + + case 0xC01F: // SRP_SHA_DSS_WITH_AES_128_CBC_SHA + return Ciphersuite("DSA", "SRP_SHA", "SHA-1", "AES-128", 16); + + case 0xC022: // SRP_SHA_DSS_WITH_AES_256_CBC_SHA + return Ciphersuite("DSA", "SRP_SHA", "SHA-1", "AES-256", 32); + + case 0xC01B: // SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("RSA", "SRP_SHA", "SHA-1", "3DES", 24); + + case 0xC01E: // SRP_SHA_RSA_WITH_AES_128_CBC_SHA + return Ciphersuite("RSA", "SRP_SHA", "SHA-1", "AES-128", 16); + + case 0xC021: // SRP_SHA_RSA_WITH_AES_256_CBC_SHA + return Ciphersuite("RSA", "SRP_SHA", "SHA-1", "AES-256", 32); + + case 0xC01A: // SRP_SHA_WITH_3DES_EDE_CBC_SHA + return Ciphersuite("", "SRP_SHA", "SHA-1", "3DES", 24); + + case 0xC01D: // SRP_SHA_WITH_AES_128_CBC_SHA + return Ciphersuite("", "SRP_SHA", "SHA-1", "AES-128", 16); + + case 0xC020: // SRP_SHA_WITH_AES_256_CBC_SHA + return Ciphersuite("", "SRP_SHA", "SHA-1", "AES-256", 32); + } + + return Ciphersuite(); // some unknown ciphersuite + } + +} + +} diff --git a/src/tls/tls_version.cpp b/src/tls/tls_version.cpp new file mode 100644 index 000000000..4445998eb --- /dev/null +++ b/src/tls/tls_version.cpp @@ -0,0 +1,33 @@ +/* +* TLS Protocol Version Management +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#include <botan/tls_version.h> +#include <botan/parsing.h> + +namespace Botan { + +namespace TLS { + +std::string Protocol_Version::to_string() const + { + const byte maj = major_version(); + const byte min = minor_version(); + + // Some very new or very old protocol? + if(maj != 3) + return "Protocol " + Botan::to_string(maj) + "." + Botan::to_string(min); + + if(maj == 3 && min == 0) + return "SSL v3"; + + // The TLS v1.[0123...] case + return "TLS v1." + Botan::to_string(min-1); + } + +} + +} diff --git a/src/tls/tls_version.h b/src/tls/tls_version.h new file mode 100644 index 000000000..aa689b300 --- /dev/null +++ b/src/tls/tls_version.h @@ -0,0 +1,87 @@ +/* +* TLS Protocol Version Management +* (C) 2012 Jack Lloyd +* +* Released under the terms of the Botan license +*/ + +#ifndef BOTAN_TLS_PROTOCOL_VERSION_H__ +#define BOTAN_TLS_PROTOCOL_VERSION_H__ + +#include <botan/get_byte.h> +#include <string> + +namespace Botan { + +namespace TLS { + +class BOTAN_DLL Protocol_Version + { + public: + enum Version_Code { + SSL_V3 = 0x0300, + TLS_V10 = 0x0301, + TLS_V11 = 0x0302, + TLS_V12 = 0x0303 + }; + + Protocol_Version() : m_version(0) {} + + Protocol_Version(Version_Code named_version) : + m_version(static_cast<u16bit>(named_version)) {} + + Protocol_Version(byte major, byte minor) : + m_version((static_cast<u16bit>(major) << 8) | minor) {} + + /** + * Get the major version of the protocol version + */ + byte major_version() const { return get_byte(0, m_version); } + + /** + * Get the minor version of the protocol version + */ + byte minor_version() const { return get_byte(1, m_version); } + + bool operator==(const Protocol_Version& other) const + { + return (m_version == other.m_version); + } + + bool operator!=(const Protocol_Version& other) const + { + return (m_version != other.m_version); + } + + bool operator>=(const Protocol_Version& other) const + { + return (m_version >= other.m_version); + } + + bool operator>(const Protocol_Version& other) const + { + return (m_version > other.m_version); + } + + bool operator<=(const Protocol_Version& other) const + { + return (m_version <= other.m_version); + } + + bool operator<(const Protocol_Version& other) const + { + return (m_version < other.m_version); + } + + std::string to_string() const; + + private: + u16bit m_version; + }; + +} + +} + +#endif + diff --git a/src/utils/assert.cpp b/src/utils/assert.cpp index 29af831d8..3747912d6 100644 --- a/src/utils/assert.cpp +++ b/src/utils/assert.cpp @@ -21,7 +21,7 @@ void assertion_failure(const char* expr_str, format << "Assertion " << expr_str << " failed "; - if(msg) + if(msg && msg[0] != 0) format << "(" << msg << ") "; if(func) diff --git a/src/utils/assert.h b/src/utils/assert.h index 67ca665e3..d68f683a6 100644 --- a/src/utils/assert.h +++ b/src/utils/assert.h @@ -36,6 +36,16 @@ void assertion_failure(const char* expr_str, __LINE__); \ } while(0) +#define BOTAN_ASSERT_NONNULL(ptr) \ + do { \ + if(static_cast<bool>(ptr) == false) \ + Botan::assertion_failure(#ptr " is not null", \ + "", \ + BOTAN_ASSERT_FUNCTION, \ + __FILE__, \ + __LINE__); \ + } while(0) + /* * Unfortunately getting the function name from the preprocessor * isn't standard in C++98 (C++0x uses C99's __func__) diff --git a/src/utils/parsing.cpp b/src/utils/parsing.cpp index 9ec00040c..25f021c8c 100644 --- a/src/utils/parsing.cpp +++ b/src/utils/parsing.cpp @@ -288,4 +288,17 @@ std::string ipv4_to_string(u32bit ip) return str; } +std::string replace_char(const std::string& str, + char from_char, + char to_char) + { + std::string out = str; + + for(size_t i = 0; i != out.size(); ++i) + if(out[i] == from_char) + out[i] = to_char; + + return out; + } + } diff --git a/src/utils/parsing.h b/src/utils/parsing.h index 12370bf2b..668272309 100644 --- a/src/utils/parsing.h +++ b/src/utils/parsing.h @@ -32,6 +32,16 @@ BOTAN_DLL std::vector<std::string> split_on( const std::string& str, char delim); /** +* Replace a character in a string +* @param str the input string +* @param from_char the character to replace +* @return to_char the character to replace it with +*/ +BOTAN_DLL std::string replace_char(const std::string& str, + char from_char, + char to_char); + +/** * Parse an ASN.1 OID * @param oid the OID in string form * @return OID components diff --git a/src/utils/rounding.h b/src/utils/rounding.h index c77ab9b52..4ddd7a432 100644 --- a/src/utils/rounding.h +++ b/src/utils/rounding.h @@ -21,6 +21,9 @@ namespace Botan { template<typename T> inline T round_up(T n, T align_to) { + if(align_to == 0) + return n; + if(n % align_to || n == 0) n += align_to - (n % align_to); return n; @@ -35,9 +38,24 @@ inline T round_up(T n, T align_to) template<typename T> inline T round_down(T n, T align_to) { + if(align_to == 0) + return n; + return (n - (n % align_to)); } +/** +* Clamp +*/ +inline size_t clamp(size_t n, size_t lower_bound, size_t upper_bound) + { + if(n < lower_bound) + return lower_bound; + if(n > upper_bound) + return upper_bound; + return n; + } + } #endif diff --git a/src/utils/stl_util.h b/src/utils/stl_util.h index 0e0617d5b..9ae5c5f7a 100644 --- a/src/utils/stl_util.h +++ b/src/utils/stl_util.h @@ -84,6 +84,19 @@ void multimap_insert(std::multimap<K, V>& multimap, #endif } +/** +* Existence check for values +*/ +template<typename T> +bool value_exists(const std::vector<T>& vec, + const T& val) + { + for(size_t i = 0; i != vec.size(); ++i) + if(vec[i] == val) + return true; + return false; + } + } #endif |