From c84a0c8aa2b53d594a789a26195278510ff0100a Mon Sep 17 00:00:00 2001 From: Peter M Date: Fri, 30 Jan 2026 13:18:46 +0100 Subject: [PATCH 1/9] otp_crypto: add mbedtls 4 support Port otp_crypto to the PSA Crypto API used by mbedtls 4.x. - Replace deprecated low-level mbedtls APIs with PSA equivalents for hash, HMAC, cipher, and AEAD operations - Guard legacy mbedtls 2/3 code paths with version checks - Update CMake to detect mbedtls 4 and set HAVE_PSA_CRYPTO - Keep ESP32 JIT config outside mbedtls version guards Signed-off-by: Peter M --- .github/workflows/build-and-test-macos.yaml | 2 +- .github/workflows/build-libraries.yaml | 24 +- src/libAtomVM/otp_crypto.c | 356 +++++++++++++++++- src/libAtomVM/otp_ssl.c | 40 +- src/libAtomVM/sys_mbedtls.h | 11 + .../emscripten/src/lib/emscripten_sys.h | 13 +- src/platforms/emscripten/src/lib/sys.c | 11 + .../components/avm_sys/include/esp32_sys.h | 11 + .../esp32/components/avm_sys/platform_nifs.c | 6 + src/platforms/esp32/components/avm_sys/sys.c | 22 +- src/platforms/generic_unix/lib/CMakeLists.txt | 2 +- src/platforms/generic_unix/lib/sys.c | 22 +- src/platforms/rp2/src/lib/rp2_sys.h | 5 + src/platforms/rp2/src/lib/sys.c | 20 +- src/platforms/stm32/src/lib/sys.c | 11 + 15 files changed, 519 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build-and-test-macos.yaml b/.github/workflows/build-and-test-macos.yaml index 744dfbe4f0..6a9d0f644b 100644 --- a/.github/workflows/build-and-test-macos.yaml +++ b/.github/workflows/build-and-test-macos.yaml @@ -40,7 +40,7 @@ jobs: matrix: os: ["macos-14", "macos-15", "macos-15-intel", "macos-26"] otp: ["26", "27", "28"] - mbedtls: ["mbedtls@3"] + mbedtls: ["mbedtls@3", "mbedtls@4"] cmake_opts_other: [""] include: diff --git a/.github/workflows/build-libraries.yaml b/.github/workflows/build-libraries.yaml index bb8894f83b..20eb26189d 100644 --- a/.github/workflows/build-libraries.yaml +++ b/.github/workflows/build-libraries.yaml @@ -19,6 +19,8 @@ jobs: runs-on: "ubuntu-22.04" strategy: fail-fast: false + matrix: + mbedtls: ["default", "mbedtls@4"] steps: - name: "Checkout repo" @@ -36,10 +38,26 @@ jobs: - name: "Install deps" run: | - sudo apt install -y build-essential cmake gperf zlib1g-dev libmbedtls-dev + sudo apt install -y build-essential cmake gperf zlib1g-dev + if [[ "${{ matrix.mbedtls }}" == "default" ]]; then + sudo apt install -y libmbedtls-dev + fi # Get a more recent valgrind sudo snap install valgrind --classic + - name: "Install specific MbedTLS version" + if: matrix.mbedtls == 'mbedtls@4' + run: | + git clone --depth 1 --branch mbedtls-4.0.0 --recurse-submodules https://github.com/Mbed-TLS/mbedtls + cd mbedtls + mkdir build + cd build + cmake -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=On -DCMAKE_INSTALL_PREFIX=/usr/local .. + make -j$(nproc) + sudo make install + sudo ldconfig + echo "MBEDTLS_ROOT_DIR=/usr/local" >> $GITHUB_ENV + # Builder info - name: "System info" run: | @@ -64,7 +82,7 @@ jobs: - name: "Build: run cmake" working-directory: build run: | - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. + cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ${MBEDTLS_ROOT_DIR:+-DMBEDTLS_ROOT_DIR=$MBEDTLS_ROOT_DIR} .. - name: "Build: run make" working-directory: build @@ -124,7 +142,7 @@ jobs: - name: Release uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls == 'default' with: draft: true fail_on_unmatched_files: true diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index 224c93a432..debe72f0e3 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -32,6 +32,8 @@ #include #include +#include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include #include @@ -40,6 +42,7 @@ #include #include #include +#endif #if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) #include #else @@ -51,6 +54,8 @@ #ifdef HAVE_PSA_CRYPTO #include +#endif +#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 #include #endif @@ -69,7 +74,6 @@ #include #define AVM_HAVE_MBEDTLS_CT_MEMCMP 1 #endif - // #define ENABLE_TRACE #include "trace.h" @@ -101,6 +105,7 @@ enum crypto_algorithm CryptoSha512 }; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 static const AtomStringIntPair crypto_algorithm_table[] = { { ATOM_STR("\x3", "md5"), CryptoMd5 }, { ATOM_STR("\x3", "sha"), CryptoSha1 }, @@ -110,6 +115,7 @@ static const AtomStringIntPair crypto_algorithm_table[] = { { ATOM_STR("\x6", "sha512"), CryptoSha512 }, SELECT_INT_DEFAULT(CryptoInvalidAlgorithm) }; +#endif #define DEFINE_HASH_FOLD(ALGORITHM, SUFFIX) \ static InteropFunctionResult ALGORITHM##_hash_fold_fun(term t, void *accum) \ @@ -225,6 +231,7 @@ static const AtomStringIntPair crypto_algorithm_table[] = { return true; \ } +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #if MBEDTLS_VERSION_NUMBER >= 0x03000000 // 3.x API: functions return an int that represents errors @@ -270,6 +277,52 @@ DEFINE_HASH_FOLD_NORET(sha512, ) DEFINE_DO_HASH_NORET_IS_OTHER(sha512, , true) DEFINE_DO_HASH_NORET_IS_OTHER(sha512, , false) +#endif +#endif + +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +static psa_algorithm_t atom_to_psa_hash_alg(term type, GlobalContext *global) +{ + if (type == globalcontext_make_atom(global, ATOM_STR("\x3", "md5"))) { + return PSA_ALG_MD5; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\x3", "sha"))) { + return PSA_ALG_SHA_1; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\x6", "sha224"))) { + return PSA_ALG_SHA_224; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\x6", "sha256"))) { + return PSA_ALG_SHA_256; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\x6", "sha384"))) { + return PSA_ALG_SHA_384; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\x6", "sha512"))) { + return PSA_ALG_SHA_512; + } + return PSA_ALG_NONE; +} + +static InteropFunctionResult psa_hash_fold_fun(term t, void *accum) +{ + psa_hash_operation_t *operation = (psa_hash_operation_t *) accum; + if (term_is_integer(t)) { + avm_int64_t tmp = term_maybe_unbox_int64(t); + if (tmp < 0 || tmp > 255) { + return InteropBadArg; + } + uint8_t val = (uint8_t) tmp; + if (UNLIKELY(psa_hash_update(operation, &val, 1) != PSA_SUCCESS)) { + return InteropBadArg; + } + } else /* term_is_binary(t) */ { + if (UNLIKELY(psa_hash_update(operation, (uint8_t *) term_binary_data(t), term_binary_size(t)) != PSA_SUCCESS)) { + return InteropBadArg; + } + } + return InteropOk; +} #endif static term nif_crypto_hash(Context *ctx, int argc, term argv[]) @@ -282,6 +335,33 @@ static term nif_crypto_hash(Context *ctx, int argc, term argv[]) unsigned char digest[MAX_MD_SIZE]; size_t digest_len = 0; +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_algorithm_t alg = atom_to_psa_hash_alg(type, ctx->global); + if (alg == PSA_ALG_NONE) { + TRACE("crypto:hash unknown algorithm\n"); + RAISE_ERROR(BADARG_ATOM); + } + digest_len = PSA_HASH_LENGTH(alg); + + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + psa_status_t status = psa_hash_setup(&operation, alg); + if (UNLIKELY(status != PSA_SUCCESS)) { + TRACE("crypto:hash psa_hash_setup failed with status %d for alg 0x%08lx\n", (int) status, (unsigned long) alg); + RAISE_ERROR(BADARG_ATOM); + } + + InteropFunctionResult result = interop_chardata_fold(data, psa_hash_fold_fun, NULL, (void *) &operation); + if (UNLIKELY(result != InteropOk)) { + psa_hash_abort(&operation); + RAISE_ERROR(BADARG_ATOM); + } + + status = psa_hash_finish(&operation, digest, sizeof(digest), &digest_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + psa_hash_abort(&operation); + RAISE_ERROR(BADARG_ATOM); + } +#else enum crypto_algorithm algo = interop_atom_term_select_int(crypto_algorithm_table, type, ctx->global); switch (algo) { case CryptoMd5: { @@ -329,6 +409,7 @@ static term nif_crypto_hash(Context *ctx, int argc, term argv[]) default: RAISE_ERROR(BADARG_ATOM); } +#endif if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(digest_len)) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -336,6 +417,7 @@ static term nif_crypto_hash(Context *ctx, int argc, term argv[]) return term_from_literal_binary(digest, digest_len, &ctx->heap, ctx->global); } +#if MBEDTLS_VERSION_NUMBER < 0x04000000 static const AtomStringIntPair cipher_table[] = { { ATOM_STR("\xB", "aes_128_ecb"), MBEDTLS_CIPHER_AES_128_ECB }, { ATOM_STR("\xB", "aes_192_ecb"), MBEDTLS_CIPHER_AES_192_ECB }, @@ -357,6 +439,7 @@ static const AtomStringIntPair padding_table[] = { { ATOM_STR("\xC", "pkcs_padding"), MBEDTLS_PADDING_PKCS7 }, SELECT_INT_DEFAULT(-1) }; +#endif static void secure_free(void *buf, size_t len) { @@ -407,6 +490,72 @@ static term handle_iodata(term iodata, const void **data, size_t *len, void **al } } +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +static psa_algorithm_t atom_to_psa_cipher_alg(term type, GlobalContext *global, psa_key_type_t *key_type, size_t *key_bits) +{ + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_128_ecb"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 128; + return PSA_ALG_ECB_NO_PADDING; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_192_ecb"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 192; + return PSA_ALG_ECB_NO_PADDING; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_256_ecb"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 256; + return PSA_ALG_ECB_NO_PADDING; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_128_cbc"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 128; + return PSA_ALG_CBC_NO_PADDING; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_192_cbc"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 192; + return PSA_ALG_CBC_NO_PADDING; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_256_cbc"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 256; + return PSA_ALG_CBC_NO_PADDING; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xE", "aes_128_cfb128"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 128; + return PSA_ALG_CFB; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xE", "aes_192_cfb128"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 192; + return PSA_ALG_CFB; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xE", "aes_256_cfb128"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 256; + return PSA_ALG_CFB; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_128_ctr"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 128; + return PSA_ALG_CTR; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_192_ctr"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 192; + return PSA_ALG_CTR; + } + if (type == globalcontext_make_atom(global, ATOM_STR("\xB", "aes_256_ctr"))) { + *key_type = PSA_KEY_TYPE_AES; + *key_bits = 256; + return PSA_ALG_CTR; + } + return PSA_ALG_NONE; +} +#else static bool bool_to_mbedtls_operation(term encrypt_flag, mbedtls_operation_t *operation) { switch (encrypt_flag) { @@ -420,6 +569,7 @@ static bool bool_to_mbedtls_operation(term encrypt_flag, mbedtls_operation_t *op return false; } } +#endif static term make_crypto_error_tag( const char *file, int line, const char *message, term tag, Context *ctx) @@ -470,11 +620,21 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) } term cipher_term = argv[0]; + +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_key_type_t key_type; + size_t key_bits; + psa_algorithm_t alg = atom_to_psa_cipher_alg(cipher_term, ctx->global, &key_type, &key_bits); + if (UNLIKELY(alg == PSA_ALG_NONE)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown cipher", ctx)); + } +#else mbedtls_cipher_type_t cipher = interop_atom_term_select_int(cipher_table, cipher_term, ctx->global); if (UNLIKELY(cipher == MBEDTLS_CIPHER_NONE)) { RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown cipher", ctx)); } +#endif // from this point onward use `goto raise_error` in order to raise and free all buffers term error_atom = UNDEFINED_ATOM; @@ -508,6 +668,169 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) goto raise_error; } +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + bool encrypt = true; + bool padding_pkcs7 = false; + psa_key_id_t key_id = 0; + void *temp_buf = NULL; + psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT; + + if (term_is_list(flag_or_options)) { + term encrypt_flag = interop_kv_get_value_default( + flag_or_options, ATOM_STR("\x7", "encrypt"), UNDEFINED_ATOM, ctx->global); + if (encrypt_flag == FALSE_ATOM) { + encrypt = false; + } else if (encrypt_flag != TRUE_ATOM && encrypt_flag != UNDEFINED_ATOM) { + error_atom = BADARG_ATOM; + goto raise_error; + } + + term padding_term = interop_kv_get_value_default( + flag_or_options, ATOM_STR("\x7", "padding"), UNDEFINED_ATOM, ctx->global); + + if (padding_term != UNDEFINED_ATOM) { + if (padding_term == globalcontext_make_atom(ctx->global, ATOM_STR("\xC", "pkcs_padding"))) { + padding_pkcs7 = true; + } else if (padding_term != globalcontext_make_atom(ctx->global, ATOM_STR("\x4", "none"))) { + error_atom = BADARG_ATOM; + goto raise_error; + } + } + + } else { + if (flag_or_options == FALSE_ATOM) { + encrypt = false; + } else if (flag_or_options != TRUE_ATOM) { + error_atom = make_crypto_error( + __FILE__, __LINE__, "Options are not a boolean or a proper list", ctx); + goto raise_error; + } + } + + if (padding_pkcs7) { + if (alg == PSA_ALG_CBC_NO_PADDING) { + alg = PSA_ALG_CBC_PKCS7; + } else if (alg == PSA_ALG_ECB_NO_PADDING) { + // PSA does not support PKCS7 padding with ECB mode + error_atom = BADARG_ATOM; + goto raise_error; + } + } + + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_usage_flags(&attributes, encrypt ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, alg); + psa_set_key_type(&attributes, key_type); + psa_set_key_bits(&attributes, key_bits); + + psa_status_t status = psa_import_key(&attributes, key_data, key_len, &key_id); + if (UNLIKELY(status != PSA_SUCCESS)) { + char err_msg[48]; + snprintf(err_msg, sizeof(err_msg), "key import err %d", (int) status); + error_atom = make_crypto_error(__FILE__, __LINE__, err_msg, ctx); + goto psa_error; + } + + size_t output_size = PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, data_size); + if (!encrypt) { + output_size = PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, data_size); + } + temp_buf = malloc(output_size); + if (IS_NULL_PTR(temp_buf)) { + error_atom = OUT_OF_MEMORY_ATOM; + goto psa_error; + } + + size_t output_len; + if (encrypt) { + status = psa_cipher_encrypt_setup(&operation, key_id, alg); + } else { + status = psa_cipher_decrypt_setup(&operation, key_id, alg); + } + if (UNLIKELY(status != PSA_SUCCESS)) { + char err_msg[48]; + snprintf(err_msg, sizeof(err_msg), "cipher setup err %d", (int) status); + error_atom = make_crypto_error(__FILE__, __LINE__, err_msg, ctx); + goto psa_error; + } + + // PSA rejects IVs for ECB; ignore IV to preserve legacy behavior. + if (iv_len > 0 && alg != PSA_ALG_ECB_NO_PADDING) { + status = psa_cipher_set_iv(&operation, iv_data, iv_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + char err_msg[24]; + snprintf(err_msg, sizeof(err_msg), "IV err %d", (int) status); + error_atom = make_crypto_error(__FILE__, __LINE__, err_msg, ctx); + goto psa_error; + } + } + + // For CBC/ECB with no padding, PSA requires block-aligned input. + // The legacy mbedtls behavior was to process only complete blocks, + // so we truncate the input to the nearest block boundary for these modes. + size_t block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type); + size_t process_size = data_size; + if (alg == PSA_ALG_CBC_NO_PADDING || alg == PSA_ALG_ECB_NO_PADDING) { + process_size = (data_size / block_size) * block_size; + if (process_size == 0) { + // No complete blocks to process + psa_cipher_abort(&operation); + psa_destroy_key(key_id); + free(temp_buf); + secure_free(allocated_key_data, key_len); + secure_free(allocated_iv_data, iv_len); + free(allocated_data_data); + // Return empty binary + if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(0)) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + return term_from_literal_binary("", 0, &ctx->heap, ctx->global); + } + } + + size_t update_len = 0; + status = psa_cipher_update(&operation, data_data, process_size, temp_buf, output_size, &update_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + char err_msg[24]; + snprintf(err_msg, sizeof(err_msg), "update err %d", (int) status); + error_atom = make_crypto_error(__FILE__, __LINE__, err_msg, ctx); + goto psa_error; + } + + size_t finish_len = 0; + status = psa_cipher_finish(&operation, (uint8_t *) temp_buf + update_len, output_size - update_len, &finish_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + char err_msg[24]; + snprintf(err_msg, sizeof(err_msg), "finish err %d", (int) status); + error_atom = make_crypto_error(__FILE__, __LINE__, err_msg, ctx); + goto psa_error; + } + output_len = update_len + finish_len; + + psa_destroy_key(key_id); + + secure_free(allocated_key_data, key_len); + secure_free(allocated_iv_data, iv_len); + free(allocated_data_data); + + int ensure_size = term_binary_heap_size(output_len); + if (UNLIKELY(memory_ensure_free(ctx, ensure_size) != MEMORY_GC_OK)) { + free(temp_buf); + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + + term out = term_from_literal_binary(temp_buf, output_len, &ctx->heap, ctx->global); + free(temp_buf); + return out; + +psa_error: + psa_cipher_abort(&operation); + if (key_id != 0) { + psa_destroy_key(key_id); + } + free(temp_buf); + goto raise_error; +#else mbedtls_operation_t operation; mbedtls_cipher_padding_t padding = MBEDTLS_PADDING_NONE; bool padding_has_been_set = false; @@ -625,6 +948,15 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) char err_msg[24]; snprintf(err_msg, sizeof(err_msg), "Error %x", -result); RAISE_ERROR(make_crypto_error(__FILE__, source_line, err_msg, ctx)); +#endif + +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +raise_error: + secure_free(allocated_key_data, key_len); + secure_free(allocated_iv_data, iv_len); + free(allocated_data_data); + RAISE_ERROR(error_atom); +#endif } #ifdef HAVE_PSA_CRYPTO @@ -1369,7 +1701,11 @@ static term nif_crypto_sign(Context *ctx, int argc, term argv[]) size_t sig_raw_size = PSA_ECDSA_SIGNATURE_SIZE(psa_key_bits); uint8_t *sig_raw = NULL; +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + size_t sig_der_size = MBEDTLS_ECDSA_DER_MAX_SIG_LEN(psa_key_bits); +#else size_t sig_der_size = MBEDTLS_ECDSA_MAX_SIG_LEN(psa_key_bits); +#endif void *sig_der = NULL; void *maybe_allocated_data = NULL; @@ -3113,12 +3449,6 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) term digest_type_term = argv[0]; // argv[1] is password, argv[2] is salt, argv[3] is iterations, argv[4] is key_len - mbedtls_md_type_t md_type - = interop_atom_term_select_int(md_hash_algorithm_table, digest_type_term, glb); - if (UNLIKELY(md_type == MBEDTLS_MD_NONE)) { - RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown digest type", ctx)); - } - bool success = false; term result = ERROR_ATOM; @@ -3231,6 +3561,17 @@ term nif_crypto_strong_rand_bytes(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + term out_bin = term_create_uninitialized_binary(out_len, &ctx->heap, ctx->global); + unsigned char *out = (unsigned char *) term_binary_data(out_bin); + + psa_status_t status = psa_generate_random(out, out_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Failed random", ctx)); + } + + return out_bin; +#else mbedtls_ctr_drbg_context *rnd_ctx = sys_mbedtls_get_ctr_drbg_context_lock(ctx->global); if (IS_NULL_PTR(rnd_ctx)) { RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Failed CTR_DRBG init", ctx)); @@ -3246,6 +3587,7 @@ term nif_crypto_strong_rand_bytes(Context *ctx, int argc, term argv[]) } return out_bin; +#endif } static const char *get_mbedtls_version_string_full(char *buf, size_t buf_size) diff --git a/src/libAtomVM/otp_ssl.c b/src/libAtomVM/otp_ssl.c index b74d2cb8af..56124bf549 100644 --- a/src/libAtomVM/otp_ssl.c +++ b/src/libAtomVM/otp_ssl.c @@ -35,11 +35,14 @@ #include +#include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include +#endif #include -#if defined(HAVE_PSA_CRYPTO) +#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 #include #endif @@ -75,12 +78,20 @@ static void mbedtls_debug_cb(void *ctx, int level, const char *filename, int lin struct EntropyContextResource { +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_entropy_context context; +#else + char dummy; +#endif }; struct CtrDrbgResource { +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_ctr_drbg_context context; +#else + char dummy; +#endif }; struct SSLContextResource @@ -98,7 +109,11 @@ static void entropycontext_dtor(ErlNifEnv *caller_env, void *obj) UNUSED(caller_env); struct EntropyContextResource *rsrc_obj = (struct EntropyContextResource *) obj; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_entropy_free(&rsrc_obj->context); +#else + UNUSED(rsrc_obj); +#endif } static void ctrdrbg_dtor(ErlNifEnv *caller_env, void *obj) @@ -107,6 +122,7 @@ static void ctrdrbg_dtor(ErlNifEnv *caller_env, void *obj) UNUSED(caller_env); struct CtrDrbgResource *rsrc_obj = (struct CtrDrbgResource *) obj; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_entropy_context *entropy_context = rsrc_obj->context.MBEDTLS_PRIVATE(p_entropy); // Release the drbg first mbedtls_ctr_drbg_free(&rsrc_obj->context); @@ -116,6 +132,9 @@ static void ctrdrbg_dtor(ErlNifEnv *caller_env, void *obj) struct RefcBinary *entropy_refc = refc_binary_from_data(entropy_obj); refc_binary_decrement_refcount(entropy_refc, caller_env->global); } +#else + UNUSED(rsrc_obj); +#endif } static void sslcontext_dtor(ErlNifEnv *caller_env, void *obj) @@ -141,15 +160,19 @@ static void sslconfig_dtor(ErlNifEnv *caller_env, void *obj) UNUSED(caller_env); struct SSLConfigResource *rsrc_obj = (struct SSLConfigResource *) obj; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 const mbedtls_ctr_drbg_context *ctr_drbg_context = rsrc_obj->config.MBEDTLS_PRIVATE(p_rng); +#endif mbedtls_ssl_config_free(&rsrc_obj->config); +#if MBEDTLS_VERSION_NUMBER < 0x04000000 // Eventually release the ctrdrbg if (ctr_drbg_context) { struct CtrDrbgResource *rng_obj = CONTAINER_OF(ctr_drbg_context, struct CtrDrbgResource, context); struct RefcBinary *config_refc = refc_binary_from_data(rng_obj); refc_binary_decrement_refcount(config_refc, caller_env->global); } +#endif } static const ErlNifResourceTypeInit EntropyContextResourceTypeInit = { @@ -238,7 +261,9 @@ static term nif_ssl_entropy_init(Context *ctx, int argc, term argv[]) term obj = term_from_resource(rsrc_obj, &ctx->heap); enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_entropy_init(&rsrc_obj->context); +#endif return obj; } @@ -261,7 +286,9 @@ static term nif_ssl_ctr_drbg_init(Context *ctx, int argc, term argv[]) term obj = term_from_resource(rsrc_obj, &ctx->heap); enif_release_resource(rsrc_obj); // decrement refcount after enif_alloc_resource +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_ctr_drbg_init(&rsrc_obj->context); +#endif return obj; } @@ -277,11 +304,14 @@ static term nif_ssl_ctr_drbg_seed(Context *ctx, int argc, term argv[]) if (UNLIKELY(!enif_get_resource(erl_nif_env_from_context(ctx), argv[0], ctrdrbg_resource_type, &rsrc_obj_ptr))) { RAISE_ERROR(BADARG_ATOM); } +#if MBEDTLS_VERSION_NUMBER < 0x04000000 struct CtrDrbgResource *ctrdrbg_obj = (struct CtrDrbgResource *) rsrc_obj_ptr; +#endif if (UNLIKELY(!enif_get_resource(erl_nif_env_from_context(ctx), argv[1], entropycontext_resource_type, &rsrc_obj_ptr))) { RAISE_ERROR(BADARG_ATOM); } +#if MBEDTLS_VERSION_NUMBER < 0x04000000 struct EntropyContextResource *entropy_obj = (struct EntropyContextResource *) rsrc_obj_ptr; int err = mbedtls_ctr_drbg_seed(&ctrdrbg_obj->context, mbedtls_entropy_func, &entropy_obj->context, (const unsigned char *) term_binary_data(argv[2]), term_binary_size(argv[2])); @@ -291,6 +321,7 @@ static term nif_ssl_ctr_drbg_seed(Context *ctx, int argc, term argv[]) struct RefcBinary *entropy_refc = refc_binary_from_data(entropy_obj); refc_binary_increment_refcount(entropy_refc); +#endif return OK_ATOM; } @@ -315,7 +346,7 @@ static term nif_ssl_init(Context *ctx, int argc, term argv[]) mbedtls_ssl_init(&rsrc_obj->context); -#if defined(HAVE_PSA_CRYPTO) +#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 psa_status_t status = psa_crypto_init(); if (UNLIKELY(status != PSA_SUCCESS)) { AVM_LOGW(TAG, "Failed to initialize PSA %s:%i.\n", __FILE__, __LINE__); @@ -484,10 +515,15 @@ static term nif_ssl_conf_rng(Context *ctx, int argc, term argv[]) } struct CtrDrbgResource *ctr_drbg_obj = (struct CtrDrbgResource *) rsrc_obj_ptr; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 struct RefcBinary *ctr_drbg_refc = refc_binary_from_data(ctr_drbg_obj); refc_binary_increment_refcount(ctr_drbg_refc); mbedtls_ssl_conf_rng(&conf_obj->config, mbedtls_ctr_drbg_random, &ctr_drbg_obj->context); +#else + UNUSED(conf_obj); + UNUSED(ctr_drbg_obj); +#endif return OK_ATOM; } diff --git a/src/libAtomVM/sys_mbedtls.h b/src/libAtomVM/sys_mbedtls.h index 51eb59f64a..7d51ea1107 100644 --- a/src/libAtomVM/sys_mbedtls.h +++ b/src/libAtomVM/sys_mbedtls.h @@ -21,8 +21,15 @@ #ifndef _SYS_MBEDTLS_H_ #define _SYS_MBEDTLS_H_ +// Include version.h to get MBEDTLS_VERSION_NUMBER (available in all versions) +#include + +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include +#else #include #include +#endif #ifdef __cplusplus extern "C" { @@ -36,6 +43,7 @@ extern "C" { // On non-SMP builds, we don't need any lock because all calls we make to // mbedtls_ctr_drbg* functions are done from the scheduler thread itself. +#if MBEDTLS_VERSION_NUMBER < 0x04000000 /** * @brief get and acquire lock on mbedtls_entropy_context. * @details this function must be called from a scheduler thread (nif, @@ -60,7 +68,9 @@ void sys_mbedtls_entropy_context_unlock(GlobalContext *global); * the entropy mutex to call `mbedtls_entropy_func`. */ int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size); +#endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 /** * @brief get and acquire lock on mbedtls_ctr_drbg_context. * @details this function must be called from a scheduler thread (nif, @@ -78,6 +88,7 @@ mbedtls_ctr_drbg_context *sys_mbedtls_get_ctr_drbg_context_lock(GlobalContext *g * @param global the global context */ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global); +#endif #ifdef __cplusplus } diff --git a/src/platforms/emscripten/src/lib/emscripten_sys.h b/src/platforms/emscripten/src/lib/emscripten_sys.h index 1889b2ab97..f2978e4da2 100644 --- a/src/platforms/emscripten/src/lib/emscripten_sys.h +++ b/src/platforms/emscripten/src/lib/emscripten_sys.h @@ -33,17 +33,14 @@ #include #include +#include +#include "sys_mbedtls.h" + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include - -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include #endif -#include "sys_mbedtls.h" - struct PromiseResource { em_promise_t promise; @@ -117,6 +114,7 @@ struct EmscriptenPlatformData ErlNifResourceType *htmlevent_user_data_resource_type; ErlNifResourceType *websocket_resource_type; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif @@ -128,6 +126,7 @@ struct EmscriptenPlatformData #endif mbedtls_ctr_drbg_context random_ctx; bool random_is_initialized; +#endif }; void sys_enqueue_emscripten_cast_message(GlobalContext *glb, const char *target, const char *message); diff --git a/src/platforms/emscripten/src/lib/sys.c b/src/platforms/emscripten/src/lib/sys.c index ab8c0404b7..41fbc00bba 100644 --- a/src/platforms/emscripten/src/lib/sys.c +++ b/src/platforms/emscripten/src/lib/sys.c @@ -176,6 +176,14 @@ void sys_init_platform(GlobalContext *glb) AVM_ABORT(); } +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#endif + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP platform->entropy_mutex = smp_mutex_create(); if (IS_NULL_PTR(platform->entropy_mutex)) { @@ -188,6 +196,7 @@ void sys_init_platform(GlobalContext *glb) #endif platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#endif glb->platform_data = platform; } @@ -197,12 +206,14 @@ void sys_free_platform(GlobalContext *glb) struct EmscriptenPlatformData *platform = glb->platform_data; pthread_cond_destroy(&platform->poll_cond); pthread_mutex_destroy(&platform->poll_mutex); +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } if (platform->entropy_is_initialized) { mbedtls_entropy_free(&platform->entropy_ctx); } +#endif free(platform); } diff --git a/src/platforms/esp32/components/avm_sys/include/esp32_sys.h b/src/platforms/esp32/components/avm_sys/include/esp32_sys.h index e38368d3db..becb45e1c9 100644 --- a/src/platforms/esp32/components/avm_sys/include/esp32_sys.h +++ b/src/platforms/esp32/components/avm_sys/include/esp32_sys.h @@ -30,8 +30,15 @@ #include #endif +// Include version.h to get MBEDTLS_VERSION_NUMBER (available in all versions) +#include +#if defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include +#endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include +#endif #include #include @@ -72,13 +79,17 @@ struct ESP32PlatformData #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_entropy_context entropy_ctx; +#endif bool entropy_is_initialized; #ifndef AVM_NO_SMP Mutex *random_mutex; #endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_ctr_drbg_context random_ctx; +#endif bool random_is_initialized; #ifdef CONFIG_AVM_ENABLE_STORAGE_NIFS diff --git a/src/platforms/esp32/components/avm_sys/platform_nifs.c b/src/platforms/esp32/components/avm_sys/platform_nifs.c index 3db18b6c53..8573a6efe7 100644 --- a/src/platforms/esp32/components/avm_sys/platform_nifs.c +++ b/src/platforms/esp32/components/avm_sys/platform_nifs.c @@ -42,11 +42,17 @@ #include #include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include #include #include #include +#endif +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include +#endif + #include #include diff --git a/src/platforms/esp32/components/avm_sys/sys.c b/src/platforms/esp32/components/avm_sys/sys.c index bd2eab6841..8bd4acdd2f 100644 --- a/src/platforms/esp32/components/avm_sys/sys.c +++ b/src/platforms/esp32/components/avm_sys/sys.c @@ -29,6 +29,8 @@ #include "otp_socket.h" #include "scheduler.h" #include "utils.h" +#include +#include // #define ENABLE_TRACE #include "trace.h" @@ -57,11 +59,7 @@ #include "soc/soc_caps.h" #endif -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include -#endif +#include // Platform uses listeners #include "listeners.h" @@ -285,6 +283,13 @@ void sys_init_platform(GlobalContext *glb) platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#if defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#endif + ErlNifResourceFlags flags; ErlNifEnv env; erl_nif_env_partial_init_from_globalcontext(&env, glb); @@ -310,7 +315,7 @@ void sys_free_platform(GlobalContext *glb) AVM_ABORT(); } } - +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } @@ -318,6 +323,7 @@ void sys_free_platform(GlobalContext *glb) if (platform->entropy_is_initialized) { mbedtls_entropy_free(&platform->entropy_ctx); } +#endif #ifndef AVM_NO_SMP smp_mutex_destroy(platform->entropy_mutex); @@ -346,7 +352,7 @@ const void *esp32_sys_mmap_partition(const char *partition_name, spi_flash_mmap_ ESP_LOGE(TAG, "Failed to map BEAM partition for %s", partition_name); return NULL; } - ESP_LOGI(TAG, "Loaded BEAM partition %s at address 0x%"PRIx32" (size=%"PRIu32" bytes)", + ESP_LOGI(TAG, "Loaded BEAM partition %s at address 0x%" PRIx32 " (size=%" PRIu32 " bytes)", partition_name, partition->address, partition->size); return mapped_memory; @@ -773,6 +779,7 @@ term esp_err_to_term(GlobalContext *glb, esp_err_t status) } } +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #if !defined(MBEDTLS_THREADING_C) && !defined(AVM_NO_SMP) @@ -858,6 +865,7 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) UNUSED(global); #endif } +#endif #ifndef AVM_NO_JIT #include diff --git a/src/platforms/generic_unix/lib/CMakeLists.txt b/src/platforms/generic_unix/lib/CMakeLists.txt index e5734eaeef..8cc0d18114 100644 --- a/src/platforms/generic_unix/lib/CMakeLists.txt +++ b/src/platforms/generic_unix/lib/CMakeLists.txt @@ -87,7 +87,7 @@ if (MbedTLS_FOUND) set(CMAKE_REQUIRED_INCLUDES "${MBEDTLS_ROOT_DIR}/include") endif() include(CheckCSourceCompiles) - set(CMAKE_REQUIRED_LIBRARIES MbedTLS::mbedcrypto) + set(CMAKE_REQUIRED_LIBRARIES MbedTLS::mbedtls) check_c_source_compiles(" #include #ifndef MBEDTLS_PSA_CRYPTO_C diff --git a/src/platforms/generic_unix/lib/sys.c b/src/platforms/generic_unix/lib/sys.c index d88ff54f18..ba7646e4c8 100644 --- a/src/platforms/generic_unix/lib/sys.c +++ b/src/platforms/generic_unix/lib/sys.c @@ -32,13 +32,10 @@ #include "utils.h" #if ATOMVM_HAS_MBEDTLS +#include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include - -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include #endif #include "otp_ssl.h" @@ -114,6 +111,7 @@ struct GenericUnixPlatformData #endif #ifdef ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif @@ -126,6 +124,7 @@ struct GenericUnixPlatformData mbedtls_ctr_drbg_context random_ctx; bool random_is_initialized; #endif +#endif }; static void mapped_file_avm_pack_destructor(struct AVMPackData *obj, GlobalContext *global); @@ -579,6 +578,12 @@ void sys_init_platform(GlobalContext *global) otp_net_init(global); otp_socket_init(global); #if ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#else #ifndef AVM_NO_SMP platform->entropy_mutex = smp_mutex_create(); if (IS_NULL_PTR(platform->entropy_mutex)) { @@ -591,6 +596,7 @@ void sys_init_platform(GlobalContext *global) #endif platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#endif otp_ssl_init(global); #endif #ifndef AVM_NO_JIT @@ -624,11 +630,14 @@ void sys_free_platform(GlobalContext *global) #endif #if !defined(AVM_NO_SMP) && ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 smp_mutex_destroy(platform->entropy_mutex); smp_mutex_destroy(platform->random_mutex); #endif +#endif #if ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } @@ -636,6 +645,7 @@ void sys_free_platform(GlobalContext *global) if (platform->entropy_is_initialized) { mbedtls_entropy_free(&platform->entropy_ctx); } +#endif #endif free(platform); @@ -745,6 +755,7 @@ bool event_listener_is_event(EventListener *listener, listener_event_t event) } #ifdef ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #ifndef MBEDTLS_THREADING_C @@ -811,6 +822,7 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) struct GenericUnixPlatformData *platform = global->platform_data; SMP_MUTEX_UNLOCK(platform->random_mutex); } +#endif #endif diff --git a/src/platforms/rp2/src/lib/rp2_sys.h b/src/platforms/rp2/src/lib/rp2_sys.h index a7cd4413bc..7d5499a1b4 100644 --- a/src/platforms/rp2/src/lib/rp2_sys.h +++ b/src/platforms/rp2/src/lib/rp2_sys.h @@ -30,8 +30,11 @@ #include #include +#include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include +#endif #pragma GCC diagnostic pop @@ -82,6 +85,7 @@ struct RP2PlatformData #endif queue_t event_queue; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif @@ -93,6 +97,7 @@ struct RP2PlatformData #endif mbedtls_ctr_drbg_context random_ctx; bool random_is_initialized; +#endif }; #endif diff --git a/src/platforms/rp2/src/lib/sys.c b/src/platforms/rp2/src/lib/sys.c index ac4c9c717c..c15c24e4eb 100644 --- a/src/platforms/rp2/src/lib/sys.c +++ b/src/platforms/rp2/src/lib/sys.c @@ -43,10 +43,9 @@ #include #endif -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include +#include +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include #endif // libAtomVM @@ -92,6 +91,14 @@ void sys_init_platform(GlobalContext *glb) otp_socket_init(glb); #endif +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#endif + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP platform->entropy_mutex = smp_mutex_create(); if (IS_NULL_PTR(platform->entropy_mutex)) { @@ -105,6 +112,7 @@ void sys_init_platform(GlobalContext *glb) platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#endif } void sys_free_platform(GlobalContext *glb) @@ -116,6 +124,7 @@ void sys_free_platform(GlobalContext *glb) struct RP2PlatformData *platform = glb->platform_data; queue_free(&platform->event_queue); +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } @@ -127,6 +136,7 @@ void sys_free_platform(GlobalContext *glb) #ifndef AVM_NO_SMP smp_mutex_destroy(platform->entropy_mutex); smp_mutex_destroy(platform->random_mutex); +#endif #endif free(platform); @@ -408,6 +418,7 @@ void sys_unregister_listener_from_event(GlobalContext *global, listener_event_t // TODO: enable mbedtls threading support by defining MBEDTLS_THREADING_ALT // and remove this function. +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #ifndef MBEDTLS_THREADING_C @@ -474,6 +485,7 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) struct RP2PlatformData *platform = global->platform_data; SMP_MUTEX_UNLOCK(platform->random_mutex); } +#endif #ifndef AVM_NO_JIT ModuleNativeEntryPoint sys_map_native_code(const uint8_t *native_code, size_t size, size_t offset) diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index 9471abf063..81556f2466 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -26,6 +26,9 @@ #include #include #include +#if ATOMVM_HAS_MBEDTLS +#include +#endif // #define ENABLE_TRACE #include @@ -190,6 +193,14 @@ void sys_init_platform(GlobalContext *glb) AVM_LOGE(TAG, "Out of memory!"); AVM_ABORT(); } +#if ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + AVM_ABORT(); + } +#endif +#endif glb->platform_data = platform; list_init(&platform->locked_pins); } From 707a672b76a918c4e7432692d5bd28c7c3e415da Mon Sep 17 00:00:00 2001 From: Peter M Date: Tue, 17 Mar 2026 05:10:17 +0100 Subject: [PATCH 2/9] otp_crypto: use PSA PBKDF2 on mbedtls 4 Avoid including mbedtls/pkcs5.h when building against mbedtls 4, where that header is not available. Keep the existing PKCS5-based pbkdf2_hmac implementation for mbedtls 2/3, but switch the mbedtls 4 path to the PSA key derivation API so crypto:pbkdf2_hmac/5 remains available. Also reject zero iterations in PBKDF2 with a clear error message, and update the feature/NIF guards so pbkdf2_hmac stays registered on both legacy and mbedtls 4 builds. Signed-off-by: Peter M --- src/libAtomVM/otp_crypto.c | 84 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index debe72f0e3..f37e455eb7 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -59,7 +59,7 @@ #include #endif -#ifdef MBEDTLS_PKCS5_C +#if MBEDTLS_VERSION_NUMBER < 0x04000000 && defined(MBEDTLS_PKCS5_C) #include #include #endif @@ -68,6 +68,10 @@ #include #endif +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 || defined(MBEDTLS_PKCS5_C) +#define AVM_HAVE_PBKDF2_HMAC 1 +#endif + // mbedtls_ct_memcmp is available in 2.28.x+ and 3.1.x+ (absent in 3.0.x) #if (MBEDTLS_VERSION_NUMBER >= 0x021C0000 && MBEDTLS_VERSION_NUMBER < 0x03000000) \ || MBEDTLS_VERSION_NUMBER >= 0x03010000 @@ -301,6 +305,11 @@ static psa_algorithm_t atom_to_psa_hash_alg(term type, GlobalContext *global) if (type == globalcontext_make_atom(global, ATOM_STR("\x6", "sha512"))) { return PSA_ALG_SHA_512; } +#ifdef PSA_ALG_RIPEMD160 + if (type == globalcontext_make_atom(global, ATOM_STR("\x9", "ripemd160"))) { + return PSA_ALG_RIPEMD160; + } +#endif return PSA_ALG_NONE; } @@ -3427,7 +3436,8 @@ static term nif_crypto_hash_equals(Context *ctx, int argc, term argv[]) return cmp == 0 ? TRUE_ATOM : FALSE_ATOM; } -#ifdef MBEDTLS_PKCS5_C +#ifdef AVM_HAVE_PBKDF2_HMAC +#if MBEDTLS_VERSION_NUMBER < 0x04000000 static const AtomStringIntPair md_hash_algorithm_table[] = { { ATOM_STR("\x3", "sha"), MBEDTLS_MD_SHA1 }, { ATOM_STR("\x6", "sha224"), MBEDTLS_MD_SHA224 }, @@ -3439,6 +3449,7 @@ static const AtomStringIntPair md_hash_algorithm_table[] = { SELECT_INT_DEFAULT(MBEDTLS_MD_NONE) }; +#endif static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) { @@ -3459,6 +3470,19 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) void *derived_key = NULL; avm_int_t derived_key_len = 0; +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_algorithm_t hash_alg = atom_to_psa_hash_alg(digest_type_term, glb); + if (UNLIKELY(hash_alg == PSA_ALG_NONE)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown digest type", ctx)); + } +#else + mbedtls_md_type_t md_type + = interop_atom_term_select_int(md_hash_algorithm_table, digest_type_term, glb); + if (UNLIKELY(md_type == MBEDTLS_MD_NONE)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown digest type", ctx)); + } +#endif + term password_term = argv[1]; const void *password; term iodata_handle_result @@ -3484,6 +3508,11 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) goto cleanup; } uint32_t iterations = term_to_uint32(iterations_term); + if (UNLIKELY(iterations == 0)) { + result + = make_crypto_error(__FILE__, __LINE__, "Iterations must be a positive integer", ctx); + goto cleanup; + } term key_len_term = argv[4]; if (UNLIKELY(!term_is_pos_int(key_len_term))) { @@ -3498,6 +3527,52 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) goto cleanup; } +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + result = make_crypto_error(__FILE__, __LINE__, "PSA init failed", ctx); + goto cleanup; + } + + status = psa_key_derivation_setup(&operation, PSA_ALG_PBKDF2_HMAC(hash_alg)); + if (UNLIKELY(status != PSA_SUCCESS)) { + psa_key_derivation_abort(&operation); + result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); + goto cleanup; + } + + status = psa_key_derivation_input_integer( + &operation, PSA_KEY_DERIVATION_INPUT_COST, iterations); + if (UNLIKELY(status != PSA_SUCCESS)) { + psa_key_derivation_abort(&operation); + result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); + goto cleanup; + } + + status = psa_key_derivation_input_bytes( + &operation, PSA_KEY_DERIVATION_INPUT_SALT, salt, salt_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + psa_key_derivation_abort(&operation); + result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); + goto cleanup; + } + + status = psa_key_derivation_input_bytes( + &operation, PSA_KEY_DERIVATION_INPUT_PASSWORD, password, password_len); + if (UNLIKELY(status != PSA_SUCCESS)) { + psa_key_derivation_abort(&operation); + result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); + goto cleanup; + } + + status = psa_key_derivation_output_bytes(&operation, derived_key, derived_key_len); + psa_key_derivation_abort(&operation); + if (UNLIKELY(status != PSA_SUCCESS)) { + result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); + goto cleanup; + } +#else #if MBEDTLS_VERSION_NUMBER >= 0x03030000 // mbedtls_pkcs5_pbkdf2_hmac_ext is available since 3.3.0 int ret = mbedtls_pkcs5_pbkdf2_hmac_ext( @@ -3522,6 +3597,7 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); goto cleanup; } +#endif if (UNLIKELY(memory_ensure_free(ctx, TERM_BINARY_HEAP_SIZE(derived_key_len)) != MEMORY_GC_OK)) { result = OUT_OF_MEMORY_ATOM; @@ -3762,7 +3838,7 @@ static const struct Nif crypto_crypto_one_time_aead_nif = { .nif_ptr = nif_crypto_crypto_one_time_aead }; #endif -#ifdef MBEDTLS_PKCS5_C +#ifdef AVM_HAVE_PBKDF2_HMAC static const struct Nif crypto_pbkdf2_hmac_nif = { .base.type = NIFFunctionType, .nif_ptr = nif_crypto_pbkdf2_hmac @@ -3875,7 +3951,7 @@ const struct Nif *otp_crypto_nif_get_nif(const char *nifname) return &crypto_crypto_one_time_aead_nif; } #endif -#ifdef MBEDTLS_PKCS5_C +#ifdef AVM_HAVE_PBKDF2_HMAC if (strcmp("pbkdf2_hmac/5", rest) == 0) { TRACE("Resolved platform nif %s ...\n", nifname); return &crypto_pbkdf2_hmac_nif; From 395f2ef729117dbf5dec281fe6104f1061f723d4 Mon Sep 17 00:00:00 2001 From: Peter M Date: Sat, 21 Mar 2026 14:57:44 +0100 Subject: [PATCH 3/9] otp_crypto: harden PSA crypto lifecycle and memory Improve PSA crypto resource management and memory safety: - Normalize do_psa_init() across all PSA-backed NIFs so every entry point initializes PSA consistently - Destroy PSA key handles immediately after finalization instead of deferring to GC, reducing key material residency time - Abort PSA operations and destroy keys on update failure to avoid dangling handles - Replace free() with secure_free() for all scratch buffers that may contain sensitive data (plaintext, key material) Signed-off-by: Peter M --- src/libAtomVM/otp_crypto.c | 81 ++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index f37e455eb7..a215e47937 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -98,6 +98,15 @@ #define MAX_MD_SIZE 64 +#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +static void do_psa_init(void) +{ + if (UNLIKELY(psa_crypto_init() != PSA_SUCCESS)) { + abort(); + } +} +#endif + enum crypto_algorithm { CryptoInvalidAlgorithm = 0, @@ -345,6 +354,7 @@ static term nif_crypto_hash(Context *ctx, int argc, term argv[]) size_t digest_len = 0; #if MBEDTLS_VERSION_NUMBER >= 0x04000000 + do_psa_init(); psa_algorithm_t alg = atom_to_psa_hash_alg(type, ctx->global); if (alg == PSA_ALG_NONE) { TRACE("crypto:hash unknown algorithm\n"); @@ -631,6 +641,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) term cipher_term = argv[0]; #if MBEDTLS_VERSION_NUMBER >= 0x04000000 + do_psa_init(); psa_key_type_t key_type; size_t key_bits; psa_algorithm_t alg = atom_to_psa_cipher_alg(cipher_term, ctx->global, &key_type, &key_bits); @@ -785,10 +796,10 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) // No complete blocks to process psa_cipher_abort(&operation); psa_destroy_key(key_id); - free(temp_buf); + secure_free(temp_buf, output_size); secure_free(allocated_key_data, key_len); secure_free(allocated_iv_data, iv_len); - free(allocated_data_data); + secure_free(allocated_data_data, data_size); // Return empty binary if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(0)) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); @@ -820,16 +831,16 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) secure_free(allocated_key_data, key_len); secure_free(allocated_iv_data, iv_len); - free(allocated_data_data); + secure_free(allocated_data_data, data_size); int ensure_size = term_binary_heap_size(output_len); if (UNLIKELY(memory_ensure_free(ctx, ensure_size) != MEMORY_GC_OK)) { - free(temp_buf); + secure_free(temp_buf, output_size); RAISE_ERROR(OUT_OF_MEMORY_ATOM); } term out = term_from_literal_binary(temp_buf, output_len, &ctx->heap, ctx->global); - free(temp_buf); + secure_free(temp_buf, output_size); return out; psa_error: @@ -837,7 +848,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) if (key_id != 0) { psa_destroy_key(key_id); } - free(temp_buf); + secure_free(temp_buf, output_size); goto raise_error; #else mbedtls_operation_t operation; @@ -963,7 +974,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) raise_error: secure_free(allocated_key_data, key_len); secure_free(allocated_iv_data, iv_len); - free(allocated_data_data); + secure_free(allocated_data_data, data_size); RAISE_ERROR(error_atom); #endif } @@ -1053,13 +1064,6 @@ static const struct PsaEccCurveParams *psa_ecc_curve_table_lookup(enum pk_param_ return NULL; } -static void do_psa_init(void) -{ - if (UNLIKELY(psa_crypto_init() != PSA_SUCCESS)) { - abort(); - } -} - // TODO: MbedTLS PSA Crypto API is expected to add Ed25519/X25519 support in a future version. // Once that version is widely adopted, we may be able to replace the libsodium backend with // pure PSA API calls. @@ -2153,7 +2157,10 @@ static void psa_mac_op_dtor(ErlNifEnv *caller_env, void *obj) struct MacState *mac_state = (struct MacState *) obj; psa_mac_abort(&mac_state->psa_op); - psa_destroy_key(mac_state->key_id); + if (mac_state->key_id != 0) { + psa_destroy_key(mac_state->key_id); + mac_state->key_id = 0; + } #ifndef AVM_NO_SMP if (mac_state->mutex) { smp_mutex_destroy(mac_state->mutex); @@ -2321,11 +2328,16 @@ static term nif_crypto_mac_update(Context *ctx, int argc, term argv[]) SMP_MUTEX_LOCK(mac_state->mutex); psa_status_t status = psa_mac_update(&mac_state->psa_op, data, data_len); - SMP_MUTEX_UNLOCK(mac_state->mutex); - free(maybe_allocated_data); if (UNLIKELY(status != PSA_SUCCESS)) { + psa_mac_abort(&mac_state->psa_op); + psa_destroy_key(mac_state->key_id); + mac_state->key_id = 0; + SMP_MUTEX_UNLOCK(mac_state->mutex); + secure_free(maybe_allocated_data, data_len); RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx)); } + SMP_MUTEX_UNLOCK(mac_state->mutex); + secure_free(maybe_allocated_data, data_len); return argv[0]; } @@ -2362,6 +2374,8 @@ static term nif_crypto_mac_final(Context *ctx, int argc, term argv[]) size_t mac_len = 0; SMP_MUTEX_LOCK(mac_state->mutex); psa_status_t status = psa_mac_sign_finish(&mac_state->psa_op, mac_buf, mac_size, &mac_len); + psa_destroy_key(mac_state->key_id); + mac_state->key_id = 0; SMP_MUTEX_UNLOCK(mac_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); @@ -2421,6 +2435,8 @@ static term nif_crypto_mac_finalN(Context *ctx, int argc, term argv[]) size_t mac_len = 0; SMP_MUTEX_LOCK(mac_state->mutex); psa_status_t status = psa_mac_sign_finish(&mac_state->psa_op, mac_buf, mac_size, &mac_len); + psa_destroy_key(mac_state->key_id); + mac_state->key_id = 0; SMP_MUTEX_UNLOCK(mac_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); @@ -2649,7 +2665,10 @@ static void psa_cipher_op_dtor(ErlNifEnv *caller_env, void *obj) struct CipherState *cipher_state = (struct CipherState *) obj; psa_cipher_abort(&cipher_state->psa_op); - psa_destroy_key(cipher_state->key_id); + if (cipher_state->key_id != 0) { + psa_destroy_key(cipher_state->key_id); + cipher_state->key_id = 0; + } #ifndef AVM_NO_SMP if (cipher_state->mutex) { smp_mutex_destroy(cipher_state->mutex); @@ -2989,11 +3008,16 @@ static term nif_crypto_crypto_update(Context *ctx, int argc, term argv[]) size_t out_len = 0; psa_status_t status = psa_cipher_update(&cipher_state->psa_op, data, data_len, out_buf, out_size, &out_len); - SMP_MUTEX_UNLOCK(cipher_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { + psa_cipher_abort(&cipher_state->psa_op); + psa_destroy_key(cipher_state->key_id); + cipher_state->key_id = 0; + cipher_state->finalized = true; + SMP_MUTEX_UNLOCK(cipher_state->mutex); result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); goto cleanup; } + SMP_MUTEX_UNLOCK(cipher_state->mutex); if (UNLIKELY(memory_ensure_free(ctx, TERM_BINARY_HEAP_SIZE(out_len)) != MEMORY_GC_OK)) { result = OUT_OF_MEMORY_ATOM; @@ -3004,8 +3028,8 @@ static term nif_crypto_crypto_update(Context *ctx, int argc, term argv[]) result = term_from_literal_binary(out_buf, out_len, &ctx->heap, glb); cleanup: - free(maybe_allocated_data); - free(out_buf); + secure_free(maybe_allocated_data, data_len); + secure_free(out_buf, out_size); if (UNLIKELY(!success)) { RAISE_ERROR(result); @@ -3059,6 +3083,8 @@ static term nif_crypto_crypto_final(Context *ctx, int argc, term argv[]) size_t out_len = 0; psa_status_t status = psa_cipher_finish(&cipher_state->psa_op, out_buf, out_size, &out_len); cipher_state->finalized = true; + psa_destroy_key(cipher_state->key_id); + cipher_state->key_id = 0; SMP_MUTEX_UNLOCK(cipher_state->mutex); if (status == PSA_SUCCESS) { @@ -3105,7 +3131,7 @@ static term nif_crypto_crypto_final(Context *ctx, int argc, term argv[]) } cleanup: - free(out_buf); + secure_free(out_buf, out_size); if (UNLIKELY(!success)) { RAISE_ERROR(result); @@ -3382,7 +3408,7 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) cleanup: psa_destroy_key(key_id); - free(maybe_allocated_intext); + secure_free(maybe_allocated_intext, intext_len); free(maybe_allocated_aad); secure_free(out_buf, out_buf_size); @@ -3528,14 +3554,10 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) } #if MBEDTLS_VERSION_NUMBER >= 0x04000000 + do_psa_init(); psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; - psa_status_t status = psa_crypto_init(); - if (UNLIKELY(status != PSA_SUCCESS)) { - result = make_crypto_error(__FILE__, __LINE__, "PSA init failed", ctx); - goto cleanup; - } - status = psa_key_derivation_setup(&operation, PSA_ALG_PBKDF2_HMAC(hash_alg)); + psa_status_t status = psa_key_derivation_setup(&operation, PSA_ALG_PBKDF2_HMAC(hash_alg)); if (UNLIKELY(status != PSA_SUCCESS)) { psa_key_derivation_abort(&operation); result = make_crypto_error(__FILE__, __LINE__, "Key derivation failed", ctx); @@ -3638,6 +3660,7 @@ term nif_crypto_strong_rand_bytes(Context *ctx, int argc, term argv[]) } #if MBEDTLS_VERSION_NUMBER >= 0x04000000 + do_psa_init(); term out_bin = term_create_uninitialized_binary(out_len, &ctx->heap, ctx->global); unsigned char *out = (unsigned char *) term_binary_data(out_bin); From a0899912aa713ba5e88fa55967b48b788a51307a Mon Sep 17 00:00:00 2001 From: Peter M Date: Sun, 22 Mar 2026 22:59:14 +0100 Subject: [PATCH 4/9] otp_crypto: initialize PSA cleanup sizes before error paths Declare the PSA output buffer size variables before any goto-based cleanup path can skip their initialization. This fixes Clang -Wsometimes-uninitialized failures in crypto_one_time/4-5 and crypto_update/2 when cleanup frees scratch buffers after early exits. Signed-off-by: Peter M --- src/libAtomVM/otp_crypto.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index a215e47937..909156e739 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -692,6 +692,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) bool encrypt = true; bool padding_pkcs7 = false; psa_key_id_t key_id = 0; + size_t output_size = 0; void *temp_buf = NULL; psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT; @@ -751,7 +752,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) goto psa_error; } - size_t output_size = PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, data_size); + output_size = PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, data_size); if (!encrypt) { output_size = PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, data_size); } @@ -2975,12 +2976,13 @@ static term nif_crypto_crypto_update(Context *ctx, int argc, term argv[]) void *maybe_allocated_data = NULL; void *out_buf = NULL; + size_t data_len = 0; + size_t out_size = 0; // from this point onward use `goto cleanup` in order to raise and free all buffers /* 2. Handle iodata input */ const void *data; - size_t data_len; term iodata_result = handle_iodata(argv[1], &data, &data_len, &maybe_allocated_data); if (UNLIKELY(iodata_result == BADARG_ATOM)) { SMP_MUTEX_UNLOCK(cipher_state->mutex); @@ -2994,7 +2996,7 @@ static term nif_crypto_crypto_update(Context *ctx, int argc, term argv[]) } /* 3. Encrypt/decrypt via PSA - PSA handles internal block buffering */ - size_t out_size = PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(data_len); + out_size = PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(data_len); if (out_size == 0) { out_size = 1; /* ensure valid malloc even for zero-length input */ } From fe9c378101a065d0059760506a3f22a65bdb0fc2 Mon Sep 17 00:00:00 2001 From: Peter M Date: Thu, 26 Mar 2026 16:55:00 +0100 Subject: [PATCH 5/9] otp_crypto: avoid malloc(0) in handle_iodata and one-shot cipher Allocate at least 1 byte when the computed size is zero to avoid undefined malloc(0) behaviour on embedded libc implementations that may legally return NULL for zero-length allocations. This aligns the one-shot cipher and handle_iodata paths with the streaming cipher code that already guards against this case. Signed-off-by: Peter M Amp-Thread-ID: https://ampcode.com/threads/T-019d2a7c-9760-707a-b24e-f6f6475a8608 Co-authored-by: Amp --- src/libAtomVM/otp_crypto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index 909156e739..b03394ea92 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -486,7 +486,7 @@ static term handle_iodata(term iodata, const void **data, size_t *len, void **al case InteropBadArg: return BADARG_ATOM; } - void *allocated_buf = malloc(*len); + void *allocated_buf = malloc(*len ? *len : 1); if (IS_NULL_PTR(allocated_buf)) { return OUT_OF_MEMORY_ATOM; } @@ -756,7 +756,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) if (!encrypt) { output_size = PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, data_size); } - temp_buf = malloc(output_size); + temp_buf = malloc(output_size ? output_size : 1); if (IS_NULL_PTR(temp_buf)) { error_atom = OUT_OF_MEMORY_ATOM; goto psa_error; From f18d9b0e79773e8cfd4ae7524f4f9cc5bb8b3fb9 Mon Sep 17 00:00:00 2001 From: Peter M Date: Thu, 26 Mar 2026 17:03:43 +0100 Subject: [PATCH 6/9] otp_crypto: harden PSA security and error handling - Reset key attributes after psa_import_key in one-shot cipher path to match all other PSA import sites - Use secure_free for all crypto-adjacent buffers (sign/verify data, signature buffers, MAC data, AEAD AAD and combined buffers) to prevent sensitive data from lingering in freed memory - Reject AEAD decryption without a tag early with a clear error instead of letting it fail deep in PSA - Add finalized flag to MAC state so repeated mac_final/mac_update calls after finalization raise a clear error instead of a generic PSA failure - Document that ssl:nif_conf_rng is a no-op on mbedtls 4.x where PSA handles randomness internally Signed-off-by: Peter M --- src/libAtomVM/otp_crypto.c | 59 +++++++++++++------ src/libAtomVM/otp_ssl.c | 1 + .../emscripten/src/lib/emscripten_sys.h | 2 +- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index b03394ea92..43079f3c13 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -745,6 +745,7 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) psa_set_key_bits(&attributes, key_bits); psa_status_t status = psa_import_key(&attributes, key_data, key_len, &key_id); + psa_reset_key_attributes(&attributes); if (UNLIKELY(status != PSA_SUCCESS)) { char err_msg[48]; snprintf(err_msg, sizeof(err_msg), "key import err %d", (int) status); @@ -1727,7 +1728,7 @@ static term nif_crypto_sign(Context *ctx, int argc, term argv[]) term data_term = argv[2]; const void *data; - size_t data_len; + size_t data_len = 0; term iodata_handle_result = handle_iodata(data_term, &data, &data_len, &maybe_allocated_data); if (UNLIKELY(iodata_handle_result != OK_ATOM)) { result = make_crypto_error(__FILE__, __LINE__, "Expected a binary or a list", ctx); @@ -1782,9 +1783,9 @@ static term nif_crypto_sign(Context *ctx, int argc, term argv[]) cleanup: psa_destroy_key(key_id); - free(maybe_allocated_data); - free(sig_raw); - free(sig_der); + secure_free(maybe_allocated_data, data_len); + secure_free(sig_raw, sig_raw_size); + secure_free(sig_der, sig_der_size); if (UNLIKELY(!success)) { RAISE_ERROR(result); @@ -1904,7 +1905,7 @@ static term nif_crypto_verify(Context *ctx, int argc, term argv[]) term data_term = argv[2]; const void *data; - size_t data_len; + size_t data_len = 0; term iodata_handle_result = handle_iodata(data_term, &data, &data_len, &maybe_allocated_data); if (UNLIKELY(iodata_handle_result != OK_ATOM)) { result = make_crypto_error(__FILE__, __LINE__, "Expected a binary or a list", ctx); @@ -1951,8 +1952,8 @@ static term nif_crypto_verify(Context *ctx, int argc, term argv[]) cleanup: psa_destroy_key(key_id); - free(maybe_allocated_data); - free(sig_raw); + secure_free(maybe_allocated_data, data_len); + secure_free(sig_raw, sig_raw_size); if (UNLIKELY(!success)) { RAISE_ERROR(result); @@ -2005,6 +2006,8 @@ static term nif_crypto_mac(Context *ctx, int argc, term argv[]) void *maybe_allocated_key = NULL; size_t key_len = 0; void *maybe_allocated_data = NULL; + const void *data = NULL; + size_t data_len = 0; size_t mac_out_size = 0; void *mac_out = NULL; @@ -2072,8 +2075,6 @@ static term nif_crypto_mac(Context *ctx, int argc, term argv[]) } term data_term = argv[3]; - const void *data; - size_t data_len; iodata_handle_result = handle_iodata(data_term, &data, &data_len, &maybe_allocated_data); if (UNLIKELY(iodata_handle_result != OK_ATOM)) { result = make_crypto_error(__FILE__, __LINE__, "Expected a binary or a list", ctx); @@ -2112,7 +2113,7 @@ static term nif_crypto_mac(Context *ctx, int argc, term argv[]) psa_destroy_key(key_id); secure_free(mac_out, mac_out_size); secure_free(maybe_allocated_key, key_len); - free(maybe_allocated_data); + secure_free(maybe_allocated_data, data_len); if (UNLIKELY(!success)) { RAISE_ERROR(result); @@ -2147,6 +2148,7 @@ struct MacState psa_algorithm_t psa_algo; psa_key_type_t psa_key_type; size_t key_bit_size; + bool finalized; #ifndef AVM_NO_SMP Mutex *mutex; #endif @@ -2317,13 +2319,17 @@ static term nif_crypto_mac_update(Context *ctx, int argc, term argv[]) } struct MacState *mac_state = (struct MacState *) psa_mac_obj_ptr; + if (UNLIKELY(mac_state->finalized)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "MAC already finalized", ctx)); + } + void *maybe_allocated_data = NULL; - size_t data_len; + size_t data_len = 0; term data_term = argv[1]; const void *data; term iodata_handle_result = handle_iodata(data_term, &data, &data_len, &maybe_allocated_data); if (UNLIKELY(iodata_handle_result != OK_ATOM)) { - free(maybe_allocated_data); + secure_free(maybe_allocated_data, data_len); RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Bad text", ctx)); } @@ -2333,6 +2339,7 @@ static term nif_crypto_mac_update(Context *ctx, int argc, term argv[]) psa_mac_abort(&mac_state->psa_op); psa_destroy_key(mac_state->key_id); mac_state->key_id = 0; + mac_state->finalized = true; SMP_MUTEX_UNLOCK(mac_state->mutex); secure_free(maybe_allocated_data, data_len); RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx)); @@ -2358,6 +2365,10 @@ static term nif_crypto_mac_final(Context *ctx, int argc, term argv[]) } struct MacState *mac_state = (struct MacState *) psa_mac_obj_ptr; + if (UNLIKELY(mac_state->finalized)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "MAC already finalized", ctx)); + } + bool success = false; term result = ERROR_ATOM; @@ -2377,6 +2388,7 @@ static term nif_crypto_mac_final(Context *ctx, int argc, term argv[]) psa_status_t status = psa_mac_sign_finish(&mac_state->psa_op, mac_buf, mac_size, &mac_len); psa_destroy_key(mac_state->key_id); mac_state->key_id = 0; + mac_state->finalized = true; SMP_MUTEX_UNLOCK(mac_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); @@ -2409,6 +2421,10 @@ static term nif_crypto_mac_finalN(Context *ctx, int argc, term argv[]) } struct MacState *mac_state = (struct MacState *) psa_mac_obj_ptr; + if (UNLIKELY(mac_state->finalized)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "MAC already finalized", ctx)); + } + avm_int_t requested_len; if (UNLIKELY(!term_is_integer(argv[1]))) { RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Bad length", ctx)); @@ -2438,6 +2454,7 @@ static term nif_crypto_mac_finalN(Context *ctx, int argc, term argv[]) psa_status_t status = psa_mac_sign_finish(&mac_state->psa_op, mac_buf, mac_size, &mac_len); psa_destroy_key(mac_state->key_id); mac_state->key_id = 0; + mac_state->finalized = true; SMP_MUTEX_UNLOCK(mac_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); @@ -3214,6 +3231,10 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "EncFlag must be a boolean", ctx)); } + if (UNLIKELY(!encrypting && argc == 6)) { + RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Tag is required for AEAD decryption", ctx)); + } + size_t tag_len = aead_params->default_tag_len; const void *tag_data = NULL; size_t tag_data_len = 0; @@ -3289,19 +3310,19 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) void *maybe_allocated_aad = NULL; void *out_buf = NULL; size_t out_buf_size = 0; + const void *intext_data = NULL; + size_t intext_len = 0; + const void *aad_data = NULL; + size_t aad_len = 0; // from this point onward use `goto cleanup` in order to raise and free all buffers - const void *intext_data; - size_t intext_len; term iodata_result = handle_iodata(argv[3], &intext_data, &intext_len, &maybe_allocated_intext); if (UNLIKELY(iodata_result != OK_ATOM)) { result = make_crypto_error(__FILE__, __LINE__, "Expected a binary or a list", ctx); goto cleanup; } - const void *aad_data; - size_t aad_len; iodata_result = handle_iodata(argv[4], &aad_data, &aad_len, &maybe_allocated_aad); if (UNLIKELY(iodata_result != OK_ATOM)) { result = make_crypto_error(__FILE__, __LINE__, "Expected a binary or a list", ctx); @@ -3369,7 +3390,7 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) out_buf_size = pt_size + 1; out_buf = malloc(out_buf_size); // +1 to ensure valid malloc even for 0 if (IS_NULL_PTR(out_buf)) { - free(combined_buf); + secure_free(combined_buf, combined_len); result = OUT_OF_MEMORY_ATOM; goto cleanup; } @@ -3377,7 +3398,7 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) size_t pt_len = 0; status = psa_aead_decrypt(key_id, psa_algo, iv_data, iv_len, aad_data, aad_len, combined_buf, combined_len, out_buf, pt_size, &pt_len); - free(combined_buf); + secure_free(combined_buf, combined_len); switch (status) { case PSA_SUCCESS: @@ -3411,7 +3432,7 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) cleanup: psa_destroy_key(key_id); secure_free(maybe_allocated_intext, intext_len); - free(maybe_allocated_aad); + secure_free(maybe_allocated_aad, aad_len); secure_free(out_buf, out_buf_size); if (UNLIKELY(!success)) { diff --git a/src/libAtomVM/otp_ssl.c b/src/libAtomVM/otp_ssl.c index 56124bf549..5bdf1a337f 100644 --- a/src/libAtomVM/otp_ssl.c +++ b/src/libAtomVM/otp_ssl.c @@ -521,6 +521,7 @@ static term nif_ssl_conf_rng(Context *ctx, int argc, term argv[]) mbedtls_ssl_conf_rng(&conf_obj->config, mbedtls_ctr_drbg_random, &ctr_drbg_obj->context); #else + // mbedtls 4.x uses PSA for randomness; no explicit RNG configuration needed. UNUSED(conf_obj); UNUSED(ctr_drbg_obj); #endif diff --git a/src/platforms/emscripten/src/lib/emscripten_sys.h b/src/platforms/emscripten/src/lib/emscripten_sys.h index f2978e4da2..68555d7050 100644 --- a/src/platforms/emscripten/src/lib/emscripten_sys.h +++ b/src/platforms/emscripten/src/lib/emscripten_sys.h @@ -33,8 +33,8 @@ #include #include -#include #include "sys_mbedtls.h" +#include #if MBEDTLS_VERSION_NUMBER < 0x04000000 #include From d88f5249ce3df91f82f83062b3ab76c1bae074ea Mon Sep 17 00:00:00 2001 From: Peter M Date: Fri, 27 Mar 2026 08:59:22 +0100 Subject: [PATCH 7/9] Fix esp32 HAVE_PSA_CRYPTO Signed-off-by: Peter M --- src/platforms/esp32/components/avm_sys/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/esp32/components/avm_sys/CMakeLists.txt b/src/platforms/esp32/components/avm_sys/CMakeLists.txt index 7c3bae1e5c..5e07820107 100644 --- a/src/platforms/esp32/components/avm_sys/CMakeLists.txt +++ b/src/platforms/esp32/components/avm_sys/CMakeLists.txt @@ -117,7 +117,7 @@ if(HAVE_SOC_CPU_CORES_NUM) endif() check_c_source_compiles(" - #include + #include #ifndef MBEDTLS_PSA_CRYPTO_C #error PSA Crypto not available #endif From dbee591bf07c15e1908f6321cca0b9cb6ed039b2 Mon Sep 17 00:00:00 2001 From: Peter M Date: Thu, 26 Mar 2026 22:24:27 +0100 Subject: [PATCH 8/9] Add mbedtls4 CI coverage Signed-off-by: Peter M --- .../workflows/build-and-test-on-freebsd.yaml | 32 +++++++++++- .github/workflows/build-and-test.yaml | 51 +++++++++++++++++-- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-and-test-on-freebsd.yaml b/.github/workflows/build-and-test-on-freebsd.yaml index f22718ecc2..b56311fc5f 100644 --- a/.github/workflows/build-and-test-on-freebsd.yaml +++ b/.github/workflows/build-and-test-on-freebsd.yaml @@ -35,7 +35,7 @@ concurrency: jobs: build-and-test-on-freebsd: runs-on: ubuntu-24.04 - name: Build and test AtomVM on FreeBSD + name: Build and test AtomVM on FreeBSD ${{ matrix.os_release }} (${{ matrix.mbedtls }}) env: ATOMVM_EXAMPLE: "atomvm-example" @@ -44,6 +44,10 @@ jobs: matrix: os_release: ["13.5", "14.3", "15.0"] + mbedtls: ["mbedtls@3"] + include: + - os_release: "14.3" + mbedtls: "mbedtls@4" steps: @@ -60,10 +64,25 @@ jobs: sync: rsync copyback: false + - name: "Use latest pkg repo for MbedTLS 4" + if: matrix.mbedtls == 'mbedtls@4' + shell: freebsd {0} + run: | + mkdir -p /usr/local/etc/pkg/repos + echo 'FreeBSD: { url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf + pkg update -f + - name: "Install deps" + if: matrix.mbedtls == 'mbedtls@3' + shell: freebsd {0} + run: | + pkg install -y curl cmake gperf erlang elixir rebar3 ninja mbedtls3 socat + + - name: "Install deps (MbedTLS 4)" + if: matrix.mbedtls == 'mbedtls@4' shell: freebsd {0} run: | - pkg install -y curl cmake gperf erlang elixir rebar3 mbedtls3 ninja socat + pkg install -y curl cmake gperf erlang elixir rebar3 ninja mbedtls4 socat - name: "Add hostname to /etc/hosts for distribution tests" shell: freebsd {0} @@ -102,6 +121,15 @@ jobs: mkdir build - name: "Build: run cmake" + if: matrix.mbedtls == 'mbedtls@3' + shell: freebsd {0} + run: | + cd $GITHUB_WORKSPACE; + cd build + cmake .. -DAVM_WARNINGS_ARE_ERRORS=ON + + - name: "Build: run cmake (MbedTLS 4)" + if: matrix.mbedtls == 'mbedtls@4' shell: freebsd {0} run: | cd $GITHUB_WORKSPACE; diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 3bee12be9d..40a63a3727 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -59,6 +59,7 @@ jobs: # We only test several OTP versions with default compilers for supported OSes (gcc 11, gcc 13, clang 14, clang 18) cc: ["gcc-11", "gcc-13", "clang-14", "clang-18"] otp: ["26", "27", "28"] + mbedtls: ["default"] include: ### gcc @@ -159,6 +160,12 @@ jobs: otp: "master" elixir_version: "main" + # Additional mbedtls@4 coverage with the default Linux toolchain + - cc: "cc" + cxx: "c++" + otp: "28" + mbedtls: "mbedtls@4" + # Additional latest & -Os compiler builds - cc: "gcc-14" cxx: "g++-14" @@ -540,13 +547,26 @@ jobs: run: sudo apt update -y - name: "Install deps" - if: matrix.container != '' - run: sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen valgrind libmbedtls-dev socat + if: matrix.container != '' && matrix.mbedtls != 'mbedtls@4' + run: | + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen valgrind libmbedtls-dev socat + + - name: "Install deps (MbedTLS 4)" + if: matrix.container != '' && matrix.mbedtls == 'mbedtls@4' + run: | + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen valgrind socat - name: "Install deps" - if: matrix.container == '' + if: matrix.container == '' && matrix.mbedtls != 'mbedtls@4' + run: | + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen libc6-dbg libmbedtls-dev socat + # Get a more recent valgrind + sudo snap install valgrind --classic + + - name: "Install deps (MbedTLS 4)" + if: matrix.container == '' && matrix.mbedtls == 'mbedtls@4' run: | - sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen libmbedtls-dev libc6-dbg socat + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen libc6-dbg socat # Get a more recent valgrind sudo snap install valgrind --classic @@ -582,6 +602,19 @@ jobs: https://repo.hex.pm https://cdn.jsdelivr.net/hex + - name: "Install specific MbedTLS version" + if: matrix.mbedtls == 'mbedtls@4' + run: | + git clone --depth 1 --branch mbedtls-4.0.0 --recurse-submodules https://github.com/Mbed-TLS/mbedtls + cd mbedtls + mkdir build + cd build + cmake -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=On -DCMAKE_INSTALL_PREFIX=/usr/local .. + make -j$(nproc) + sudo make install + sudo ldconfig + echo "MBEDTLS_ROOT_DIR=/usr/local" >> $GITHUB_ENV + # Builder info - name: "System info" run: | @@ -619,6 +652,7 @@ jobs: key: ${{ matrix.otp || env.DEFAULT_OTP_VERSION }}-${{ hashFiles('**/build-and-test.yaml', 'tests/**/*.erl', 'tests/**/*.hrl', 'tests/**/*.ex') }}-${{ matrix.jit_target_arch || 'nojit' }}-${{ contains(matrix.cmake_opts_other, 'AVM_DISABLE_JIT_DWARF=OFF') && 'dwarf' || 'nodwarf' }} - name: "Build: run cmake" + if: matrix.mbedtls != 'mbedtls@4' working-directory: build run: | cmake ${{ matrix.cmake_opts_fp }} ${{ matrix.cmake_opts_smp }} ${{ matrix.cmake_opts_other || env.DEFAULT_CMAKE_OPTS_OTHER }} .. @@ -626,6 +660,15 @@ jobs: # touch them so we can benefit from the cache and avoid costly beam file rebuild. find . -name '*.beam' -exec touch {} \; + - name: "Build: run cmake (MbedTLS 4)" + if: matrix.mbedtls == 'mbedtls@4' + working-directory: build + run: | + cmake -DMBEDTLS_ROOT_DIR=/usr/local ${{ matrix.cmake_opts_fp }} ${{ matrix.cmake_opts_smp }} ${{ matrix.cmake_opts_other || env.DEFAULT_CMAKE_OPTS_OTHER }} .. + # git clone will use more recent timestamps than cached beam files + # touch them so we can benefit from the cache and avoid costly beam file rebuild. + find . -name '*.beam' -exec touch {} \; + - name: "Build: run make" working-directory: build run: make -j3 From 7a79a64e2ce6b4a0c5af7d9b57e37e7dc4e30214 Mon Sep 17 00:00:00 2001 From: Peter M Date: Tue, 31 Mar 2026 15:52:18 +0200 Subject: [PATCH 9/9] emscripten: support MbedTLS 4 in build and CI Make the fetched MbedTLS version configurable and extend the wasm jobs to exercise both mbedtls3 and mbedtls4 builds while keeping release artifacts on the mbedtls3 path. Update the Emscripten build to link against MbedTLS::mbedtls, guard the legacy entropy helpers to pre-4.x builds, and switch the PSA feature probes to compile-only checks so they work with fetched and custom MbedTLS targets. Install the Python modules required by the MbedTLS 4 generators in the Emscripten CI container so the tf-psa-crypto build completes. Signed-off-by: Peter M --- .github/workflows/wasm-build.yaml | 42 ++++++++++++++----- CMakeModules/FetchMbedTLS.cmake | 4 +- .../emscripten/src/lib/CMakeLists.txt | 13 ++++-- src/platforms/emscripten/src/lib/sys.c | 5 +++ 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/.github/workflows/wasm-build.yaml b/.github/workflows/wasm-build.yaml index a3be9405cb..14a6d8826e 100644 --- a/.github/workflows/wasm-build.yaml +++ b/.github/workflows/wasm-build.yaml @@ -107,13 +107,21 @@ jobs: needs: compile_tests runs-on: ubuntu-24.04 container: emscripten/emsdk + strategy: + fail-fast: false + matrix: + include: + - mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - mbedtls_label: "mbedtls4" + mbedtls_git_tag: "v4.0.0" steps: - name: Checkout repo uses: actions/checkout@v4 - name: "Install deps" - run: sudo apt update -y && sudo apt install -y cmake gperf + run: sudo apt update -y && sudo apt install -y cmake gperf python3-jinja2 python3-jsonschema - name: Build shell: bash @@ -122,7 +130,7 @@ jobs: set -euo pipefail mkdir build cd build - emcmake cmake .. + emcmake cmake .. -DAVM_FETCH_MBEDTLS_GIT_TAG=${{ matrix.mbedtls_git_tag }} emmake make -j - name: Download AtomVM and test modules @@ -147,7 +155,7 @@ jobs: node src/AtomVM.js ../../../../build/tests/erlang_tests/test_crypto.beam - name: "Rename and write sha256sum (node)" - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' shell: bash working-directory: src/platforms/emscripten/build/src run: | @@ -160,7 +168,7 @@ jobs: - name: "Release (node)" uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' with: draft: true fail_on_unmatched_files: true @@ -181,14 +189,20 @@ jobs: strategy: fail-fast: false matrix: - language: ["javascript-typescript"] + include: + - language: "javascript-typescript" + mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - language: "javascript-typescript" + mbedtls_label: "mbedtls4" + mbedtls_git_tag: "v4.0.0" steps: - name: Checkout repo uses: actions/checkout@v4 - name: "Install deps" - run: sudo apt update -y && sudo apt install -y cmake gperf + run: sudo apt update -y && sudo apt install -y cmake gperf python3-jinja2 python3-jsonschema - name: "Initialize CodeQL" uses: github/codeql-action/init@v3 @@ -204,7 +218,7 @@ jobs: set -euo pipefail mkdir build cd build - emcmake cmake .. -DAVM_EMSCRIPTEN_ENV=web + emcmake cmake .. -DAVM_EMSCRIPTEN_ENV=web -DAVM_FETCH_MBEDTLS_GIT_TAG=${{ matrix.mbedtls_git_tag }} emmake make -j - name: "Perform CodeQL Analysis" @@ -213,7 +227,7 @@ jobs: - name: Upload wasm build for web uses: actions/upload-artifact@v4 with: - name: atomvm-js-web + name: atomvm-js-web-${{ matrix.mbedtls_label }} path: | src/platforms/emscripten/build/**/*.wasm src/platforms/emscripten/build/**/*.js @@ -222,6 +236,12 @@ jobs: wasm_test_web: needs: [compile_tests, wasm_build_web] runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - mbedtls_label: "mbedtls3" + - mbedtls_label: "mbedtls4" steps: - name: Checkout repo uses: actions/checkout@v4 @@ -235,7 +255,7 @@ jobs: - name: Download wasm build for web uses: actions/download-artifact@v4 with: - name: atomvm-js-web + name: atomvm-js-web-${{ matrix.mbedtls_label }} path: src/platforms/emscripten/build - name: Download emscripten test modules @@ -270,7 +290,7 @@ jobs: retention-days: 7 - name: "Rename and write sha256sum (web)" - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' shell: bash working-directory: src/platforms/emscripten/build/src run: | @@ -283,7 +303,7 @@ jobs: - name: "Release (web)" uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' with: draft: true fail_on_unmatched_files: true diff --git a/CMakeModules/FetchMbedTLS.cmake b/CMakeModules/FetchMbedTLS.cmake index 81154b4b86..c53405b8f0 100644 --- a/CMakeModules/FetchMbedTLS.cmake +++ b/CMakeModules/FetchMbedTLS.cmake @@ -20,10 +20,12 @@ include(FetchContent) +set(AVM_FETCH_MBEDTLS_GIT_TAG "v3.6.3.1" CACHE STRING "MbedTLS git tag to fetch for Emscripten builds") + FetchContent_Declare( mbedtls GIT_REPOSITORY http://github.com/mbed-TLS/mbedtls.git - GIT_TAG v3.6.3.1 + GIT_TAG ${AVM_FETCH_MBEDTLS_GIT_TAG} GIT_SHALLOW 1 ) diff --git a/src/platforms/emscripten/src/lib/CMakeLists.txt b/src/platforms/emscripten/src/lib/CMakeLists.txt index c91b0c87d9..e87ad049c4 100644 --- a/src/platforms/emscripten/src/lib/CMakeLists.txt +++ b/src/platforms/emscripten/src/lib/CMakeLists.txt @@ -47,19 +47,24 @@ target_compile_features(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC c_std_11) target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC libAtomVM) target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC ATOMVM_HAS_MBEDTLS) -target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC MbedTLS::mbedcrypto) +target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC MbedTLS::mbedtls) include(CheckCSourceCompiles) -get_target_property(_mbedcrypto_includes MbedTLS::mbedcrypto INTERFACE_INCLUDE_DIRECTORIES) -set(CMAKE_REQUIRED_INCLUDES ${_mbedcrypto_includes}) +get_target_property(_mbedtls_includes MbedTLS::mbedtls INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_REQUIRED_INCLUDES ${_mbedtls_includes}) +set(_avm_try_compile_target_type ${CMAKE_TRY_COMPILE_TARGET_TYPE}) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) check_c_source_compiles(" #include #ifndef MBEDTLS_PSA_CRYPTO_C #error PSA Crypto not available #endif + #include int main(void) { return 0; } " HAVE_PSA_CRYPTO) +set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_avm_try_compile_target_type}) unset(CMAKE_REQUIRED_INCLUDES) -unset(_mbedcrypto_includes) +unset(_avm_try_compile_target_type) +unset(_mbedtls_includes) if (HAVE_PSA_CRYPTO) target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC HAVE_PSA_CRYPTO) endif() diff --git a/src/platforms/emscripten/src/lib/sys.c b/src/platforms/emscripten/src/lib/sys.c index 41fbc00bba..0884468050 100644 --- a/src/platforms/emscripten/src/lib/sys.c +++ b/src/platforms/emscripten/src/lib/sys.c @@ -771,6 +771,8 @@ term sys_get_info(Context *ctx, term key) return UNDEFINED_ATOM; } +#ifdef ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #ifndef MBEDTLS_THREADING_C @@ -837,3 +839,6 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) struct EmscriptenPlatformData *platform = global->platform_data; SMP_MUTEX_UNLOCK(platform->random_mutex); } +#endif + +#endif