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