diff options
Diffstat (limited to 'doc/examples')
-rw-r--r-- | doc/examples/GNUmakefile | 3 | ||||
-rw-r--r-- | doc/examples/asio_tls_server.cpp | 330 | ||||
-rw-r--r-- | doc/examples/cms_dec.cpp | 120 | ||||
-rw-r--r-- | doc/examples/cms_enc.cpp | 59 | ||||
-rw-r--r-- | doc/examples/credentials.h | 178 | ||||
-rw-r--r-- | doc/examples/self_sig.cpp | 3 | ||||
-rw-r--r-- | doc/examples/socket.h | 20 | ||||
-rw-r--r-- | doc/examples/tls_client.cpp | 244 | ||||
-rw-r--r-- | doc/examples/tls_server.cpp | 180 |
9 files changed, 868 insertions, 269 deletions
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) |