blob: 1429ef59fd75c4174691b93fb37424fd41bc3cff [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
64``pw_tls_client:time`` is added that wraps these APIs. For gn build,
65specify 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 Zhao08dd6a52021-05-10 15:50:22 -070072Setup
73=====
74This module requires the following setup:
75
76 1. Choose a ``pw_tls_client`` backend, or write one yourself.
77 2. If using GN build, Specify the ``pw_tls_client_BACKEND`` GN build arg to
Yecheng Zhaofb666552021-06-22 10:02:43 -070078 point the library that provides a ``pw_tls_client`` backend. To use the
79 MbedTLS backend, set variable ``pw_tls_client_BACKEND`` to
80 ``//pw_tls_client_mbedtls``. To use the BoringSSL backend, set it to
81 ``//pw_tls_client_boringssl``.
Yecheng Zhaoe5dbfc02021-06-07 16:38:48 -070082 3. Provide a `pw_tls_client:entropy` backend. If using GN build, specify the
83 backend with variable ``pw_tls_client_ENTROPY_BACKEND``.
Yecheng Zhao08dd6a52021-05-10 15:50:22 -070084
85Module usage
86============
87For GN build, add ``//pw_tls_client`` to the dependency list.
88
89The following gives an example code for using the module on host platform.
90The example uses a Pigweed socket stream as the transport and performs TLS
91connection to www.google.com:
92
93.. code-block:: cpp
94
95 // Host domain name
96 constexpr char kHost[] = "www.google.com";
97
98 constexpr int kPort = 443;
99
100 // Server Name Indication.
101 constexpr const char* kServerNameIndication = kHost;
102
103 // An example message to send.
104 constexpr char kHTTPRequest[] = "GET / HTTP/1.1\r\n\r\n";
105
106 // pw::stream::SocketStream doesn't accept host domain name as input. Thus we
107 // introduce this helper function for getting the IP address
108 pw::Status GetIPAddrFromHostName(std::string_view host, std::span<char> ip) {
109 char null_terminated_host_name[256] = {0};
110 auto host_copy_status = pw::string::Copy(host, null_terminated_host_name);
111 if (!host_copy_status.ok()) {
112 return host_copy_status.status();
113 }
114
115 struct hostent* ent = gethostbyname(null_terminated_host_name);
116 if (ent == NULL) {
117 return PW_STATUS_INTERNAL;
118 }
119
120 in_addr** addr_list = reinterpret_cast<in_addr**>(ent->h_addr_list);
121 if (addr_list[0] == nullptr) {
122 return PW_STATUS_INTERNAL;
123 }
124
125 auto ip_copy_status = pw::string::Copy(inet_ntoa(*addr_list[0]), ip);
126 if (!ip_copy_status.ok()) {
127 return ip_copy_status.status();
128 }
129
130 return pw::OkStatus();
131 }
132
133 int main() {
134 // Get the IP address of the target host.
135 char ip_address[64] = {0};
136 auto get_ip_status = GetIPAddrFromHostName(kHost, ip_address);
137 if (!get_ip_status.ok()) {
138 return 1;
139 }
140
141 // Use a socket stream as the transport.
142 pw::stream::SocketStream socket_stream;
143
144 // Connect the socket to the remote host.
145 auto socket_connect_status = socket_stream.Connect(ip_address, kPort);
146 if (!socket_connect_status.ok()) {
147 return 1;
148 }
149
150 // Create a TLS session. Register the transport.
151 auto options = pw::tls_client::SessionOptions()
152 .set_server_name(kServerNameIndication)
153 .set_transport(socket_stream);
154 auto tls_conn = pw::tls_client::Session::Create(options);
155 if (!tls_conn.ok()) {
156 // Handle errors.
157 return 1;
158 }
159
160 auto open_status = tls_conn.value()->Open();
161 if (!open_status.ok()) {
162 // Inspect/handle error with open_status.code() and
163 // tls_conn.value()->GetLastTLSStatus().
164 return 1;
165 }
166
167 auto write_status = tls_conn.value()->Write(std::as_bytes(std::span{kHTTPRequest}));
168 if (!write_status.ok()) {
169 // Inspect/handle error with write_status.code() and
170 // tls_conn.value()->GetLastTLSStatus().
171 return 0;
172 }
173
174 // Listen for incoming data.
175 std::array<std::byte, 4096> buffer;
176 while (true) {
177 auto res = tls_conn.value()->Read(buffer);
178 if (!res.ok()) {
179 // Inspect/handle error with res.status().code() and
180 // tls_conn.value()->GetLastTLSStatus().
181 return 1;
182 }
183
184 // Process data in |buffer|. res.value() gives the span of read bytes.
185 // The following simply print to console.
186 if (res.value().size()) {
187 auto print_status = pw::sys_io::WriteBytes(res.value());
188 if (!print_status.ok()) {
189 return 1;
190 }
191 }
192
193 }
194 }
195
196A list of other demos will be provided in ``//pw_tls_client/examples/``
197
198Warning
199============
200
201Open()/Read() APIs are synchronous for now. Support for
202non-blocking/asynchronous usage will be added in the future.