.. _module-pw_tls_client: -------------- pw_tls_client -------------- This module provides a facade that defines the public APIs for establishing TLS sessions over arbitrary transports. Two options of backends, pw_tls_client_mbedtls and pw_tls_client_boringssl, which are based on BoringSSL and MbedTLS libraries, are under construction. The facade provides a class ``pw::tls_client::Session`` with Open(), Read(), Write() and Close() methods for TLS communication. An instance is created by ``pw::tls_client::Session::Create`` method. The method takes a ``pw::tls_client::SessionOptions`` object, which is used to configure TLS connection options. The list of supported configurations currently include: 1. Host name of the target server. This will be used as the Server Name Indication(SNI) extension during TLS handshake. 2. User-implemented transport. The underlying transport for the TLS communication. It is an object that implements the interface of ``pw::stream::ReaderWriter``. The module will also provide mechanisms/APIs for users to specify sources of trust anchors, time and entropy. These are under construction. .. warning:: This module is under construction, not ready for use, and the documentation is incomplete. Setup ===== This module requires the following setup: 1. Choose a ``pw_tls_client`` backend, or write one yourself. 2. If using GN build, Specify the ``pw_tls_client_BACKEND`` GN build arg to point the library that provides a ``pw_tls_client`` backend. Module usage ============ For GN build, add ``//pw_tls_client`` to the dependency list. The following gives an example code for using the module on host platform. The example uses a Pigweed socket stream as the transport and performs TLS connection to www.google.com: .. code-block:: cpp // Host domain name constexpr char kHost[] = "www.google.com"; constexpr int kPort = 443; // Server Name Indication. constexpr const char* kServerNameIndication = kHost; // An example message to send. constexpr char kHTTPRequest[] = "GET / HTTP/1.1\r\n\r\n"; // pw::stream::SocketStream doesn't accept host domain name as input. Thus we // introduce this helper function for getting the IP address pw::Status GetIPAddrFromHostName(std::string_view host, std::span ip) { char null_terminated_host_name[256] = {0}; auto host_copy_status = pw::string::Copy(host, null_terminated_host_name); if (!host_copy_status.ok()) { return host_copy_status.status(); } struct hostent* ent = gethostbyname(null_terminated_host_name); if (ent == NULL) { return PW_STATUS_INTERNAL; } in_addr** addr_list = reinterpret_cast(ent->h_addr_list); if (addr_list[0] == nullptr) { return PW_STATUS_INTERNAL; } auto ip_copy_status = pw::string::Copy(inet_ntoa(*addr_list[0]), ip); if (!ip_copy_status.ok()) { return ip_copy_status.status(); } return pw::OkStatus(); } int main() { // Get the IP address of the target host. char ip_address[64] = {0}; auto get_ip_status = GetIPAddrFromHostName(kHost, ip_address); if (!get_ip_status.ok()) { return 1; } // Use a socket stream as the transport. pw::stream::SocketStream socket_stream; // Connect the socket to the remote host. auto socket_connect_status = socket_stream.Connect(ip_address, kPort); if (!socket_connect_status.ok()) { return 1; } // Create a TLS session. Register the transport. auto options = pw::tls_client::SessionOptions() .set_server_name(kServerNameIndication) .set_transport(socket_stream); auto tls_conn = pw::tls_client::Session::Create(options); if (!tls_conn.ok()) { // Handle errors. return 1; } auto open_status = tls_conn.value()->Open(); if (!open_status.ok()) { // Inspect/handle error with open_status.code() and // tls_conn.value()->GetLastTLSStatus(). return 1; } auto write_status = tls_conn.value()->Write(std::as_bytes(std::span{kHTTPRequest})); if (!write_status.ok()) { // Inspect/handle error with write_status.code() and // tls_conn.value()->GetLastTLSStatus(). return 0; } // Listen for incoming data. std::array buffer; while (true) { auto res = tls_conn.value()->Read(buffer); if (!res.ok()) { // Inspect/handle error with res.status().code() and // tls_conn.value()->GetLastTLSStatus(). return 1; } // Process data in |buffer|. res.value() gives the span of read bytes. // The following simply print to console. if (res.value().size()) { auto print_status = pw::sys_io::WriteBytes(res.value()); if (!print_status.ok()) { return 1; } } } } A list of other demos will be provided in ``//pw_tls_client/examples/`` Warning ============ Open()/Read() APIs are synchronous for now. Support for non-blocking/asynchronous usage will be added in the future.