xref: /aosp_15_r20/external/pigweed/pw_tls_client/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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