• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "mbedtls/ssl.h"
16 #include "pw_assert/check.h"
17 #include "pw_log/log.h"
18 #include "pw_tls_client/entropy.h"
19 #include "pw_tls_client/session.h"
20 #include "pw_tls_client_mbedtls/backend_types.h"
21 
22 namespace pw::tls_client {
23 namespace backend {
24 
MbedTlsWrite(void * ctx,const uint8_t * buf,size_t len)25 int SessionImplementation::MbedTlsWrite(void* ctx,
26                                         const uint8_t* buf,
27                                         size_t len) {
28   PW_CHECK_NOTNULL(ctx);
29   PW_CHECK_NOTNULL(buf);
30   auto writer =
31       static_cast<SessionImplementation*>(ctx)->session_options_.transport();
32   PW_CHECK_NOTNULL(writer);
33   return writer->Write(buf, len).ok() ? len : -1;
34 }
35 
MbedTlsRead(void * ctx,unsigned char * buf,size_t len)36 int SessionImplementation::MbedTlsRead(void* ctx,
37                                        unsigned char* buf,
38                                        size_t len) {
39   PW_CHECK_NOTNULL(ctx);
40   PW_CHECK_NOTNULL(buf);
41   auto reader =
42       static_cast<SessionImplementation*>(ctx)->session_options_.transport();
43   PW_CHECK_NOTNULL(reader);
44   auto res = reader->Read(buf, len);
45   if (!res.ok()) {
46     return -1;
47   }
48   return res.value().empty() ? MBEDTLS_ERR_SSL_WANT_READ : res.value().size();
49 }
50 
51 Status SessionImplementation::entropy_source_status_ = OkStatus();
52 
SetEntropySourceStatus(Status status)53 void SessionImplementation::SetEntropySourceStatus(Status status) {
54   entropy_source_status_ = status;
55 }
56 
57 // Entropy source callback
MbedTlsEntropySource(void * ctx,unsigned char * out,size_t len,size_t * output_length)58 int SessionImplementation::MbedTlsEntropySource(void* ctx,
59                                                 unsigned char* out,
60                                                 size_t len,
61                                                 size_t* output_length) {
62   Status status;
63   if (entropy_source_status_ != OkStatus()) {
64     status = entropy_source_status_;
65   } else {
66     status = GetRandomBytes({out, len});
67   }
68 
69   if (!status.ok()) {
70     PW_LOG_DEBUG("Failed to generate random bytes");
71     auto session_impl = static_cast<SessionImplementation*>(ctx);
72     session_impl->SetTlsStatus(pw::tls_client::TLSStatus::kEntropySourceFailed);
73     return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
74   }
75   *output_length = len;
76   return 0;
77 }
78 
SessionImplementation(SessionOptions options)79 SessionImplementation::SessionImplementation(SessionOptions options)
80     : session_options_(options) {
81   mbedtls_ssl_init(&ssl_ctx_);
82   mbedtls_ssl_config_init(&ssl_config_);
83   mbedtls_ctr_drbg_init(&drbg_ctx_);
84   mbedtls_entropy_init(&entropy_ctx_);
85 }
86 
~SessionImplementation()87 SessionImplementation::~SessionImplementation() {
88   mbedtls_ssl_free(&ssl_ctx_);
89   mbedtls_ssl_config_free(&ssl_config_);
90   mbedtls_ctr_drbg_free(&drbg_ctx_);
91   mbedtls_entropy_free(&entropy_ctx_);
92 }
93 
Setup()94 Status SessionImplementation::Setup() {
95   int ret = 0;
96 
97   // Set up default configuration.
98   ret = mbedtls_ssl_config_defaults(
99       &ssl_config_,
100       // Configured as client.
101       MBEDTLS_SSL_IS_CLIENT,
102       // Statndard TLS. The other option is MBEDTLS_SSL_TRANSPORT_DATAGRAM
103       // for DTLS, which we'll consider later.
104       MBEDTLS_SSL_TRANSPORT_STREAM,
105       // This option is used in all MbedTLS native examples.
106       // The other option is MBEDTLS_SSL_PRESET_SUITEB.
107       // However, there is no document/comment availalbe on what they do.
108       // Base on the source code, these options will restrict the version
109       // of TLS protocol. MBEDTLS_SSL_PRESET_SUITEB forces TLS 1.2.
110       // MBEDTLS_SSL_PRESET_DEFAULT is more relaxed. But since we
111       // define MBEDTLS_SSL_PROTO_TLS1_2 for all configs. There shouldn't be
112       // any difference.
113       MBEDTLS_SSL_PRESET_DEFAULT);
114   if (ret) {
115     return Status::Internal();
116   }
117 
118   // Set up an entropy source.
119   ret = mbedtls_entropy_add_source(&entropy_ctx_,
120                                    MbedTlsEntropySource,
121                                    this,
122                                    1,
123                                    MBEDTLS_ENTROPY_SOURCE_STRONG);
124   if (ret) {
125     return Status::Internal();
126   }
127 
128   // Set up drbg.
129   unsigned char personalized_bytes[] = "pw_tls_client";
130   ret = mbedtls_ctr_drbg_seed(&drbg_ctx_,
131                               mbedtls_entropy_func,
132                               &entropy_ctx_,
133                               personalized_bytes,
134                               sizeof(personalized_bytes));
135   if (ret) {
136     if (ret == MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED) {
137       tls_status_ = TLSStatus::kEntropySourceFailed;
138     }
139     return Status::Internal();
140   }
141 
142   // The API does not fail.
143   mbedtls_ssl_conf_rng(&ssl_config_, mbedtls_ctr_drbg_random, &drbg_ctx_);
144 
145   // The API does not fail.
146   mbedtls_ssl_conf_authmode(&ssl_config_, MBEDTLS_SSL_VERIFY_REQUIRED);
147 
148   // TODO(pwbug/398): Add logic for loading trust anchors.
149 
150   // Load configuration to SSL.
151   ret = mbedtls_ssl_setup(&ssl_ctx_, &ssl_config_);
152   if (ret) {
153     return Status::Internal();
154   }
155 
156   // Set up transport.
157   // The API does not fail.
158   mbedtls_ssl_set_bio(&ssl_ctx_, this, MbedTlsWrite, MbedTlsRead, nullptr);
159 
160   ret = mbedtls_ssl_set_hostname(&ssl_ctx_,
161                                  session_options_.server_name().data());
162   if (ret) {
163     return Status::Internal();
164   }
165 
166   return OkStatus();
167 }
168 
169 }  // namespace backend
170 
Session(const SessionOptions & options)171 Session::Session(const SessionOptions& options) : session_impl_(options) {}
172 
173 Session::~Session() = default;
174 
Create(const SessionOptions & options)175 Result<Session*> Session::Create(const SessionOptions& options) {
176   if (!options.transport()) {
177     PW_LOG_DEBUG("Must provide a transport");
178     return Status::Internal();
179   }
180 
181   auto sess = new Session(options);
182   if (!sess) {
183     return Status::ResourceExhausted();
184   }
185 
186   // Set up the client.
187   auto setup_status = sess->session_impl_.Setup();
188   if (!setup_status.ok()) {
189     PW_LOG_DEBUG("Failed to setup");
190     // TODO(pwbug/398): `tls_status_` may be set, but the session object will
191     // be released. Map `tls_stauts_` to string and print out here so that
192     // the information can be catched.
193     delete sess;
194     return setup_status;
195   }
196 
197   return sess;
198 }
199 
Open()200 Status Session::Open() {
201   // TODO(pwbug/398): To implement
202   return Status::Unimplemented();
203 }
204 
Close()205 Status Session::Close() {
206   // TODO(pwbug/398): To implement
207   return Status::Unimplemented();
208 }
209 
DoRead(ByteSpan)210 StatusWithSize Session::DoRead(ByteSpan) {
211   // TODO(pwbug/398): To implement
212   return StatusWithSize(Status::Unimplemented(), 0);
213 }
214 
DoWrite(ConstByteSpan)215 Status Session::DoWrite(ConstByteSpan) {
216   // TODO(pwbug/398): To implement
217   return Status::Unimplemented();
218 }
219 
GetLastTLSStatus()220 TLSStatus Session::GetLastTLSStatus() { return session_impl_.GetTlsStatus(); }
221 
222 }  // namespace pw::tls_client
223