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