1.. _module-pw_tls_client: 2 3-------------- 4pw_tls_client 5-------------- 6 7This module provides a facade that defines the public APIs for establishing TLS 8sessions over arbitrary transports. Two options of backends, 9pw_tls_client_mbedtls and pw_tls_client_boringssl, which are based on BoringSSL 10and MbedTLS libraries, are under construction. 11 12The facade provides a class ``pw::tls_client::Session`` with Open(), Read(), 13Write() and Close() methods for TLS communication. An instance is created by 14``pw::tls_client::Session::Create`` method. The method takes a 15``pw::tls_client::SessionOptions`` object, which is used to configure TLS 16connection options. The list of supported configurations currently include: 17 181. Host name of the target server. This will be used as the Server Name 19 Indication(SNI) extension during TLS handshake. 20 212. User-implemented transport. The underlying transport for the TLS 22 communication. It is an object that implements the interface of 23 ``pw::stream::ReaderWriter``. 24 25The module will also provide mechanisms/APIs for users to specify sources of 26trust anchors, time and entropy. These are under construction. 27 28.. warning:: 29 This module is under construction, not ready for use, and the documentation 30 is incomplete. 31 32Prerequisites 33============= 34This module requires the following dependencies: 35 361. Entropy 37----------- 38TLS requires an entropy source for generating random bytes. Users of this 39module should provide one by implementing a backend to the 40``pw_tls_client:entropy`` facade. The backend defaults to 41``pw_tls_client:fake_entropy`` that does nothing. 42 432. Chromium Verifier 44--------------------- 45BoringSSL backend uses chromium verifier for certication verification. If the 46downstream project uses BoringSSL as the backend, the sources of the verifier, 47which is part of the chorimum sources, needs to be downloaded in order for 48``//third_party/chromium_verifier`` to build. It is recommended to use our 49support in pw_package for downloading compatible and tested version: 50 51.. code-block:: sh 52 53 pw package install chromium_verifier 54 55Then follow instruction for setting ``dir_pw_third_party_chromium_verifier`` to 56the path of the downloaded repo. 57 583. Date time 59------------- 60TLS needs a trust-worthy source of wall clock time in order to check 61expiration. Provisioning of time source for TLS communication is very specific 62to the TLS library in use. However, common TLS libraires, such as BoringSSL 63and MbedTLS, support the use of C APIs ``time()`` and ``getimtofday()`` for 64obtaining date time. To accomodate the use of these libraries, a facade target 65``pw_tls_client:time`` is added that wraps these APIs. For GN builds, 66specify the backend target with variable ``pw_tls_client_TIME_BACKEND``. 67``pw_tls_client_TIME_BACKEND`` defaults to the ``pw_chrono::wrap_time_build_time`` 68backend that returns build time. 69 70If downstream project chooses to use other TLS libraires that handle time source 71differently, then it needs to be investigated separately. 72 734. CRLSet 74----------- 75The module supports CRLSet based revocation check for certificates. A CRLSet 76file specifies a list of X509 certificates that either need to be blocked, or 77have been revoked by the issuer. It is introduced by chromium and primarily 78used for certificate verification/revocation checks during TLS handshake. The 79format of a CRLSet file is available in 80https://chromium.googlesource.com/chromium/src/+/refs/heads/main/net/cert/crl_set.cc#24. 81 82Downstream projects need to provide a CRLSet file at build time. For GN builds, 83specify the path of the CRLSet file with the GN variable 84``pw_tls_client_CRLSET_FILE``. This module converts the CRLSet file into 85source code at build time and generates APIs for querying certificate 86block/revocation status. See ``pw_tls_client/crlset.h`` for more detail. 87 88Chromium maintains its own CRLSet that targets at the general Internet. To use it, 89run the following command to download the latest version: 90 91.. code-block:: sh 92 93 pw package install crlset --force 94 95The `--force` option forces CRLSet to be always re-downloaded so that it is 96up-to-date. Project that are concerned about up-to-date CRLSet should always 97run the above command before build. 98 99Toolings will be provided for generating custom CRLSet files from user-provided 100certificate files. The functionality is under construction. 101 102Setup 103===== 104This module requires the following setup: 105 1061. Choose a ``pw_tls_client`` backend, or write one yourself. 1072. If using GN build, Specify the ``pw_tls_client_BACKEND`` GN build arg to 108 point the library that provides a ``pw_tls_client`` backend. To use the 109 MbedTLS backend, set variable ``pw_tls_client_BACKEND`` to 110 ``//pw_tls_client_mbedtls``. To use the BoringSSL backend, set it to 111 ``//pw_tls_client_boringssl``. 1123. Provide a `pw_tls_client:entropy` backend. If using GN build, specify the 113 backend with variable ``pw_tls_client_ENTROPY_BACKEND``. 114 115Module usage 116============ 117For GN build, add ``//pw_tls_client`` to the dependency list. 118 119The following gives an example code for using the module on host platform. 120The example uses a Pigweed socket stream as the transport and performs TLS 121connection to www.google.com: 122 123.. code-block:: cpp 124 125 // Host domain name 126 constexpr char kHost[] = "www.google.com"; 127 128 constexpr int kPort = 443; 129 130 // Server Name Indication. 131 constexpr const char* kServerNameIndication = kHost; 132 133 // An example message to send. 134 constexpr char kHTTPRequest[] = "GET / HTTP/1.1\r\n\r\n"; 135 136 // pw::stream::SocketStream doesn't accept host domain name as input. Thus we 137 // introduce this helper function for getting the IP address 138 pw::Status GetIPAddrFromHostName(std::string_view host, pw::span<char> ip) { 139 char null_terminated_host_name[256] = {0}; 140 auto host_copy_status = pw::string::Copy(host, null_terminated_host_name); 141 if (!host_copy_status.ok()) { 142 return host_copy_status.status(); 143 } 144 145 struct hostent* ent = gethostbyname(null_terminated_host_name); 146 if (ent == NULL) { 147 return PW_STATUS_INTERNAL; 148 } 149 150 in_addr** addr_list = reinterpret_cast<in_addr**>(ent->h_addr_list); 151 if (addr_list[0] == nullptr) { 152 return PW_STATUS_INTERNAL; 153 } 154 155 auto ip_copy_status = pw::string::Copy(inet_ntoa(*addr_list[0]), ip); 156 if (!ip_copy_status.ok()) { 157 return ip_copy_status.status(); 158 } 159 160 return pw::OkStatus(); 161 } 162 163 int main() { 164 // Get the IP address of the target host. 165 char ip_address[64] = {0}; 166 auto get_ip_status = GetIPAddrFromHostName(kHost, ip_address); 167 if (!get_ip_status.ok()) { 168 return 1; 169 } 170 171 // Use a socket stream as the transport. 172 pw::stream::SocketStream socket_stream; 173 174 // Connect the socket to the remote host. 175 auto socket_connect_status = socket_stream.Connect(ip_address, kPort); 176 if (!socket_connect_status.ok()) { 177 return 1; 178 } 179 180 // Create a TLS session. Register the transport. 181 auto options = pw::tls_client::SessionOptions() 182 .set_server_name(kServerNameIndication) 183 .set_transport(socket_stream); 184 auto tls_conn = pw::tls_client::Session::Create(options); 185 if (!tls_conn.ok()) { 186 // Handle errors. 187 return 1; 188 } 189 190 auto open_status = tls_conn.value()->Open(); 191 if (!open_status.ok()) { 192 // Inspect/handle error with open_status.code() and 193 // tls_conn.value()->GetLastTLSStatus(). 194 return 1; 195 } 196 197 auto write_status = tls_conn.value()->Write(pw::as_bytes(pw::span{kHTTPRequest})); 198 if (!write_status.ok()) { 199 // Inspect/handle error with write_status.code() and 200 // tls_conn.value()->GetLastTLSStatus(). 201 return 0; 202 } 203 204 // Listen for incoming data. 205 std::array<std::byte, 4096> buffer; 206 while (true) { 207 auto res = tls_conn.value()->Read(buffer); 208 if (!res.ok()) { 209 // Inspect/handle error with res.status().code() and 210 // tls_conn.value()->GetLastTLSStatus(). 211 return 1; 212 } 213 214 // Process data in |buffer|. res.value() gives the span of read bytes. 215 // The following simply print to console. 216 if (res.value().size()) { 217 auto print_status = pw::sys_io::WriteBytes(res.value()); 218 if (!print_status.ok()) { 219 return 1; 220 } 221 } 222 223 } 224 } 225 226A list of other demos will be provided in ``//pw_tls_client/examples/`` 227 228.. warning:: 229 Open()/Read() APIs are synchronous for now. Support for 230 non-blocking/asynchronous usage will be added in the future. 231 232 233.. toctree:: 234 :hidden: 235 :maxdepth: 1 236 237 Backends <backends> 238