Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions adapters/httpapi_curl.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@
#include "wolfssl/ssl.h"
#include "wolfssl/error-ssl.h"
#elif USE_MBEDTLS
#include "mbedtls/version.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/ssl.h"
#define TLSIO_MBEDTLS_VERSION_3_0_0 0x03000000
#define TLSIO_MBEDTLS_VERSION_4_0_0 0x04000000
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
// mbedTLS 4.x removed the public entropy and CTR_DRBG modules in favor of the
// PSA Crypto random generator.
#include "psa/crypto.h"
#else
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#define TLSIO_MBEDTLS_VERSION_3_0_0 0x03000000
#endif
#endif
#include "azure_c_shared_utility/shared_util_options.h"
#include "azure_c_shared_utility/safe_math.h"
Expand Down Expand Up @@ -333,11 +341,46 @@ static size_t ContentWriteFunction(void *ptr, size_t size, size_t nmemb, void *u
}

#ifdef USE_MBEDTLS
// mbedTLS 4.x requires psa_crypto_init() to have succeeded before ANY
// cryptographic operation, including indirect ones such as parsing a
// certificate or a private key. It is idempotent, and a no-op on earlier
// versions, which do not have a PSA subsystem to bring up.
static int init_psa_crypto(void)
{
int result = 0;

#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
psa_status_t psa_status = psa_crypto_init();

if (psa_status != PSA_SUCCESS)
{
LogError("psa_crypto_init failed (%d)", (int)psa_status);
result = MU_FAILURE;
}
#endif // MBEDTLS_VERSION_NUMBER

return result;
}

static int parse_key(const char* key, mbedtls_pk_context* out_parsed_key)
{
int result;

#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_3_0_0
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
// mbedTLS 4.x takes its randomness from PSA Crypto. The caller is expected
// to have brought PSA up already; this call is a cheap idempotent safety
// net in case parse_key() ever gains another caller.
if (init_psa_crypto() != 0)
{
result = MU_FAILURE;
}
else if ((result = mbedtls_pk_parse_key(out_parsed_key,
(const unsigned char *)key, (int)(strlen(key) + 1),
NULL, 0)) != 0)
{
LogError("mbedtls_pk_parse_key failed (%d)", result);
}
#elif defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_3_0_0
const char *pers = "httpapi_curl";
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
Expand Down Expand Up @@ -453,8 +496,16 @@ static CURLcode ssl_ctx_callback(CURL *curl, void *ssl_ctx, void *userptr)
result = CURLE_SSL_CERTPROBLEM;
}
#elif USE_MBEDTLS
// mbedTLS 4.x requires PSA to be up before ANY X.509 or key parsing.
// Both the client-certificate path and the trusted-CA-only path below
// call mbedtls_x509_crt_parse(), so this has to happen before either.
if (init_psa_crypto() != 0)
{
LogError("unable to initialize PSA crypto");
result = CURLE_SSL_CERTPROBLEM;
}
// set device cert and key
if (
else if (
(httpHandleData->x509certificate != NULL) && (httpHandleData->x509privatekey != NULL) &&
!(
(mbedtls_x509_crt_parse(&httpHandleData->cert, (const unsigned char *)httpHandleData->x509certificate, (int)(strlen(httpHandleData->x509certificate) + 1)) == 0) &&
Expand Down
103 changes: 96 additions & 7 deletions adapters/tlsio_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,27 @@

#define TLSIO_MBEDTLS_VERSION_2_16_0 0x02160000
#define TLSIO_MBEDTLS_VERSION_3_0_0 0x03000000
#define TLSIO_MBEDTLS_VERSION_4_0_0 0x04000000

#include "mbedtls/version.h"
#include "mbedtls/debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/error.h"
#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_4_0_0
// mbedTLS 4.x removed the public entropy and CTR_DRBG modules. Everything that
// needs randomness now goes through the PSA Crypto random generator, which is
// enabled by psa_crypto_init().
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#endif // MBEDTLS_VERSION_NUMBER
#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_3_0_0
#include "mbedtls/certs.h"
#include "mbedtls/entropy_poll.h"
#endif // MBEDTLS_VERSION_NUMBER
#include "mbedtls/pk.h"
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
#include "psa/crypto.h"
#endif // MBEDTLS_VERSION_NUMBER

#include "azure_c_shared_utility/gballoc.h"
#include "azure_c_shared_utility/optimize_size.h"
Expand Down Expand Up @@ -71,8 +80,10 @@ typedef struct TLS_IO_INSTANCE_TAG
size_t socket_io_read_byte_count;
SEND_COMPLETE_INFO send_complete_info;

#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_4_0_0
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
#endif // MBEDTLS_VERSION_NUMBER
mbedtls_ssl_context ssl;
mbedtls_ssl_config config;
mbedtls_x509_crt trusted_certificates_parsed;
Expand All @@ -83,6 +94,9 @@ typedef struct TLS_IO_INSTANCE_TAG
char *hostname;
mbedtls_x509_crt owncert;
mbedtls_pk_context pKey;
// mbedTLS 4.x made mbedtls_pk_get_type() private, so the SDK tracks whether
// pKey holds a successfully parsed private key on its own.
bool pkey_parsed;

char* x509_certificate;
char* x509_private_key;
Expand Down Expand Up @@ -146,8 +160,14 @@ static bool is_fragmented_send_request(TLS_IO_INSTANCE *tls_io_instance, size_t
#else // MBEDTLS_VERSION_NUMBER
size_t max_len = mbedtls_ssl_get_max_frag_len(&tls_io_instance->ssl);
#endif // MBEDTLS_VERSION_NUMBER
#else
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_3_0_0
// MBEDTLS_SSL_MAX_CONTENT_LEN was removed in mbedTLS 3.0 in favor of
// separate incoming/outgoing limits.
size_t max_len = MBEDTLS_SSL_OUT_CONTENT_LEN;
#else
size_t max_len = MBEDTLS_SSL_MAX_CONTENT_LEN;
#endif // MBEDTLS_VERSION_NUMBER
(void)tls_io_instance;
#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
bool result;
Expand Down Expand Up @@ -456,6 +476,7 @@ static int on_io_send(void *context, const unsigned char *buf, size_t sz)
return result;
}

#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_4_0_0
static int tlsio_entropy_poll(void *v, unsigned char *output, size_t len, size_t *olen)
{
(void)v;
Expand All @@ -468,6 +489,7 @@ static int tlsio_entropy_poll(void *v, unsigned char *output, size_t len, size_t
*olen = len;
return result;
}
#endif // MBEDTLS_VERSION_NUMBER

// Un-initialize mbedTLS
static void mbedtls_uninit(TLS_IO_INSTANCE *tls_io_instance)
Expand All @@ -481,8 +503,14 @@ static void mbedtls_uninit(TLS_IO_INSTANCE *tls_io_instance)
mbedtls_x509_crt_free(&tls_io_instance->trusted_certificates_parsed);
mbedtls_x509_crt_free(&tls_io_instance->owncert);
mbedtls_pk_free(&tls_io_instance->pKey);
tls_io_instance->pkey_parsed = false;
#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_4_0_0
mbedtls_ctr_drbg_free(&tls_io_instance->ctr_drbg);
mbedtls_entropy_free(&tls_io_instance->entropy);
#endif // MBEDTLS_VERSION_NUMBER
// Note: psa_crypto_free() is deliberately not called on mbedTLS 4.x.
// The PSA subsystem is global to the process and may still be in use by
// other tlsio instances or by the application itself.

tls_io_instance->tls_status = TLS_STATE_NOT_INITIALIZED;
}
Expand All @@ -492,33 +520,65 @@ static void mbedtls_uninit(TLS_IO_INSTANCE *tls_io_instance)
}
}

static void mbedtls_init(TLS_IO_INSTANCE *tls_io_instance)
static int mbedtls_init(TLS_IO_INSTANCE *tls_io_instance)
{
const char* pers = "azure_iot_client";
int result = 0;
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
psa_status_t psa_status;
#endif // MBEDTLS_VERSION_NUMBER

if (tls_io_instance->tls_status != TLS_STATE_INITIALIZED)
{
if (tls_io_instance->tls_status == TLS_STATE_CLOSING)
{
// The underlying connection has been closed, so here un-initialize first
mbedtls_uninit(tls_io_instance);
}

#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
// mbedTLS 4.x routes every source of randomness (TLS, X.509 and key
// parsing included) through PSA Crypto, so psa_crypto_init() must
// succeed before any other mbedTLS call. It is idempotent. This is done
// first so that nothing has been initialized yet if it fails.
(void)pers;
psa_status = psa_crypto_init();

if (psa_status != PSA_SUCCESS)
{
LogError("psa_crypto_init failed (%d)", (int)psa_status);
result = MU_FAILURE;
}
else
#endif // MBEDTLS_VERSION_NUMBER
{
// mbedTLS initialize...
mbedtls_x509_crt_init(&tls_io_instance->trusted_certificates_parsed);
mbedtls_x509_crt_init(&tls_io_instance->owncert);
mbedtls_pk_init(&tls_io_instance->pKey);
tls_io_instance->pkey_parsed = false;

#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_4_0_0
mbedtls_entropy_init(&tls_io_instance->entropy);
// Add a weak entropy source here,avoid some platform doesn't have strong / hardware entropy
mbedtls_entropy_add_source(&tls_io_instance->entropy, tlsio_entropy_poll, NULL, MBEDTLS_ENTROPY_MAX_GATHER, MBEDTLS_ENTROPY_SOURCE_WEAK);

mbedtls_ctr_drbg_init(&tls_io_instance->ctr_drbg);
mbedtls_ctr_drbg_seed(&tls_io_instance->ctr_drbg, mbedtls_entropy_func, &tls_io_instance->entropy, (const unsigned char *)pers, strlen(pers));
#endif // MBEDTLS_VERSION_NUMBER

mbedtls_ssl_config_init(&tls_io_instance->config);
mbedtls_ssl_config_defaults(&tls_io_instance->config, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
#if !defined(MBEDTLS_VERSION_NUMBER) || MBEDTLS_VERSION_NUMBER < TLSIO_MBEDTLS_VERSION_4_0_0
// mbedtls_ssl_conf_rng() was removed in mbedTLS 4.x.
mbedtls_ssl_conf_rng(&tls_io_instance->config, mbedtls_ctr_drbg_random, &tls_io_instance->ctr_drbg);
#endif // MBEDTLS_VERSION_NUMBER
mbedtls_ssl_conf_authmode(&tls_io_instance->config, MBEDTLS_SSL_VERIFY_REQUIRED);
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
mbedtls_ssl_conf_min_tls_version(&tls_io_instance->config, MBEDTLS_SSL_VERSION_TLS1_2); // v1.2
#else
mbedtls_ssl_conf_min_version(&tls_io_instance->config, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); // v1.2
#endif // MBEDTLS_VERSION_NUMBER

mbedtls_ssl_init(&tls_io_instance->ssl);
mbedtls_ssl_set_bio(&tls_io_instance->ssl, tls_io_instance, on_io_send, on_io_recv, NULL);
Expand All @@ -530,7 +590,10 @@ static void mbedtls_init(TLS_IO_INSTANCE *tls_io_instance)
mbedtls_ssl_setup(&tls_io_instance->ssl, &tls_io_instance->config);

tls_io_instance->tls_status = TLS_STATE_INITIALIZED;
}
}

return result;
}

CONCRETE_IO_HANDLE tlsio_mbedtls_create(void *io_create_parameters)
Expand Down Expand Up @@ -591,9 +654,24 @@ CONCRETE_IO_HANDLE tlsio_mbedtls_create(void *io_create_parameters)
else
{
result->tls_status = TLS_STATE_NOT_INITIALIZED;
mbedtls_init((void*)result);
result->tlsio_state = TLSIO_STATE_NOT_OPEN;
result->invoke_on_send_complete_callback_for_fragments = tls_io_config->invoke_on_send_complete_callback_for_fragments;

// Note: mbedtls_init() only fails before it has initialized
// any mbedTLS context, so there is nothing to unwind with
// mbedtls_uninit() here. Keep that true if it gains new
// failure points.
if (mbedtls_init((void*)result) != 0)
{
LogError("Failure initializing mbedTLS");
xio_destroy(result->socket_io);
free(result->hostname);
free(result);
result = NULL;
}
else
{
result->tlsio_state = TLSIO_STATE_NOT_OPEN;
result->invoke_on_send_complete_callback_for_fragments = tls_io_config->invoke_on_send_complete_callback_for_fragments;
}
}
}
}
Expand Down Expand Up @@ -934,7 +1012,16 @@ static int parse_key(char* key, mbedtls_pk_context* out_parsed_key)
{
int result;

#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_3_0_0
#if defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_4_0_0
// mbedTLS 4.x dropped the f_rng/p_rng arguments again: key parsing uses the
// PSA Crypto RNG, which mbedtls_init() has already brought up.
if ((result = mbedtls_pk_parse_key(out_parsed_key,
(const unsigned char *)key, (int)(strlen(key) + 1),
NULL, 0)) != 0)
{
LogError("mbedtls_pk_parse_key failed (%d)", result);
}
#elif defined(MBEDTLS_VERSION_NUMBER) && MBEDTLS_VERSION_NUMBER >= TLSIO_MBEDTLS_VERSION_3_0_0
const char *pers = "tlsio_mbedtls";
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
Expand Down Expand Up @@ -1023,7 +1110,7 @@ int tlsio_mbedtls_setoption(CONCRETE_IO_HANDLE tls_io, const char *optionName, c
free(temp_cert);
result = MU_FAILURE;
}
else if (mbedtls_pk_get_type(&tls_io_instance->pKey) != MBEDTLS_PK_NONE &&
else if (tls_io_instance->pkey_parsed &&
mbedtls_ssl_conf_own_cert(&tls_io_instance->config, &tls_io_instance->owncert, &tls_io_instance->pKey) != 0)
{
LogError("failure calling mbedtls_ssl_conf_own_cert");
Expand Down Expand Up @@ -1058,12 +1145,14 @@ int tlsio_mbedtls_setoption(CONCRETE_IO_HANDLE tls_io, const char *optionName, c
}
else if (tls_io_instance->owncert.version > 0 && mbedtls_ssl_conf_own_cert(&tls_io_instance->config, &tls_io_instance->owncert, &tls_io_instance->pKey))
{
tls_io_instance->pkey_parsed = true;
LogError("failure calling mbedtls_ssl_conf_own_cert");
free(temp_key);
result = MU_FAILURE;
}
else
{
tls_io_instance->pkey_parsed = true;
if (tls_io_instance->x509_private_key != NULL)
{
// Free the memory if it has been previously allocated
Expand Down
53 changes: 53 additions & 0 deletions build/.vsts-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,59 @@ jobs:
env:
LD_LIBRARY_PATH: /usr/local/lib

- job: linux_mbed_4x
# mbedTLS 4.x moved all cryptography to TF-PSA-Crypto and removed the public
# entropy/CTR_DRBG modules, mbedtls_ssl_conf_rng() and
# mbedtls_ssl_conf_min_version(). adapters/tlsio_mbedtls.c handles that via
# MBEDTLS_VERSION_NUMBER guards; this job keeps that path compiling.
timeoutInMinutes: 60
pool:
vmImage: 'ubuntu-24.04'
displayName: 'mbedTLS 4.x'
steps:
- checkout: self
submodules: true
- script: |
set -e
sudo apt-get update
# Purge the distro mbedTLS 3.x so the 4.x install under /usr/local is the
# only one CMake can find.
sudo apt-get purge -y 'libmbedtls*' 'libmbedcrypto*' 'libmbedx509*' || true
sudo apt-get install -y \
clang \
cmake \
build-essential \
pkg-config \
curl \
libcurl4-openssl-dev \
uuid-dev \
python3-pip
displayName: 'Host setup'
- script: |
set -e
git clone --depth 1 -b mbedtls-4.2.0 https://github.com/Mbed-TLS/mbedtls /tmp/mbedtls-4x
cd /tmp/mbedtls-4x
git submodule update --init --recursive --depth 1
python3 -m pip install --break-system-packages -r scripts/basic.requirements.txt
mkdir build && cd build
cmake -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DENABLE_TESTING=Off -DENABLE_PROGRAMS=Off -DCMAKE_INSTALL_PREFIX=/usr/local ..
make -j$(nproc)
sudo make install
sudo ldconfig
displayName: 'Install mbedTLS 4.2'
- script: |
chmod +x jenkins/linux_mbed_clang_buildonly.sh
./jenkins/linux_mbed_clang_buildonly.sh
displayName: 'Clang Build'
env:
LD_LIBRARY_PATH: /usr/local/lib
- script: |
sudo chmod 755 jenkins/linux_mbed.sh
sudo -E ./jenkins/linux_mbed.sh
displayName: 'Build'
env:
LD_LIBRARY_PATH: /usr/local/lib

- job: wolfssl
timeoutInMinutes: 60
pool:
Expand Down
Loading