223 lines
6.8 KiB
C++
223 lines
6.8 KiB
C++
// Copyright 2021 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
#include "mbedtls/ssl.h"
|
|
#include "pw_assert/check.h"
|
|
#include "pw_log/log.h"
|
|
#include "pw_tls_client/entropy.h"
|
|
#include "pw_tls_client/session.h"
|
|
#include "pw_tls_client_mbedtls/backend_types.h"
|
|
|
|
namespace pw::tls_client {
|
|
namespace backend {
|
|
|
|
int SessionImplementation::MbedTlsWrite(void* ctx,
|
|
const uint8_t* buf,
|
|
size_t len) {
|
|
PW_CHECK_NOTNULL(ctx);
|
|
PW_CHECK_NOTNULL(buf);
|
|
auto writer =
|
|
static_cast<SessionImplementation*>(ctx)->session_options_.transport();
|
|
PW_CHECK_NOTNULL(writer);
|
|
return writer->Write(buf, len).ok() ? len : -1;
|
|
}
|
|
|
|
int SessionImplementation::MbedTlsRead(void* ctx,
|
|
unsigned char* buf,
|
|
size_t len) {
|
|
PW_CHECK_NOTNULL(ctx);
|
|
PW_CHECK_NOTNULL(buf);
|
|
auto reader =
|
|
static_cast<SessionImplementation*>(ctx)->session_options_.transport();
|
|
PW_CHECK_NOTNULL(reader);
|
|
auto res = reader->Read(buf, len);
|
|
if (!res.ok()) {
|
|
return -1;
|
|
}
|
|
return res.value().empty() ? MBEDTLS_ERR_SSL_WANT_READ : res.value().size();
|
|
}
|
|
|
|
Status SessionImplementation::entropy_source_status_ = OkStatus();
|
|
|
|
void SessionImplementation::SetEntropySourceStatus(Status status) {
|
|
entropy_source_status_ = status;
|
|
}
|
|
|
|
// Entropy source callback
|
|
int SessionImplementation::MbedTlsEntropySource(void* ctx,
|
|
unsigned char* out,
|
|
size_t len,
|
|
size_t* output_length) {
|
|
Status status;
|
|
if (entropy_source_status_ != OkStatus()) {
|
|
status = entropy_source_status_;
|
|
} else {
|
|
status = GetRandomBytes({out, len});
|
|
}
|
|
|
|
if (!status.ok()) {
|
|
PW_LOG_DEBUG("Failed to generate random bytes");
|
|
auto session_impl = static_cast<SessionImplementation*>(ctx);
|
|
session_impl->SetTlsStatus(pw::tls_client::TLSStatus::kEntropySourceFailed);
|
|
return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
|
|
}
|
|
*output_length = len;
|
|
return 0;
|
|
}
|
|
|
|
SessionImplementation::SessionImplementation(SessionOptions options)
|
|
: session_options_(options) {
|
|
mbedtls_ssl_init(&ssl_ctx_);
|
|
mbedtls_ssl_config_init(&ssl_config_);
|
|
mbedtls_ctr_drbg_init(&drbg_ctx_);
|
|
mbedtls_entropy_init(&entropy_ctx_);
|
|
}
|
|
|
|
SessionImplementation::~SessionImplementation() {
|
|
mbedtls_ssl_free(&ssl_ctx_);
|
|
mbedtls_ssl_config_free(&ssl_config_);
|
|
mbedtls_ctr_drbg_free(&drbg_ctx_);
|
|
mbedtls_entropy_free(&entropy_ctx_);
|
|
}
|
|
|
|
Status SessionImplementation::Setup() {
|
|
int ret = 0;
|
|
|
|
// Set up default configuration.
|
|
ret = mbedtls_ssl_config_defaults(
|
|
&ssl_config_,
|
|
// Configured as client.
|
|
MBEDTLS_SSL_IS_CLIENT,
|
|
// Statndard TLS. The other option is MBEDTLS_SSL_TRANSPORT_DATAGRAM
|
|
// for DTLS, which we'll consider later.
|
|
MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
// This option is used in all MbedTLS native examples.
|
|
// The other option is MBEDTLS_SSL_PRESET_SUITEB.
|
|
// However, there is no document/comment availalbe on what they do.
|
|
// Base on the source code, these options will restrict the version
|
|
// of TLS protocol. MBEDTLS_SSL_PRESET_SUITEB forces TLS 1.2.
|
|
// MBEDTLS_SSL_PRESET_DEFAULT is more relaxed. But since we
|
|
// define MBEDTLS_SSL_PROTO_TLS1_2 for all configs. There shouldn't be
|
|
// any difference.
|
|
MBEDTLS_SSL_PRESET_DEFAULT);
|
|
if (ret) {
|
|
return Status::Internal();
|
|
}
|
|
|
|
// Set up an entropy source.
|
|
ret = mbedtls_entropy_add_source(&entropy_ctx_,
|
|
MbedTlsEntropySource,
|
|
this,
|
|
1,
|
|
MBEDTLS_ENTROPY_SOURCE_STRONG);
|
|
if (ret) {
|
|
return Status::Internal();
|
|
}
|
|
|
|
// Set up drbg.
|
|
unsigned char personalized_bytes[] = "pw_tls_client";
|
|
ret = mbedtls_ctr_drbg_seed(&drbg_ctx_,
|
|
mbedtls_entropy_func,
|
|
&entropy_ctx_,
|
|
personalized_bytes,
|
|
sizeof(personalized_bytes));
|
|
if (ret) {
|
|
if (ret == MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED) {
|
|
tls_status_ = TLSStatus::kEntropySourceFailed;
|
|
}
|
|
return Status::Internal();
|
|
}
|
|
|
|
// The API does not fail.
|
|
mbedtls_ssl_conf_rng(&ssl_config_, mbedtls_ctr_drbg_random, &drbg_ctx_);
|
|
|
|
// The API does not fail.
|
|
mbedtls_ssl_conf_authmode(&ssl_config_, MBEDTLS_SSL_VERIFY_REQUIRED);
|
|
|
|
// TODO(pwbug/398): Add logic for loading trust anchors.
|
|
|
|
// Load configuration to SSL.
|
|
ret = mbedtls_ssl_setup(&ssl_ctx_, &ssl_config_);
|
|
if (ret) {
|
|
return Status::Internal();
|
|
}
|
|
|
|
// Set up transport.
|
|
// The API does not fail.
|
|
mbedtls_ssl_set_bio(&ssl_ctx_, this, MbedTlsWrite, MbedTlsRead, nullptr);
|
|
|
|
ret = mbedtls_ssl_set_hostname(&ssl_ctx_,
|
|
session_options_.server_name().data());
|
|
if (ret) {
|
|
return Status::Internal();
|
|
}
|
|
|
|
return OkStatus();
|
|
}
|
|
|
|
} // namespace backend
|
|
|
|
Session::Session(const SessionOptions& options) : session_impl_(options) {}
|
|
|
|
Session::~Session() = default;
|
|
|
|
Result<Session*> Session::Create(const SessionOptions& options) {
|
|
if (!options.transport()) {
|
|
PW_LOG_DEBUG("Must provide a transport");
|
|
return Status::Internal();
|
|
}
|
|
|
|
auto sess = new Session(options);
|
|
if (!sess) {
|
|
return Status::ResourceExhausted();
|
|
}
|
|
|
|
// Set up the client.
|
|
auto setup_status = sess->session_impl_.Setup();
|
|
if (!setup_status.ok()) {
|
|
PW_LOG_DEBUG("Failed to setup");
|
|
// TODO(pwbug/398): `tls_status_` may be set, but the session object will
|
|
// be released. Map `tls_stauts_` to string and print out here so that
|
|
// the information can be catched.
|
|
delete sess;
|
|
return setup_status;
|
|
}
|
|
|
|
return sess;
|
|
}
|
|
|
|
Status Session::Open() {
|
|
// TODO(pwbug/398): To implement
|
|
return Status::Unimplemented();
|
|
}
|
|
|
|
Status Session::Close() {
|
|
// TODO(pwbug/398): To implement
|
|
return Status::Unimplemented();
|
|
}
|
|
|
|
StatusWithSize Session::DoRead(ByteSpan) {
|
|
// TODO(pwbug/398): To implement
|
|
return StatusWithSize(Status::Unimplemented(), 0);
|
|
}
|
|
|
|
Status Session::DoWrite(ConstByteSpan) {
|
|
// TODO(pwbug/398): To implement
|
|
return Status::Unimplemented();
|
|
}
|
|
|
|
TLSStatus Session::GetLastTLSStatus() { return session_impl_.GetTlsStatus(); }
|
|
|
|
} // namespace pw::tls_client
|