aboutsummaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/contents.txt3
-rw-r--r--doc/examples/GNUmakefile3
-rw-r--r--doc/examples/asio_tls_server.cpp323
-rw-r--r--doc/examples/cms_dec.cpp120
-rw-r--r--doc/examples/cms_enc.cpp59
-rw-r--r--doc/examples/credentials.h95
-rw-r--r--doc/examples/self_sig.cpp3
-rw-r--r--doc/examples/socket.h20
-rw-r--r--doc/examples/tls_client.cpp233
-rw-r--r--doc/examples/tls_server.cpp180
-rw-r--r--doc/index.txt2
-rw-r--r--doc/ssl.txt58
-rw-r--r--doc/tls.txt148
13 files changed, 918 insertions, 329 deletions
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..23045961e 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
+ $(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..fff83f5f8
--- /dev/null
+++ b/doc/examples/asio_tls_server.cpp
@@ -0,0 +1,323 @@
+#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;
+ };
+
+int main()
+ {
+ try
+ {
+ Botan::LibraryInitializer init("thread_safe=true");
+ boost::asio::io_service io_service;
+
+ unsigned short port = 4433;
+ tls_server server(io_service, port);
+
+ size_t num_threads = boost::thread::hardware_concurrency();
+
+ if(num_threads == 0)
+ return num_threads = 2;
+
+ 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..160fec772
--- /dev/null
+++ b/doc/examples/credentials.h
@@ -0,0 +1,95 @@
+
+#ifndef EXAMPLE_CREDENTIALS_MANAGER_H__
+#define EXAMPLE_CREDENTIALS_MANAGER_H__
+
+#include <botan/credentials_manager.h>
+#include <iostream>
+
+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(const std::string&, const std::string&,
+ const std::string& identity_hint)
+ {
+ return "Client_identity";
+ }
+
+ Botan::SymmetricKey psk(const std::string&, const std::string&,
+ const std::string& identity)
+ {
+ if(identity == "Client_identity")
+ return Botan::SymmetricKey("AABBCC");
+ throw Botan::Internal_Error("No PSK set for " + identity);
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ const std::string& context)
+ {
+ std::vector<Botan::X509_Certificate> certs;
+
+ try
+ {
+ if(type == "tls-server")
+ {
+ const std::string hostname = (context == "" ? "localhost" : context);
+
+ if(value_exists(cert_key_types, "RSA"))
+ {
+ Botan::X509_Certificate cert(hostname + ".crt");
+ Botan::Private_Key* key = Botan::PKCS8::load_key(hostname + ".key", rng);
+
+ certs_and_keys[cert] = key;
+ certs.push_back(cert);
+ }
+ else if(value_exists(cert_key_types, "DSA"))
+ {
+ Botan::X509_Certificate cert(hostname + ".dsa.crt");
+ Botan::Private_Key* key = Botan::PKCS8::load_key(hostname + ".dsa.key", rng);
+
+ certs_and_keys[cert] = key;
+ certs.push_back(cert);
+ }
+ }
+ else if(type == "tls-client")
+ {
+ Botan::X509_Certificate cert("user-rsa.crt");
+ Botan::Private_Key* key = Botan::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;
+ 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..1cca002af 100644
--- a/doc/examples/tls_client.cpp
+++ b/doc/examples/tls_client.cpp
@@ -1,91 +1,222 @@
#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>
+
+#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());
- std::cout << "Warning: not checking cert signatures\n";
+ 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;
+ }
- return true;
+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";
+
+ return true;
+ }
+
+void socket_write(int sockfd, const byte buf[], size_t length)
+ {
+ size_t offset = 0;
+
+ 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;
+ TLS::Session_Manager_In_Memory session_manager;
+
+ 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..8c2b815b6
--- /dev/null
+++ b/doc/tls.txt
@@ -0,0 +1,148 @@
+
+.. _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.
+
+ 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