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