aboutsummaryrefslogtreecommitdiffstats
path: root/doc/api_ref/tls.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/api_ref/tls.rst')
-rw-r--r--doc/api_ref/tls.rst1844
1 files changed, 1844 insertions, 0 deletions
diff --git a/doc/api_ref/tls.rst b/doc/api_ref/tls.rst
new file mode 100644
index 000000000..74f8bf79a
--- /dev/null
+++ b/doc/api_ref/tls.rst
@@ -0,0 +1,1844 @@
+Transport Layer Security (TLS)
+========================================
+
+.. versionadded:: 1.11.0
+
+Botan has client and server implementations of various versions of the
+TLS protocol, including TLS v1.0, TLS v1.1, and TLS v1.2. As of
+version 1.11.13, support for the insecure SSLv3 protocol has been
+removed.
+
+There is also support for DTLS (v1.0 and v1.2), a variant of TLS
+adapted for operation on datagram transports such as UDP and
+SCTP. DTLS support should be considered as beta quality and further
+testing is invited.
+
+The TLS implementation does not know anything about sockets or the
+network layer. Instead, it calls a user provided callback (hereafter
+``output_fn``) whenever it has data that it would want to send to the
+other party (for instance, by writing it to a network socket), and
+whenever the application receives some data from the counterparty (for
+instance, by reading from a network socket) it passes that information
+to TLS using :cpp:func:`TLS::Channel::received_data`. If the data
+passed in results in some change in the state, such as a handshake
+completing, or some data or an alert being received from the other
+side, then the appropriate user provided callback will be invoked.
+
+If the reader is familiar with OpenSSL's BIO layer, it might be analogous
+to saying the only way of interacting with Botan's TLS is via a `BIO_mem` I/O
+abstraction. This makes the library completely agnostic to how you
+write your network layer, be it blocking sockets, libevent, asio, a
+message queue, lwIP on RTOS, some carrier pidgeons, etc.
+
+Starting in 1.11.31, the application callbacks are encapsulated as the class
+``TLS::Callbacks`` with the following members. The first four (``tls_emit_data``,
+``tls_record_received``, ``tls_alert``, and ``tls_session_established``) are
+mandatory for using TLS, all others are optional and provide additional
+information about the connection.
+
+ .. cpp:function:: void tls_emit_data(const uint8_t data[], size_t data_len)
+
+ Mandatory. The TLS stack requests that all bytes of *data* be queued up to send to the
+ counterparty. After this function returns, the buffer containing *data* will
+ be overwritten, so a copy of the input must be made if the callback
+ cannot send the data immediately.
+
+ As an example you could ``send`` to perform a blocking write on a socket,
+ or append the data to a queue managed by your application, and initiate
+ an asynchronous write.
+
+ For TLS all writes must occur *in the order requested*.
+ For DTLS this ordering is not strictly required, but is still recommended.
+
+ .. cpp:function:: void tls_record_received(uint64_t rec_no, const uint8_t data[], size_t data_len)
+
+ Mandatory. Called once for each application_data record which is received, with the
+ matching (TLS level) record sequence number.
+
+ Currently empty records are ignored and do not instigate a callback,
+ but this may change in a future release.
+
+ As with ``tls_emit_data``, the array will be overwritten sometime after
+ the callback returns, so a copy should be made if needed.
+
+ For TLS the record number will always increase.
+
+ For DTLS, it is possible to receive records with the `rec_no` field out of
+ order, or with gaps, corresponding to reordered or lost datagrams.
+
+ .. cpp:function:: void tls_alert(Alert alert)
+
+ Mandatory. Called when an alert is received from the peer. Note that alerts
+ received before the handshake is complete are not authenticated and
+ could have been inserted by a MITM attacker.
+
+ .. cpp:function:: bool tls_session_established(const TLS::Session& session)
+
+ Mandatory. Called whenever a negotiation completes. This can happen more
+ than once on any connection, if renegotiation occurs. The *session* parameter
+ provides information about the session which was just established.
+
+ If this function returns false, the session will not be cached
+ for later resumption.
+
+ If this function wishes to cancel the handshake, it can throw an
+ exception which will send a close message to the counterparty and
+ reset the connection state.
+
+ .. cpp::function:: void tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain, \
+ const std::vector<std::shared_ptr<const OCSP::Response>>& ocsp_responses, \
+ const std::vector<Certificate_Store*>& trusted_roots, \
+ Usage_Type usage, \
+ const std::string& hostname, \
+ const Policy& policy)
+
+ Optional - default implementation should work for many users.
+ It can be overridden for implementing extra validation routines
+ such as public key pinning.
+
+ Verifies the certificate chain in *cert_chain*, assuming the leaf
+ certificate is the first element. Throws an exception if any
+ error makes this certificate chain unacceptable.
+
+ If usage is `Usage_Type::TLS_SERVER_AUTH`, then *hostname* should
+ match the information in the server certificate. If usage is
+ `TLS_CLIENT_AUTH`, then *hostname* specifies the host the client
+ is authenticating against (from SNI); the callback can use this for
+ any special site specific auth logic.
+
+ The `ocsp_responses` is a possibly empty list of OCSP responses provided by
+ the server. In the current implementation of TLS OCSP stapling, only a
+ single OCSP response can be returned. A existing TLS extension allows the
+ server to send multiple OCSP responses, this extension may be supported in
+ the future in which case more than one OCSP response may be given during
+ this callback.
+
+ The `trusted_roots` parameter was returned by a call from the associated
+ `Credentials_Manager`.
+
+ The `policy` provided is the policy for the TLS session which is
+ being authenticated using this certificate chain. It can be consulted
+ for values such as allowable signature methods and key sizes.
+
+ .. cpp::function:: std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const
+
+ Called by default `tls_verify_cert_chain` to set timeout for online OCSP requests
+ on the certificate chain. Return 0 to disable OCSP. Current default is 0.
+
+ .. cpp:function:: std::string tls_server_choose_app_protocol(const std::vector<std::string>& client_protos)
+
+ Optional. Called by the server when a client includes a list of protocols in the ALPN extension.
+ The server then choose which protocol to use, or "" to disable sending any ALPN response.
+ The default implementation returns the empty string all of the time, effectively disabling
+ ALPN responses.
+
+ .. cpp:function:: void tls_inspect_handshake_msg(const Handshake_Message&)
+
+ This callback is optional, and can be used to inspect all handshake messages
+ while the session establishment occurs.
+
+ .. cpp:function:: void tls_modify_extensions(Extensions& extn, Connection_Side which_side)
+
+ This callback is optional, and can be used to modify extensions before they
+ are sent to the peer. For example this enables adding a custom extension,
+ or replacing or removing an extension set by the library.
+
+ .. cpp:function:: void tls_examine_extensions(const Extensions& extn, Connection_Side which_side)
+
+ This callback is optional, and can be used to examine extensions sent by
+ the peer.
+
+ .. cpp:function:: void tls_log_error(const char* msg)
+
+ Optional logging for an error message. (Not currently used)
+
+ .. cpp:function:: void tls_log_debug(const char* msg)
+
+ Optional logging for an debug message. (Not currently used)
+
+ .. cpp:function:: void tls_log_debug_bin(const char* descr, const uint8_t val[], size_t len)
+
+ Optional logging for an debug value. (Not currently used)
+
+ .. cpp:function:: std::string tls_decode_group_param(TLS::Group_Params group_param)
+
+ Optional. Called by the server when a client hello includes a list of supported groups in the
+ supported_groups extension and by the client when decoding the server key exchange including the selected curve identifier.
+ The function should return the name of the DH group or elliptic curve the passed
+ TLS group identifier should be mapped to. Therefore this callback enables the use of custom
+ elliptic curves or DH groups in TLS, if both client and server map the custom identifiers correctly.
+ Please note that it is required to allow the group TLS identifier in
+ in the used :cpp:class:`TLS::Policy`.
+
+Versions from 1.11.0 to 1.11.30 did not have ``TLS::Callbacks`` and instead
+used independent std::functions to pass the various callback functions.
+This interface is currently still included but is deprecated and will be removed
+in a future release. For the documentation for this interface, please check
+the docs for 1.11.30. This version of the manual only documents the new interface
+added in 1.11.31.
+
+TLS Channels
+----------------------------------------
+
+TLS servers and clients share an interface called `TLS::Channel`. A
+TLS channel (either client or server object) has these methods
+available:
+
+.. cpp:class:: TLS::Channel
+
+ .. cpp:function:: size_t received_data(const uint8_t buf[], size_t buf_size)
+ .. cpp:function:: size_t received_data(const std::vector<uint8_t>& buf)
+
+ 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.
+
+ The return value of ``received_data`` specifies how many more
+ bytes of input are needed to make any progress, unless the end of
+ the data fell exactly on a message boundary, in which case it
+ will return 0 instead.
+
+ .. cpp:function:: void send(const uint8_t buf[], size_t buf_size)
+ .. cpp:function:: void send(const std::string& str)
+ .. cpp:function:: void send(const std::vector<uint8_t>& vec)
+
+ Create one or more new TLS application records containing the
+ provided data and send them. This will eventually result in at
+ least one call to the ``output_fn`` callback before ``send``
+ returns.
+
+ If the current TLS connection state is unable to transmit new
+ application records (for example because a handshake has not
+ yet completed or the connection has already ended due to an
+ error) an exception will be thrown.
+
+ .. cpp:function:: void close()
+
+ A close notification is sent to the counterparty, and the
+ internal state is cleared.
+
+ .. cpp:function:: void send_alert(const Alert& alert)
+
+ Some other alert is sent to the counterparty. If the alert is
+ fatal, the internal state is cleared.
+
+ .. cpp:function:: bool is_active()
+
+ Returns true if and only if a handshake has been completed on
+ this connection and the connection has not been subsequently
+ closed.
+
+ .. cpp:function:: bool is_closed()
+
+ Returns true if and only if either a close notification or a
+ fatal alert message have been either sent or received.
+
+ .. cpp:function:: bool timeout_check()
+
+ This function does nothing unless the channel represents a DTLS
+ connection and a handshake is actively in progress. In this case
+ it will check the current timeout state and potentially initiate
+ retransmission of handshake packets. Returns true if a timeout
+ condition occurred.
+
+ .. cpp:function:: void renegotiate(bool force_full_renegotiation = false)
+
+ Initiates a renegotiation. The counterparty is allowed by the
+ protocol to ignore this request. If a successful renegotiation
+ occurs, the *handshake_cb* callback will be called again.
+
+ If *force_full_renegotiation* is false, then the client will
+ attempt to simply renew the current session - this will refresh
+ the symmetric keys but will not change the session master
+ secret. Otherwise it will initiate a completely new session.
+
+ For a server, if *force_full_renegotiation* is false, then a
+ session resumption will be allowed if the client attempts
+ it. Otherwise the server will prevent resumption and force the
+ creation of a new session.
+
+ .. cpp:function:: std::vector<X509_Certificate> peer_cert_chain()
+
+ Returns the certificate chain of the counterparty. When acting
+ as a client, this value will be non-empty unless the client's
+ policy allowed anonymous connections and the server then chose
+ an anonymous ciphersuite. Acting as a server, this value will
+ ordinarily be empty, unless the server requested a certificate
+ and the client responded with one.
+
+ .. cpp:function:: SymmetricKey key_material_export( \
+ const std::string& label, \
+ const std::string& context, \
+ size_t length)
+
+ Returns an exported key of *length* bytes derived from *label*,
+ *context*, and the session's master secret and client and server
+ random values. This key will be unique to this connection, and
+ as long as the session master secret remains secure an attacker
+ should not be able to guess the key.
+
+ Per :rfc:`5705`, *label* should begin with "EXPERIMENTAL" unless
+ the label has been standardized in an RFC.
+
+.. _tls_client:
+
+TLS Clients
+----------------------------------------
+
+.. cpp:class:: TLS::Client
+
+ .. cpp:function:: Client( \
+ Callbacks& callbacks, \
+ Session_Manager& session_manager, \
+ Credentials_Manager& creds, \
+ const Policy& policy, \
+ RandomNumberGenerator& rng, \
+ const Server_Information& server_info = Server_Information(), \
+ const Protocol_Version offer_version = Protocol_Version::latest_tls_version(), \
+ const std::vector<std::string>& next_protocols = std::vector<std::string>(), \
+ size_t reserved_io_buffer_size = 16*1024 \
+ )
+
+ Initialize a new TLS client. The constructor will immediately
+ initiate a new session.
+
+ The *callbacks* parameter specifies the various application callbacks
+ which pertain to this particular client connection.
+
+ 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
+ :cpp:class:`TLS::Session_Manager_In_Memory` which caches
+ connections for the lifetime of a single process. See
+ :ref:`tls_session_managers` for more about session managers.
+
+ The *credentials_manager* is an interface that will be called to
+ retrieve any certificates, secret keys, pre-shared keys, or SRP
+ information; see :doc:`credentials_manager` for more information.
+
+ Use the optional *server_info* to specify the DNS name of the
+ server you are attempting to connect to, if you know it. This helps
+ the server select what certificate to use and helps the client
+ validate the connection.
+
+ Note that the server name indicator name must be a FQDN. IP
+ addresses are not allowed by RFC 6066 and may lead to interoperability
+ problems.
+
+ Use the optional *offer_version* to control the version of TLS you
+ wish the client to offer. Normally, you'll want to offer the most
+ recent version of (D)TLS that is available, however some broken
+ servers are intolerant of certain versions being offered, and for
+ classes of applications that have to deal with such servers
+ (typically web browsers) it may be necessary to implement a version
+ backdown strategy if the initial attempt fails.
+
+ .. warning::
+
+ Implementing such a backdown strategy allows an attacker to
+ downgrade your connection to the weakest protocol that both you
+ and the server support.
+
+ Setting *offer_version* is also used to offer DTLS instead of TLS;
+ use :cpp:func:`TLS::Protocol_Version::latest_dtls_version`.
+
+ Optionally, the client will advertise *app_protocols* to the
+ server using the ALPN extension.
+
+ The optional *reserved_io_buffer_size* specifies how many bytes to
+ pre-allocate in the I/O buffers. Use this if you want to control
+ how much memory the channel uses initially (the buffers will be
+ resized as needed to process inputs). Otherwise some reasonable
+ default is used.
+
+Code Example
+^^^^^^^^^^^^
+A minimal example of a TLS client is provided below.
+The full code for a TLS client using BSD sockets is in `src/cli/tls_client.cpp`
+
+.. code-block:: cpp
+
+ #include <botan/tls_client.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls server, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override
+ {
+ // process full TLS record received by tls server, e.g.,
+ // by passing it to the application
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls server was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls client.
+ *
+ * It returns a list of trusted CA certificates from a local directory.
+ * TLS client authentication is disabled. See src/lib/tls/credentials_manager.h.
+ */
+ class Client_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ const std::string& context) override
+ {
+ // return a list of certificates of CAs we trust for tls server certificates,
+ // e.g., all the certificates in the local directory "cas"
+ return { new Botan::Certificate_Store_In_Memory("cas") };
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // when using tls client authentication (optional), return
+ // a certificate chain being sent to the tls server,
+ // else an empty list
+ return std::vector<Botan::X509_Certificate>();
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // when returning a chain in cert_chain(), return the private key
+ // associated with the leaf certificate here
+ return nullptr;
+ }
+ };
+
+ int main()
+ {
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::AutoSeeded_RNG rng;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Client_Credentials creds;
+ Botan::TLS::Strict_Policy policy;
+
+ // open the tls connection
+ Botan::TLS::Client client(callbacks,
+ session_mgr,
+ creds,
+ policy,
+ rng,
+ Botan::TLS::Server_Information("botan.randombit.net", 443),
+ Botan::TLS::Protocol_Version::TLS_V12);
+
+ while(!client.is_closed())
+ {
+ // read data received from the tls server, e.g., using BSD sockets or boost asio
+ // ...
+
+ // send data to the tls server using client.send_data()
+ }
+ }
+
+TLS Servers
+----------------------------------------
+
+.. cpp:class:: TLS::Server
+
+ .. cpp:function:: Server( \
+ Callbacks& callbacks, \
+ Session_Manager& session_manager, \
+ Credentials_Manager& creds, \
+ const Policy& policy, \
+ RandomNumberGenerator& rng, \
+ bool is_datagram = false, \
+ size_t reserved_io_buffer_size = 16*1024 \
+ )
+
+The first 5 arguments as well as the final argument
+*reserved_io_buffer_size*, are treated similarly to the :ref:`client
+<tls_client>`.
+
+If a client sends the ALPN extension, the ``callbacks`` function
+``tls_server_choose_app_protocol`` will be called and the result
+sent back to the client. If the empty string is returned, the server
+will not send an ALPN response. The function can also throw an exception
+to abort the handshake entirely, the ALPN specification says that if this
+occurs the alert should be of type `NO_APPLICATION_PROTOCOL`.
+
+The optional argument *is_datagram* specifies if this is a TLS or DTLS
+server; unlike clients, which know what type of protocol (TLS vs DTLS)
+they are negotiating from the start via the *offer_version*, servers
+would not until they actually received a client hello.
+
+Code Example
+^^^^^^^^^^^^
+A minimal example of a TLS server is provided below.
+The full code for a TLS server using asio is in `src/cli/tls_proxy.cpp`.
+
+.. code-block:: cpp
+
+ #include <botan/tls_client.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+ #include <botan/pk_keys.h>
+
+ #include <memory>
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls client, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override
+ {
+ // process full TLS record received by tls client, e.g.,
+ // by passing it to the application
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls client was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls server.
+ *
+ * It returns a certificate and the associated private key to
+ * authenticate the tls server to the client.
+ * TLS client authentication is not requested.
+ * See src/lib/tls/credentials_manager.h.
+ */
+ class Server_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ Server_Credentials() : m_key(Botan::PKCS8::load_key("botan.randombit.net.key"))
+ {
+ }
+
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ const std::string& context) override
+ {
+ // if client authentication is required, this function
+ // shall return a list of certificates of CAs we trust
+ // for tls client certificates, otherwise return an empty list
+ return std::vector<Certificate_Store*>();
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // return the certificate chain being sent to the tls client
+ // e.g., the certificate file "botan.randombit.net.crt"
+ return { Botan::X509_Certificate("botan.randombit.net.crt") };
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // return the private key associated with the leaf certificate,
+ // in this case the one associated with "botan.randombit.net.crt"
+ return &m_key;
+ }
+
+ private:
+ std::unique_ptr<Botan::Private_Key> m_key;
+ };
+
+ int main()
+ {
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::AutoSeeded_RNG rng;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Server_Credentials creds;
+ Botan::TLS::Strict_Policy policy;
+
+ // accept tls connection from client
+ Botan::TLS::Server server(callbacks,
+ session_mgr,
+ creds,
+ policy,
+ rng);
+
+ // read data received from the tls client, e.g., using BSD sockets or boost asio
+ // and pass it to server.received_data().
+ // ...
+
+ // send data to the tls client using server.send_data()
+ // ...
+ }
+
+.. _tls_sessions:
+
+TLS Sessions
+----------------------------------------
+
+TLS allows clients and servers to support *session resumption*, where
+the end point retains some information about an established session
+and then reuse that information to bootstrap a new session in way that
+is much cheaper computationally than a full handshake.
+
+Every time your handshake callback is called, a new session has been
+established, and a ``TLS::Session`` is included that provides
+information about that session:
+
+.. cpp:class:: TLS::Session
+
+ .. cpp:function:: Protocol_Version version() const
+
+ Returns the :cpp:class:`protocol version <TLS::Protocol_Version>`
+ that was negotiated
+
+ .. cpp:function:: Ciphersuite ciphersite() const
+
+ Returns the :cpp:class:`ciphersuite <TLS::Ciphersuite>` that
+ was negotiated.
+
+ .. cpp:function:: Server_Information server_info() const
+
+ Returns information that identifies the server side of the
+ connection. This is useful for the client in that it
+ identifies what was originally passed to the constructor. For
+ the server, it includes the name the client specified in the
+ server name indicator extension.
+
+ .. cpp:function:: std::vector<X509_Certificate> peer_certs() const
+
+ Returns the certificate chain of the peer
+
+ .. cpp:function:: std::string srp_identifier() const
+
+ If an SRP ciphersuite was used, then this is the identifier
+ that was used for authentication.
+
+ .. cpp:function:: bool secure_renegotiation() const
+
+ Returns ``true`` if the connection was negotiated with the
+ correct extensions to prevent the renegotiation attack.
+
+ .. cpp:function:: std::vector<uint8_t> encrypt(const SymmetricKey& key, \
+ RandomNumberGenerator& rng)
+
+ Encrypts a session using a symmetric key *key* and returns a raw
+ binary value that can later be passed to ``decrypt``. The key
+ may be of any length.
+
+ Currently the implementation encrypts the session using AES-256
+ in GCM mode with a random nonce.
+
+ .. cpp:function:: static Session decrypt(const uint8_t ciphertext[], \
+ size_t length, \
+ const SymmetricKey& key)
+
+ Decrypts a session that was encrypted previously with ``encrypt`` and
+ ``key``, or throws an exception if decryption fails.
+
+ .. cpp:function:: secure_vector<uint8_t> DER_encode() const
+
+ Returns a serialized version of the session.
+
+ .. warning:: The return value of ``DER_encode`` contains the
+ master secret for the session, and an attacker who
+ recovers it could recover plaintext of previous
+ sessions or impersonate one side to the other.
+
+.. _tls_session_managers:
+
+TLS Session Managers
+----------------------------------------
+
+You may want sessions stored in a specific format or storage type. To
+do so, implement the ``TLS::Session_Manager`` interface and pass your
+implementation to the ``TLS::Client`` or ``TLS::Server`` constructor.
+
+.. cpp:class:: TLS::Session_Mananger
+
+ .. cpp:function:: void save(const Session& session)
+
+ Save a new *session*. It is possible that this sessions session
+ ID will replicate a session ID already stored, in which case the
+ new session information should overwrite the previous information.
+
+ .. cpp:function:: void remove_entry(const std::vector<uint8_t>& session_id)
+
+ Remove the session identified by *session_id*. Future attempts
+ at resumption should fail for this session.
+
+ .. cpp:function:: bool load_from_session_id(const std::vector<uint8_t>& session_id, \
+ Session& session)
+
+ Attempt to resume a session identified by *session_id*. If
+ located, *session* is set to the session data previously passed
+ to *save*, and ``true`` is returned. Otherwise *session* is not
+ modified and ``false`` is returned.
+
+ .. cpp:function:: bool load_from_server_info(const Server_Information& server, \
+ Session& session)
+
+ Attempt to resume a session with a known server.
+
+ .. cpp:function:: std::chrono::seconds session_lifetime() const
+
+ Returns the expected maximum lifetime of a session when using
+ this session manager. Will return 0 if the lifetime is unknown
+ or has no explicit expiration policy.
+
+.. _tls_session_manager_inmem:
+
+In Memory Session Manager
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``TLS::Session_Manager_In_Memory`` implementation saves sessions
+in memory, with an upper bound on the maximum number of sessions and
+the lifetime of a session.
+
+It is safe to share a single object across many threads as it uses a
+lock internally.
+
+.. cpp:class:: TLS::Session_Managers_In_Memory
+
+ .. cpp:function:: Session_Manager_In_Memory(RandomNumberGenerator& rng, \
+ size_t max_sessions = 1000, \
+ std::chrono::seconds session_lifetime = 7200)
+
+ Limits the maximum number of saved sessions to *max_sessions*, and
+ expires all sessions older than *session_lifetime*.
+
+Noop Session Mananger
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``TLS::Session_Manager_Noop`` implementation does not save
+sessions at all, and thus session resumption always fails. Its
+constructor has no arguments.
+
+SQLite3 Session Manager
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This session manager is only available if support for SQLite3 was
+enabled at build time. If the macro
+``BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER`` is defined, then
+``botan/tls_session_manager_sqlite.h`` contains
+``TLS::Session_Manager_SQLite`` which stores sessions persistently to
+a sqlite3 database. The session data is encrypted using a passphrase,
+and stored in two tables, named ``tls_sessions`` (which holds the
+actual session information) and ``tls_sessions_metadata`` (which holds
+the PBKDF information).
+
+.. warning:: The hostnames associated with the saved sessions are
+ stored in the database in plaintext. This may be a
+ serious privacy risk in some applications.
+
+.. cpp:class:: TLS::Session_Manager_SQLite
+
+ .. cpp:function:: Session_Manager_SQLite( \
+ const std::string& passphrase, \
+ RandomNumberGenerator& rng, \
+ const std::string& db_filename, \
+ size_t max_sessions = 1000, \
+ std::chrono::seconds session_lifetime = 7200)
+
+ Uses the sqlite3 database named by *db_filename*.
+
+TLS Policies
+----------------------------------------
+
+``TLS::Policy`` is how an application can control details of what will
+be negotiated during a handshake. The base class acts as the default
+policy. There is also a ``Strict_Policy`` (which forces only secure
+options, reducing compatibility) and ``Text_Policy`` which reads
+policy settings from a file.
+
+.. cpp:class:: TLS::Policy
+
+ .. cpp:function:: std::vector<std::string> allowed_ciphers() const
+
+ Returns the list of ciphers we are willing to negotiate, in order
+ of preference.
+
+ Clients send a list of ciphersuites in order of preference,
+ servers are free to choose any of them. Some servers will use the
+ clients preferences, others choose from the clients list
+ prioritizing based on its preferences.
+
+ No export key exchange mechanisms or ciphersuites are supported
+ by botan. The null encryption ciphersuites (which provide only
+ authentication, sending data in cleartext) are also not supported
+ by the implementation and cannot be negotiated.
+
+ Cipher names without an explicit mode refers to CBC+HMAC ciphersuites.
+
+ Default value: "ChaCha20Poly1305", "AES-256/GCM", "AES-128/GCM"
+
+ Also allowed: "AES-256", "AES-128",
+ "AES-256/CCM", "AES-128/CCM", "AES-256/CCM(8)", "AES-128/CCM(8)",
+ "Camellia-256/GCM", "Camellia-128/GCM", "ARIA-256/GCM", "ARIA-128/GCM",
+ "Camellia-256", "Camellia-128"
+
+ Also allowed (though currently experimental): "AES-128/OCB(12)",
+ "AES-256/OCB(12)"
+
+ In versions up to 2.8.0, the CBC and CCM ciphersuites "AES-256",
+ "AES-128", "AES-256/CCM" and "AES-128/CCM" were enabled by default.
+
+ Also allowed (although **not recommended**): "SEED", "3DES"
+
+ .. note::
+
+ Before 1.11.30 only the non-standard ChaCha20Poly1305 ciphersuite
+ was implemented. The RFC 7905 ciphersuites are supported in 1.11.30
+ onwards.
+
+ .. note::
+
+ Support for the broken RC4 cipher was removed in 1.11.17
+
+ .. note::
+
+ SEED and 3DES are deprecated and will be removed in a future release.
+
+ .. cpp:function:: std::vector<std::string> allowed_macs() const
+
+ Returns the list of algorithms we are willing to use for
+ message authentication, in order of preference.
+
+ Default: "AEAD", "SHA-256", "SHA-384", "SHA-1"
+
+ A plain hash function indicates HMAC
+
+ .. note::
+
+ SHA-256 is preferred over SHA-384 in CBC mode because the
+ protections against the Lucky13 attack are somewhat more
+ effective for SHA-256 than SHA-384.
+
+ .. cpp:function:: std::vector<std::string> allowed_key_exchange_methods() const
+
+ Returns the list of key exchange methods we are willing to use,
+ in order of preference.
+
+ Default: "CECPQ1", "ECDH", "DH"
+
+ .. note::
+
+ CECPQ1 key exchange provides post-quantum security to the key exchange
+ by combining NewHope with a standard x25519 ECDH exchange. This prevents
+ an attacker, even one with a quantum computer, from later decrypting the
+ contents of a recorded TLS transcript. The NewHope algorithm is very
+ fast, but adds roughly 4 KiB of additional data transfer to every TLS
+ handshake. And even if NewHope ends up completely broken, the 'extra'
+ x25519 exchange secures the handshake.
+
+ For applications where the additional data transfer size is unacceptable,
+ simply allow only ECDH key exchange in the application policy. DH
+ exchange also often involves transferring several additional Kb (without
+ the benefit of post quantum security) so if CECPQ1 is being disabled for
+ traffic overhead reasons, DH should also be avoid.
+
+ Also allowed: "RSA", "SRP_SHA", "ECDHE_PSK", "DHE_PSK", "PSK"
+
+ .. note::
+
+ Static RSA ciphersuites are disabled by default since 1.11.34.
+ In addition to not providing forward security, any server which is
+ willing to negotiate these ciphersuites exposes themselves to a variety
+ of chosen ciphertext oracle attacks which are all easily avoided by
+ signing (as in PFS) instead of decrypting.
+
+ .. note::
+
+ In order to enable RSA, SRP, or PSK ciphersuites one must also enable
+ authentication method "IMPLICIT", see :cpp:func:`allowed_signature_methods`.
+
+ .. cpp:function:: std::vector<std::string> allowed_signature_hashes() const
+
+ Returns the list of hash algorithms we are willing to use for
+ public key signatures, in order of preference.
+
+ Default: "SHA-512", "SHA-384", "SHA-256"
+
+ Also allowed (although **not recommended**): "SHA-1"
+
+ .. note::
+
+ This is only used with TLS v1.2. In earlier versions of the
+ protocol, signatures are fixed to using only SHA-1 (for
+ DSA/ECDSA) or a MD5/SHA-1 pair (for RSA).
+
+ .. cpp:function:: std::vector<std::string> allowed_signature_methods() const
+
+ Default: "ECDSA", "RSA"
+
+ Also allowed (disabled by default): "DSA", "IMPLICIT", "ANONYMOUS"
+
+ "IMPLICIT" enables ciphersuites which are authenticated not by a signature
+ but through a side-effect of the key exchange. In particular this setting
+ is required to enable PSK, SRP, and static RSA ciphersuites.
+
+ "ANONYMOUS" allows purely anonymous DH/ECDH key exchanges. **Enabling this
+ is not recommended**
+
+ .. note::
+
+ Both DSA authentication and anonymous DH ciphersuites are deprecated,
+ and will be removed in a future release.
+
+ .. cpp:function:: std::vector<Group_Params> key_exchange_groups() const
+
+ Return a list of ECC curve and DH group TLS identifiers we are willing to use, in order of preference.
+ The default ordering puts the best performing ECC first.
+
+ Default:
+ Group_Params::X25519,
+ Group_Params::SECP256R1, Group_Params::BRAINPOOL256R1,
+ Group_Params::SECP384R1, Group_Params::BRAINPOOL384R1,
+ Group_Params::SECP521R1, Group_Params::BRAINPOOL512R1,
+ Group_Params::FFDHE_2048, Group_Params::FFDHE_3072, Group_Params::FFDHE_4096,
+ Group_Params::FFDHE_6144, Group_Params::FFDHE_8192
+
+ No other values are currently defined.
+
+ .. cpp:function:: bool use_ecc_point_compression() const
+
+ Prefer ECC point compression.
+
+ Signals that we prefer ECC points to be compressed when transmitted to us.
+ The other party may not support ECC point compression and therefore may still
+ send points uncompressed.
+
+ Note that the certificate used during authentication must also follow the other
+ party's preference.
+
+ Default: false
+
+ .. cpp:function:: bool acceptable_protocol_version(Protocol_Version version)
+
+ Return true if this version of the protocol is one that we are
+ willing to negotiate.
+
+ Default: Accepts TLS v1.0 or higher and DTLS v1.2 or higher.
+
+ .. cpp:function:: bool server_uses_own_ciphersuite_preferences() const
+
+ If this returns true, a server will pick the cipher it prefers the
+ most out of the client's list. Otherwise, it will negotiate the
+ first cipher in the client's ciphersuite list that it supports.
+
+ .. cpp:function:: bool negotiate_heartbeat_support() const
+
+ If this function returns true, clients will offer the heartbeat
+ support extension, and servers will respond to clients offering
+ the extension. Otherwise, clients will not offer heartbeat
+ support and servers will ignore clients offering heartbeat
+ support.
+
+ If this returns true, callers should expect to handle heartbeat
+ data in their ``alert_cb``.
+
+ Default: false
+
+ .. cpp:function:: bool allow_client_initiated_renegotiation() const
+
+ If this function returns true, a server will accept a
+ client-initiated renegotiation attempt. Otherwise it will send
+ the client a non-fatal ``no_renegotiation`` alert.
+
+ Default: true
+
+ .. cpp:function:: bool allow_server_initiated_renegotiation() const
+
+ If this function returns true, a client will accept a
+ server-initiated renegotiation attempt. Otherwise it will send
+ the server a non-fatal ``no_renegotiation`` alert.
+
+ Default: false
+
+ .. cpp:function:: bool allow_insecure_renegotiation() const
+
+ If this function returns true, we will allow renegotiation attempts
+ even if the counterparty does not support the RFC 5746 extensions.
+
+ .. warning:: Returning true here could expose you to attacks
+
+ Default: false
+
+ .. cpp:function:: size_t minimum_signature_strength() const
+
+ Return the minimum strength (as ``n``, representing ``2**n`` work)
+ we will accept for a signature algorithm on any certificate.
+
+ Use 80 to enable RSA-1024 (*not recommended*), or 128 to require
+ either ECC or large (~3000 bit) RSA keys.
+
+ Default: 110 (allowing 2048 bit RSA)
+
+ .. cpp:function:: bool require_cert_revocation_info() const
+
+ If this function returns true, and a ciphersuite using certificates was
+ negotiated, then we must have access to a valid CRL or OCSP response in
+ order to trust the certificate.
+
+ .. warning:: Returning false here could expose you to attacks
+
+ Default: true
+
+ .. cpp:function:: Group_Params default_dh_group() const
+
+ For ephemeral Diffie-Hellman key exchange, the server sends a
+ group parameter. Return the 2 Byte TLS group identifier specifying the group parameter a
+ server should use.
+
+ Default: 2048 bit IETF IPsec group ("modp/ietf/2048")
+
+ .. cpp:function:: size_t minimum_dh_group_size() const
+
+ Return the minimum size in bits for a Diffie-Hellman group that a
+ client will accept. Due to the design of the protocol the client
+ has only two options - accept the group, or reject it with a
+ fatal alert then attempt to reconnect after disabling ephemeral
+ Diffie-Hellman.
+
+ Default: 2048 bits
+
+ .. cpp:function:: bool allow_tls10() const
+
+ Return true from here to allow TLS v1.0. Since 2.8.0, returns
+ ``false`` by default.
+
+ .. cpp:function:: bool allow_tls11() const
+
+ Return true from here to allow TLS v1.1. Since 2.8.0, returns
+ ``false`` by default.
+
+ .. cpp:function:: bool allow_tls12() const
+
+ Return true from here to allow TLS v1.2. Returns ``true`` by default.
+
+ .. cpp:function:: size_t minimum_rsa_bits() const
+
+ Minimum accepted RSA key size. Default 2048 bits.
+
+ .. cpp:function:: size_t minimum_dsa_group_size() const
+
+ Minimum accepted DSA key size. Default 2048 bits.
+
+ .. cpp:function:: size_t minimum_ecdsa_group_size() const
+
+ Minimum size for ECDSA keys (256 bits).
+
+ .. cpp:function:: size_t minimum_ecdh_group_size() const
+
+ Minimum size for ECDH keys (255 bits).
+
+ .. cpp:function:: void check_peer_key_acceptable(const Public_Key& public_key) const
+
+ Allows the policy to examine peer public keys. Throw an exception
+ if the key should be rejected. Default implementation checks
+ against policy values `minimum_dh_group_size`, `minimum_rsa_bits`,
+ `minimum_ecdsa_group_size`, and `minimum_ecdh_group_size`.
+
+ .. cpp:function:: bool hide_unknown_users() const
+
+ The SRP and PSK suites work using an identifier along with a
+ shared secret. If this function returns true, when an identifier
+ that the server does not recognize is provided by a client, a
+ random shared secret will be generated in such a way that a
+ client should not be able to tell the difference between the
+ identifier not being known and the secret being wrong. This can
+ help protect against some username probing attacks. If it
+ returns false, the server will instead send an
+ ``unknown_psk_identity`` alert when an unknown identifier is
+ used.
+
+ Default: false
+
+ .. cpp:function:: u32bit session_ticket_lifetime() const
+
+ Return the lifetime of session tickets. Each session includes the
+ start time. Sessions resumptions using tickets older than
+ ``session_ticket_lifetime`` seconds will fail, forcing a full
+ renegotiation.
+
+ Default: 86400 seconds (1 day)
+
+TLS Ciphersuites
+----------------------------------------
+
+.. cpp:class:: TLS::Ciphersuite
+
+ .. cpp:function:: uint16_t ciphersuite_code() const
+
+ Return the numerical code for this ciphersuite
+
+ .. cpp:function:: std::string to_string() const
+
+ Return the full name of ciphersuite (for example
+ "RSA_WITH_RC4_128_SHA" or "ECDHE_RSA_WITH_AES_128_GCM_SHA256")
+
+ .. cpp:function:: std::string kex_algo() const
+
+ Return the key exchange algorithm of this ciphersuite
+
+ .. cpp:function:: std::string sig_algo() const
+
+ Return the signature algorithm of this ciphersuite
+
+ .. cpp:function:: std::string cipher_algo() const
+
+ Return the cipher algorithm of this ciphersuite
+
+ .. cpp:function:: std::string mac_algo() const
+
+ Return the authentication algorithm of this ciphersuite
+
+ .. cpp:function:: bool acceptable_ciphersuite(const Ciphersuite& suite) const
+
+ Return true if ciphersuite is accepted by the policy.
+
+ Allows an application to reject any ciphersuites, which are
+ undesirable for whatever reason without having to reimplement
+ :cpp:func:`TLS::Ciphersuite::ciphersuite_list`
+
+ .. cpp:function:: std::vector<uint16_t> ciphersuite_list(Protocol_Version version, bool have_srp) const
+
+ Return allowed ciphersuites in order of preference
+
+ Allows an application to have full control over ciphersuites
+ by returning desired ciphersuites in preference order.
+
+.. _tls_alerts:
+
+TLS Alerts
+----------------------------------------
+
+A ``TLS::Alert`` is passed to every invocation of a channel's *alert_cb*.
+
+.. cpp:class:: TLS::Alert
+
+ .. cpp:function:: is_valid() const
+
+ Return true if this alert is not a null alert
+
+ .. cpp:function:: is_fatal() const
+
+ Return true if this alert is fatal. A fatal alert causes the
+ connection to be immediately disconnected. Otherwise, the alert
+ is a warning and the connection remains valid.
+
+ .. cpp:function:: Type type() const
+
+ Returns the type of the alert as an enum
+
+ .. cpp:function:: std::string type_string()
+
+ Returns the type of the alert as a string
+
+TLS Protocol Version
+----------------------------------------
+
+TLS has several different versions with slightly different behaviors.
+The ``TLS::Protocol_Version`` class represents a specific version:
+
+.. cpp:class:: TLS::Protocol_Version
+
+ .. cpp:enum:: Version_Code
+
+ ``TLS_V10``, ``TLS_V11``, ``TLS_V12``, ``DTLS_V10``, ``DTLS_V12``
+
+ .. cpp:function:: Protocol_Version(Version_Code named_version)
+
+ Create a specific version
+
+ .. cpp:function:: uint8_t major_version() const
+
+ Returns major number of the protocol version
+
+ .. cpp:function:: uint8_t minor_version() const
+
+ Returns minor number of the protocol version
+
+ .. cpp:function:: std::string to_string() const
+
+ Returns string description of the version, for instance "TLS
+ v1.1" or "DTLS v1.0".
+
+ .. cpp:function:: static Protocol_Version latest_tls_version()
+
+ Returns the latest version of the TLS protocol known to the library
+ (currently TLS v1.2)
+
+ .. cpp:function:: static Protocol_Version latest_dtls_version()
+
+ Returns the latest version of the DTLS protocol known to the
+ library (currently DTLS v1.2)
+
+TLS Custom Curves
+----------------------------------------
+
+The supported_groups TLS extension is used in the client hello to advertise a list of supported elliptic curves
+and DH groups. The server subsequently selects one of the groups, which is supported by both endpoints.
+The groups are represented by their TLS identifier. This 2 Byte identifier is standardized for commonly used groups and curves.
+In addition, the standard reserves the identifiers 0xFE00 to 0xFEFF for custom groups or curves.
+
+Using non standardized custom curves is however not recommended and can be a serious risk if an
+insecure curve is used. Still, it might be desired in some scenarios to use custom curves or groups in the TLS handshake.
+
+To use custom curves with the Botan :cpp:class:`TLS::Client` or :cpp:class:`TLS::Server` the following additional adjustments have to be implemented
+as shown in the following code examples.
+
+1. Registration of the custom curve
+2. Implementation TLS callback ``tls_decode_group_param``
+3. Adjustment of the TLS policy by allowing the custom curve
+
+Client Code Example
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: cpp
+
+ #include <botan/tls_client.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+
+ #include <botan/ec_group.h>
+ #include <botan/oids.h>
+
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls server, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override
+ {
+ // process full TLS record received by tls server, e.g.,
+ // by passing it to the application
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls server was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+ std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override
+ {
+ // handle TLS group identifier decoding and return name as string
+ // return empty string to indicate decoding failure
+
+ switch(static_cast<uint16_t>(group_param))
+ {
+ case 0xFE00:
+ return "testcurve1102";
+ default:
+ //decode non-custom groups
+ return Botan::TLS::Callbacks::tls_decode_group_param(group_param);
+ }
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls client.
+ *
+ * It returns a list of trusted CA certificates from a local directory.
+ * TLS client authentication is disabled. See src/lib/tls/credentials_manager.h.
+ */
+ class Client_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ const std::string& context) override
+ {
+ // return a list of certificates of CAs we trust for tls server certificates,
+ // e.g., all the certificates in the local directory "cas"
+ return { new Botan::Certificate_Store_In_Memory("cas") };
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // when using tls client authentication (optional), return
+ // a certificate chain being sent to the tls server,
+ // else an empty list
+ return std::vector<Botan::X509_Certificate>();
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // when returning a chain in cert_chain(), return the private key
+ // associated with the leaf certificate here
+ return nullptr;
+ }
+ };
+
+ class Client_Policy : public Botan::TLS::Strict_Policy
+ {
+ public:
+ std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
+ {
+ // modified strict policy to allow our custom curves
+ return
+ {
+ static_cast<Botan::TLS::Group_Params>(0xFE00)
+ };
+ }
+ };
+
+ int main()
+ {
+ // prepare rng
+ Botan::AutoSeeded_RNG rng;
+
+ // prepare custom curve
+
+ // prepare curve parameters
+ const Botan::BigInt p("0x92309a3e88b94312f36891a2055725bb35ab51af96b3a651d39321b7bbb8c51575a76768c9b6b323");
+ const Botan::BigInt a("0x4f30b8e311f6b2dce62078d70b35dacb96aa84b758ab5a8dff0c9f7a2a1ff466c19988aa0acdde69");
+ const Botan::BigInt b("0x9045A513CFFF9AE1F1CC84039D852D240344A1D5C9DB203C844089F855C387823EB6FCDDF49C909C");
+
+ const Botan::BigInt x("0x9120f3779a31296cefcb5a5a08831f1a6d438ad5a3f2ce60585ac19c74eebdc65cadb96bb92622c7");
+ const Botan::BigInt y("0x836db8251c152dfee071b72c6b06c5387d82f1b5c30c5a5b65ee9429aa2687e8426d5d61276a4ede");
+ const Botan::BigInt order("0x248c268fa22e50c4bcda24688155c96ecd6ad46be5c82d7a6be6e7068cb5d1ca72b2e07e8b90d853");
+
+ const Botan::BigInt cofactor(4);
+
+ const Botan::OID oid("1.2.3.1");
+
+ // create EC_Group object to register the curve
+ Botan::EC_Group testcurve1102(p, a, b, x, y, order, cofactor, oid);
+
+ if(!testcurve1102.verify_group(rng))
+ {
+ // Warning: if verify_group returns false the curve parameters are insecure
+ }
+
+ // register name to specified oid
+ Botan::OIDS::add_oid(oid, "testcurve1102");
+
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Client_Credentials creds;
+ Client_Policy policy;
+
+ // open the tls connection
+ Botan::TLS::Client client(callbacks,
+ session_mgr,
+ creds,
+ policy,
+ rng,
+ Botan::TLS::Server_Information("botan.randombit.net", 443),
+ Botan::TLS::Protocol_Version::TLS_V12);
+
+
+ while(!client.is_closed())
+ {
+ // read data received from the tls server, e.g., using BSD sockets or boost asio
+ // ...
+
+ // send data to the tls server using client.send_data()
+
+ }
+ }
+
+Server Code Example
+^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: cpp
+
+ #include <botan/tls_server.h>
+ #include <botan/tls_callbacks.h>
+ #include <botan/tls_session_manager.h>
+ #include <botan/tls_policy.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor.h>
+ #include <botan/pk_keys.h>
+ #include <botan/pkcs8.h>
+
+ #include <botan/ec_group.h>
+ #include <botan/oids.h>
+
+ #include <memory>
+
+ /**
+ * @brief Callbacks invoked by TLS::Channel.
+ *
+ * Botan::TLS::Callbacks is an abstract class.
+ * For improved readability, only the functions that are mandatory
+ * to implement are listed here. See src/lib/tls/tls_callbacks.h.
+ */
+ class Callbacks : public Botan::TLS::Callbacks
+ {
+ public:
+ void tls_emit_data(const uint8_t data[], size_t size) override
+ {
+ // send data to tls client, e.g., using BSD sockets or boost asio
+ }
+
+ void tls_record_received(uint64_t seq_no, const uint8_t data[], size_t size) override
+ {
+ // process full TLS record received by tls client, e.g.,
+ // by passing it to the application
+ }
+
+ void tls_alert(Botan::TLS::Alert alert) override
+ {
+ // handle a tls alert received from the tls server
+ }
+
+ bool tls_session_established(const Botan::TLS::Session& session) override
+ {
+ // the session with the tls client was established
+ // return false to prevent the session from being cached, true to
+ // cache the session in the configured session manager
+ return false;
+ }
+
+ std::string tls_decode_group_param(Botan::TLS::Group_Params group_param) override
+ {
+ // handle TLS group identifier decoding and return name as string
+ // return empty string to indicate decoding failure
+
+ switch(static_cast<uint16_t>(group_param))
+ {
+ case 0xFE00:
+ return "testcurve1102";
+ default:
+ //decode non-custom groups
+ return Botan::TLS::Callbacks::tls_decode_group_param(group_param);
+ }
+ }
+ };
+
+ /**
+ * @brief Credentials storage for the tls server.
+ *
+ * It returns a certificate and the associated private key to
+ * authenticate the tls server to the client.
+ * TLS client authentication is not requested.
+ * See src/lib/tls/credentials_manager.h.
+ */
+ class Server_Credentials : public Botan::Credentials_Manager
+ {
+ public:
+ Server_Credentials() : m_key(Botan::PKCS8::load_key("botan.randombit.net.key")
+ {
+ }
+
+ std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(
+ const std::string& type,
+ const std::string& context) override
+ {
+ // if client authentication is required, this function
+ // shall return a list of certificates of CAs we trust
+ // for tls client certificates, otherwise return an empty list
+ return std::vector<Botan::Certificate_Store*>();
+ }
+
+ std::vector<Botan::X509_Certificate> cert_chain(
+ const std::vector<std::string>& cert_key_types,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // return the certificate chain being sent to the tls client
+ // e.g., the certificate file "botan.randombit.net.crt"
+ return { Botan::X509_Certificate("botan.randombit.net.crt") };
+ }
+
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& type,
+ const std::string& context) override
+ {
+ // return the private key associated with the leaf certificate,
+ // in this case the one associated with "botan.randombit.net.crt"
+ return m_key.get();
+ }
+
+ private:
+ std::unique_ptr<Botan::Private_Key> m_key;
+ };
+
+ class Server_Policy : public Botan::TLS::Strict_Policy
+ {
+ public:
+ std::vector<Botan::TLS::Group_Params> key_exchange_groups() const override
+ {
+ // modified strict policy to allow our custom curves
+ return
+ {
+ static_cast<Botan::TLS::Group_Params>(0xFE00)
+ };
+ }
+ };
+
+ int main()
+ {
+
+ // prepare rng
+ Botan::AutoSeeded_RNG rng;
+
+ // prepare custom curve
+
+ // prepare curve parameters
+ const Botan::BigInt p("0x92309a3e88b94312f36891a2055725bb35ab51af96b3a651d39321b7bbb8c51575a76768c9b6b323");
+ const Botan::BigInt a("0x4f30b8e311f6b2dce62078d70b35dacb96aa84b758ab5a8dff0c9f7a2a1ff466c19988aa0acdde69");
+ const Botan::BigInt b("0x9045A513CFFF9AE1F1CC84039D852D240344A1D5C9DB203C844089F855C387823EB6FCDDF49C909C");
+
+ const Botan::BigInt x("0x9120f3779a31296cefcb5a5a08831f1a6d438ad5a3f2ce60585ac19c74eebdc65cadb96bb92622c7");
+ const Botan::BigInt y("0x836db8251c152dfee071b72c6b06c5387d82f1b5c30c5a5b65ee9429aa2687e8426d5d61276a4ede");
+ const Botan::BigInt order("0x248c268fa22e50c4bcda24688155c96ecd6ad46be5c82d7a6be6e7068cb5d1ca72b2e07e8b90d853");
+
+ const Botan::BigInt cofactor(4);
+
+ const Botan::OID oid("1.2.3.1");
+
+ // create EC_Group object to register the curve
+ Botan::EC_Group testcurve1102(p, a, b, x, y, order, cofactor, oid);
+
+ if(!testcurve1102.verify_group(rng))
+ {
+ // Warning: if verify_group returns false the curve parameters are insecure
+ }
+
+ // register name to specified oid
+ Botan::OIDS::add_oid(oid, "testcurve1102");
+
+ // prepare all the parameters
+ Callbacks callbacks;
+ Botan::TLS::Session_Manager_In_Memory session_mgr(rng);
+ Server_Credentials creds;
+ Server_Policy policy;
+
+ // accept tls connection from client
+ Botan::TLS::Server server(callbacks,
+ session_mgr,
+ creds,
+ policy,
+ rng);
+
+ // read data received from the tls client, e.g., using BSD sockets or boost asio
+ // and pass it to server.received_data().
+ // ...
+
+ // send data to the tls client using server.send_data()
+ // ...
+ }
+
+TLS Stream
+----------------------------------------
+
+:cpp:class:`TLS::Stream` offers a Boost.Asio compatible wrapper around :cpp:class:`TLS::Client`.
+It can be used as an alternative to Boost.Asio's `ssl::stream <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ssl__stream.html>`_ with minor adjustments to the using code.
+It offers the following interface:
+
+.. cpp:class:: template <class StreamLayer, class ChannelT> TLS::Stream
+
+ *StreamLayer* specifies the type of the stream's *next layer*, for example a `Boost.Asio TCP socket <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ip__tcp/socket.html>`_.
+ *ChannelT* is the type of the stream's *native handle*; it defaults to :cpp:class:`TLS::Channel` and should not be specified manually.
+
+ .. cpp:function:: template <typename... Args> \
+ explicit Stream(Context& context, Args&& ... args)
+
+ Construct a new TLS stream.
+ The *context* parameter will be used to set up the underlying *native handle*, i.e. the :ref:`TLS::Client <tls_client>`, when :cpp:func:`handshake` is called.
+ The further *args* will be forwarded to the *next layer*'s constructor.
+
+ .. cpp:function:: template <typename... Args> \
+ explicit Stream(Arg&& arg, Context& context)
+
+ Convenience constructor for :cpp:class:`boost::asio::ssl::stream` compatibility.
+ The parameters have the same meaning as for the first constructor, but their order is changed and only one argument can be passed to the *next layer* constructor.
+
+
+ .. cpp:function:: void handshake(Connection_Side side, boost::system::error_code& ec)
+
+ Set up the *native handle* and perform the TLS handshake.
+ As only the client side of the stream is currently implemented, *side* should be ``Connection_Side::CLIENT``.
+
+ .. cpp:function:: void handshake(Connection_Side side)
+
+ Overload of :cpp:func:`handshake` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename HandshakeHandler> \
+ DEDUCED async_handshake(Connection_Side side, HandshakeHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`handshake`.
+ The function returns immediately and calls the *handler* callback function after performing asynchronous I/O to complete the TLS handshake.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *HandshakeHandler* type.
+
+
+ .. cpp:function:: void shutdown(boost::system::error_code& ec)
+
+ Calls :cpp:func:`TLS::Channel::close` on the native handle and writes the TLS alert to the *next layer*.
+
+ .. cpp:function:: void shutdown()
+
+ Overload of :cpp:func:`shutdown` that throws an exception if an error occurs.
+
+
+ .. cpp:function:: template <typename MutableBufferSequence> \
+ std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec)
+
+ Reads encrypted data from the *next layer*, decrypts it, and writes it into the provided *buffers*.
+ If an error occurs, *error_code* is set.
+ Returns the number of bytes read.
+
+ .. cpp:function:: template <typename MutableBufferSequence> \
+ std::size_t read_some(const MutableBufferSequence& buffers)
+
+ Overload of :cpp:func:`read_some` that throws an exception if an error occurs.
+
+ .. cpp:function:: template <typename MutableBufferSequence, typename ReadHandler> \
+ DEDUCED async_read_some(const MutableBufferSequence& buffers, ReadHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`read_some`.
+ The function returns immediately and calls the *handler* callback function after writing the decrypted data into the provided *buffers*.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *ReadHandler* type.
+ *ReadHandler* should suffice the `requirements to a Boost.Asio read handler <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ReadHandler.html>`_.
+
+
+ .. cpp:function:: template <typename ConstBufferSequence> \
+ std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code& ec)
+
+ Encrypts data from the provided *buffers* and writes it to the *next layer*.
+ If an error occurs, *error_code* is set.
+ Returns the number of bytes written.
+
+ .. cpp:function:: template <typename ConstBufferSequence> \
+ std::size_t write_some(const ConstBufferSequence& buffers)
+
+ Overload of :cpp:func:`write_some` that throws an exception rather than setting an error code.
+
+ .. cpp:function:: template <typename ConstBufferSequence, typename WriteHandler> \
+ DEDUCED async_write_some(const ConstBufferSequence& buffers, WriteHandler&& handler)
+
+ Asynchronous variant of :cpp:func:`write_some`.
+ The function returns immediately and calls the *handler* callback function after writing the encrypted data to the *next layer*.
+ The return type is an automatically deduced specialization of :cpp:class:`boost::asio::async_result`, depending on the *WriteHandler* type.
+ *WriteHandler* should suffice the `requirements to a Boost.Asio write handler <https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/WriteHandler.html>`_.
+
+.. cpp:struct:: TLS::Context
+
+ A helper struct to collect the initialization parameters for the Stream's underlying *native handle* (see :cpp:class:`TLS::Client`).
+ `TLS::Context` is defined as
+
+ .. code-block:: cpp
+
+ struct Context
+ {
+ Credentials_Manager* credentialsManager;
+ RandomNumberGenerator* randomNumberGenerator;
+ Session_Manager* sessionManager;
+ Policy* policy;
+ Server_Information serverInfo;
+ };
+
+
+Stream Code Example
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: cpp
+
+ #include <iostream>
+
+ #include <botan/asio_stream.h>
+ #include <botan/auto_rng.h>
+ #include <botan/certstor_system.h>
+
+ #include <boost/asio.hpp>
+ #include <boost/beast.hpp>
+ #include <boost/bind.hpp>
+
+ namespace http = boost::beast::http;
+ namespace _ = boost::asio::placeholders;
+
+ // very basic credentials manager
+ class Credentials_Manager : public Botan::Credentials_Manager
+ {
+ public:
+ Credentials_Manager() {}
+
+ std::vector<Botan::Certificate_Store*>
+ trusted_certificate_authorities(const std::string&, const std::string&) override
+ {
+ return {&cert_store_};
+ }
+
+ private:
+ Botan::System_Certificate_Store cert_store_;
+ };
+
+ // a simple https client based on TLS::Stream
+ class client
+ {
+ public:
+ client(boost::asio::io_context& io_context,
+ boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
+ http::request<http::string_body> req)
+ : request_(req)
+ , ctx_{&credentials_mgr_,
+ &rng_,
+ &session_mgr_,
+ &policy_,
+ Botan::TLS::Server_Information()}
+ , stream_(io_context, ctx_)
+ {
+ boost::asio::async_connect(stream_.lowest_layer(), endpoint_iterator,
+ boost::bind(&client::handle_connect, this, _::error));
+ }
+
+ void handle_connect(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ std::cout << "Connect failed: " << error.message() << "\n";
+ return;
+ }
+ stream_.async_handshake(Botan::TLS::Connection_Side::CLIENT,
+ boost::bind(&client::handle_handshake, this, _::error));
+ }
+
+ void handle_handshake(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ std::cout << "Handshake failed: " << error.message() << "\n";
+ return;
+ }
+ http::async_write(stream_, request_,
+ boost::bind(&client::handle_write, this, _::error, _::bytes_transferred));
+ }
+
+ void handle_write(const boost::system::error_code& error, size_t)
+ {
+ if(error)
+ {
+ std::cout << "Write failed: " << error.message() << "\n";
+ return;
+ }
+ http::async_read(stream_, reply_, response_,
+ boost::bind(&client::handle_read, this, _::error, _::bytes_transferred));
+ }
+
+ void handle_read(const boost::system::error_code& error, size_t)
+ {
+ if(!error)
+ {
+ std::cout << "Reply: ";
+ std::cout << response_.body() << "\n";
+ }
+ else
+ {
+ std::cout << "Read failed: " << error.message() << "\n";
+ }
+ }
+
+ private:
+ http::request<http::dynamic_body> request_;
+ http::response<http::string_body> response_;
+ boost::beast::flat_buffer reply_;
+
+ Botan::TLS::Session_Manager_Noop session_mgr_;
+ Botan::AutoSeeded_RNG rng_;
+ Credentials_Manager credentials_mgr_;
+ Botan::TLS::Policy policy_;
+
+ Botan::TLS::Context ctx_;
+ Botan::TLS::Stream<boost::asio::ip::tcp::socket> stream_;
+ };
+
+ int main()
+ {
+ boost::asio::io_context io_context;
+
+ boost::asio::ip::tcp::resolver resolver(io_context);
+ boost::asio::ip::tcp::resolver::query query("botan.randombit.net", "443");
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+
+ http::request<http::string_body> req;
+ req.version(11);
+ req.method(http::verb::get);
+ req.target("/news.html");
+ req.set(http::field::host, "botan.randombit.net");
+
+ client c(io_context, iterator, req);
+
+ io_context.run();
+ }