blob: 00bfa46f440f1d2df4c4a06264dba3251b2c856f [file] [log] [blame]
// 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_backend.h"
#include "pw_log/log.h"
namespace {
int MbedtlsWrite(void* ctx, const unsigned char* buf, size_t len) {
TransportInterface* transport = *static_cast<TransportInterface**>(ctx);
int status = transport->Write(buf, len);
return status;
}
int MbedtlsRead(void* ctx, unsigned char* buf, size_t len) {
TransportInterface* transport = *static_cast<TransportInterface**>(ctx);
int status = transport->Read(buf, len);
if (status == 0) {
return MBEDTLS_ERR_SSL_WANT_READ;
}
return status;
}
// A dummy source for generating random bytes for demo purpose only.
// Real applicaiton should provide an implementation.
int DummyEntropySource(void*, unsigned char*, size_t len, size_t* olen) {
*olen = len;
return 0;
}
} // namespace
MbedtlsBackend::MbedtlsBackend() {
mbedtls_ssl_init(&ssl_);
mbedtls_ssl_config_init(&conf_);
mbedtls_x509_crt_init(&cacert_);
mbedtls_x509_crl_init(&cacrl_);
mbedtls_ctr_drbg_init(&ctr_drbg_);
mbedtls_entropy_init(&entropy_);
mbedtls_ssl_set_bio(&ssl_, &mbedtls_io_ctx_, MbedtlsWrite, MbedtlsRead, NULL);
}
int MbedtlsBackend::SetHostName(const char* host) {
int ret = 0;
if ((ret = mbedtls_ssl_set_hostname(&ssl_, host)) != 0) {
PW_LOG_INFO("Failed to set host name, -0x%x",
static_cast<unsigned int>(-ret));
return -1;
}
return 0;
}
int MbedtlsBackend::Setup() {
int ret = 0;
// Add a dummy entropy source.
const char* personalization_string = "ssl_client";
mbedtls_entropy_add_source(
&entropy_, DummyEntropySource, NULL, 16, MBEDTLS_ENTROPY_SOURCE_STRONG);
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg_,
mbedtls_entropy_func,
&entropy_,
(const unsigned char*)personalization_string,
sizeof(personalization_string) - 1)) != 0) {
PW_LOG_INFO("Failed to add entroy source, -0x%x",
static_cast<unsigned int>(-ret));
return -1;
}
if ((ret = mbedtls_ssl_config_defaults(&conf_,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
PW_LOG_INFO("Failed to setup default config. -0x%x",
static_cast<unsigned int>(-ret));
return -1;
}
mbedtls_ssl_conf_authmode(&conf_, MBEDTLS_SSL_VERIFY_OPTIONAL);
mbedtls_ssl_conf_ca_chain(&conf_, &cacert_, &cacrl_);
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
if ((ret = mbedtls_ssl_setup(&ssl_, &conf_)) != 0) {
PW_LOG_INFO("Failed to set up ssl. -0x%x", static_cast<unsigned int>(-ret));
return -1;
}
return 0;
}
int MbedtlsBackend::Handshake(TransportInterface* transport) {
if (Setup() < 0) {
return -1;
}
int ret = 0;
mbedtls_io_ctx_ = transport;
while ((ret = mbedtls_ssl_handshake(&ssl_)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
PW_LOG_INFO("Failed to handshake -0x%x", static_cast<unsigned int>(-ret));
return -1;
}
}
// Check certificate verification result.
uint32_t flags = 0;
if ((flags = mbedtls_ssl_get_verify_result(&ssl_)) != 0) {
char verify_buf[512];
mbedtls_x509_crt_verify_info(verify_buf, sizeof(verify_buf), " ! ", flags);
PW_LOG_INFO("certificate verification failed, %s", verify_buf);
return -1;
}
return 0;
}
int MbedtlsBackend::Write(const void* buffer,
size_t size,
TransportInterface* transport) {
mbedtls_io_ctx_ = transport;
int ret = 0;
while ((ret = mbedtls_ssl_write(
&ssl_, static_cast<const unsigned char*>(buffer), size)) <= 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
PW_LOG_INFO("Failed to write. -0x%x", static_cast<unsigned int>(-ret));
return -1;
}
}
return static_cast<int>(size);
}
int MbedtlsBackend::Read(void* buffer,
size_t size,
TransportInterface* transport) {
mbedtls_io_ctx_ = transport;
int ret = 0;
while (true) {
ret = mbedtls_ssl_read(&ssl_, static_cast<unsigned char*>(buffer), size);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
continue;
}
PW_LOG_INFO("Failed while reading. -0x%x",
static_cast<unsigned int>(-ret));
return -1;
}
return ret;
}
}
int MbedtlsBackend::LoadCACert(const void* buffer,
size_t size,
X509LoadFormat format) {
const unsigned char* data = static_cast<const unsigned char*>(buffer);
if (format == X509LoadFormat::kPEM || format == X509LoadFormat::kTryAll) {
int ret = mbedtls_x509_crt_parse(&cacert_, data, size);
if (ret == 0) {
return 0;
} else if (format == X509LoadFormat::kPEM) {
PW_LOG_INFO("Failed to load CA certificate as PEM. -0x%x",
static_cast<unsigned int>(-ret));
return ret;
}
}
if (format == X509LoadFormat::kDER || format == X509LoadFormat::kTryAll) {
int ret = mbedtls_x509_crt_parse_der(&cacert_, data, size);
if (ret == 0) {
return 0;
} else if (format == X509LoadFormat::kDER) {
PW_LOG_INFO("Failed to load CA certificate as DER. -0x%x",
static_cast<unsigned int>(-ret));
return ret;
}
}
return -1;
}
int MbedtlsBackend::LoadCrl(const void* buffer,
size_t size,
X509LoadFormat format) {
const unsigned char* data = static_cast<const unsigned char*>(buffer);
// TODO(zyecheng) mbedtls can't handle any critical extension tag in CRL.
// However, there is one "X509v3 Issuing Distribution Point" for
// google CRLs in GTS CA 101. We'll need to find a way to bypass it.
// For context, see https://github.com/ARMmbed/mbedtls/pull/3564
if (format == X509LoadFormat::kPEM || format == X509LoadFormat::kTryAll) {
int ret = mbedtls_x509_crl_parse(&cacrl_, data, size);
if (ret == 0) {
return 0;
} else if (format == X509LoadFormat::kPEM) {
PW_LOG_INFO("Failed to load crl as PEM. -0x%x",
static_cast<unsigned int>(-ret));
return ret;
}
}
if (format == X509LoadFormat::kDER || format == X509LoadFormat::kTryAll) {
int ret = mbedtls_x509_crl_parse_der(&cacrl_, data, size);
if (ret == 0) {
return 0;
} else if (format == X509LoadFormat::kDER) {
PW_LOG_INFO("Failed to load crlas DER. -0x%x",
static_cast<unsigned int>(-ret));
return ret;
}
}
return -1;
}
TlsInterface* CreateTls() { return new MbedtlsBackend(); }