• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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