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