From d7f500169ea3350dd62a6bae52cd90af4556a6fd Mon Sep 17 00:00:00 2001 From: Peter M Date: Fri, 30 Jan 2026 13:18:46 +0100 Subject: [PATCH 01/10] 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 89d4ad9d93..8333364c55 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 fcd7fca321..1bd92cb5b5 100644 --- a/.github/workflows/build-libraries.yaml +++ b/.github/workflows/build-libraries.yaml @@ -19,6 +19,8 @@ jobs: runs-on: "ubuntu-24.04" strategy: fail-fast: false + matrix: + mbedtls: ["default", "mbedtls@4"] steps: - name: "Checkout repo" @@ -42,10 +44,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: | @@ -70,7 +88,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 @@ -130,7 +148,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 1a234c9cee..2a3a84b205 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 41492e6ba6..454ee38ddc 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; @@ -776,6 +782,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) @@ -861,6 +868,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 4ec0e9e527..e28de8de12 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 f1f8b1d6c2..2092ba437f 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 a2cd038832..587222d168 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 *code, size_t code_size) diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index 217009802b..bf8852a87b 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 60b9ebff98dc9c5e6a9856727fd90d2a4fa576eb Mon Sep 17 00:00:00 2001 From: Peter M Date: Tue, 17 Mar 2026 05:10:17 +0100 Subject: [PATCH 02/10] 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 0bf08cbae009f41df5f502bddfa16b894cca094f Mon Sep 17 00:00:00 2001 From: Peter M Date: Sat, 21 Mar 2026 14:57:44 +0100 Subject: [PATCH 03/10] 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 8e5783779a839ce426509ed448337e3a9623111a Mon Sep 17 00:00:00 2001 From: Peter M Date: Sun, 22 Mar 2026 22:59:14 +0100 Subject: [PATCH 04/10] 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 20d16d9e9c89a2a11adf5e417e5193eb9ff22787 Mon Sep 17 00:00:00 2001 From: Peter M Date: Thu, 26 Mar 2026 16:55:00 +0100 Subject: [PATCH 05/10] 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 --- 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 c7b2c8d988a20da4f81f071920362e7107e318b5 Mon Sep 17 00:00:00 2001 From: Peter M Date: Thu, 26 Mar 2026 17:03:43 +0100 Subject: [PATCH 06/10] 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 0b766db0dae6487f8c1d96f95c1385fb25edfa55 Mon Sep 17 00:00:00 2001 From: Peter M Date: Fri, 27 Mar 2026 08:59:22 +0100 Subject: [PATCH 07/10] 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 a7d841b2eae4ac8e268c499b2446b33db1ab5d79 Mon Sep 17 00:00:00 2001 From: Peter M Date: Thu, 26 Mar 2026 22:24:27 +0100 Subject: [PATCH 08/10] 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 6d0e22fe26..b895a629c1 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" @@ -587,13 +594,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 @@ -629,6 +649,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: | @@ -666,6 +699,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 }} .. @@ -673,6 +707,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 5b484c9d4aa7a608510620549a85b9fe7af028e7 Mon Sep 17 00:00:00 2001 From: Peter M Date: Tue, 31 Mar 2026 15:52:18 +0200 Subject: [PATCH 09/10] 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 | 60 ++++++++++++++----- CMakeModules/FetchMbedTLS.cmake | 4 +- .../emscripten/src/lib/CMakeLists.txt | 13 ++-- src/platforms/emscripten/src/lib/sys.c | 5 ++ 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/.github/workflows/wasm-build.yaml b/.github/workflows/wasm-build.yaml index f22c028416..1d26104fad 100644 --- a/.github/workflows/wasm-build.yaml +++ b/.github/workflows/wasm-build.yaml @@ -143,18 +143,29 @@ jobs: needs: [compile_tests, compile_tests_jit] runs-on: ubuntu-24.04 container: emscripten/emsdk - strategy: fail-fast: false matrix: - jit: ["", "-DAVM_DISABLE_JIT=OFF"] + include: + - jit: "" + mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - jit: "" + mbedtls_label: "mbedtls4" + mbedtls_git_tag: "v4.0.0" + - jit: "-DAVM_DISABLE_JIT=OFF" + mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - jit: "-DAVM_DISABLE_JIT=OFF" + 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 @@ -163,7 +174,7 @@ jobs: set -euo pipefail mkdir build cd build - emcmake cmake .. ${{ matrix.jit }} + emcmake cmake .. ${{ matrix.jit }} -DAVM_FETCH_MBEDTLS_GIT_TAG=${{ matrix.mbedtls_git_tag }} emmake make -j - name: Download AtomVM and test modules (non-JIT) @@ -210,7 +221,7 @@ jobs: node src/AtomVM.js ../../../../build/tests/libs/etest/test_etest.avm - name: "Rename and write sha256sum (node)" - if: startsWith(github.ref, 'refs/tags/') && matrix.jit == '' + if: startsWith(github.ref, 'refs/tags/') && matrix.jit == '' && matrix.mbedtls_label == 'mbedtls3' shell: bash working-directory: src/platforms/emscripten/build/src run: | @@ -223,7 +234,7 @@ jobs: - name: "Release (node)" uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') && matrix.jit == '' + if: startsWith(github.ref, 'refs/tags/') && matrix.jit == '' && matrix.mbedtls_label == 'mbedtls3' with: draft: true fail_on_unmatched_files: true @@ -244,15 +255,30 @@ jobs: strategy: fail-fast: false matrix: - jit: ["", "-DAVM_DISABLE_JIT=OFF"] - language: ["javascript-typescript"] + include: + - jit: "" + language: "javascript-typescript" + mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - jit: "" + language: "javascript-typescript" + mbedtls_label: "mbedtls4" + mbedtls_git_tag: "v4.0.0" + - jit: "-DAVM_DISABLE_JIT=OFF" + language: "javascript-typescript" + mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - jit: "-DAVM_DISABLE_JIT=OFF" + 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" if: matrix.jit == '' @@ -269,7 +295,7 @@ jobs: set -euo pipefail mkdir build cd build - emcmake cmake .. -DAVM_EMSCRIPTEN_ENV=web ${{ matrix.jit }} + emcmake cmake .. -DAVM_EMSCRIPTEN_ENV=web ${{ matrix.jit }} -DAVM_FETCH_MBEDTLS_GIT_TAG=${{ matrix.mbedtls_git_tag }} emmake make -j - name: "Perform CodeQL Analysis" @@ -280,7 +306,7 @@ jobs: if: matrix.jit == '' 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 @@ -289,6 +315,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 @@ -302,7 +334,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 @@ -337,7 +369,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: | @@ -350,7 +382,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 d4cc232f38..a9c26f9b24 100644 --- a/src/platforms/emscripten/src/lib/CMakeLists.txt +++ b/src/platforms/emscripten/src/lib/CMakeLists.txt @@ -52,19 +52,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 2a3a84b205..b44ca0aea3 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 From b151d9a4f0e52c120936b8ef605648f9e47f7690 Mon Sep 17 00:00:00 2001 From: Peter M Date: Wed, 15 Apr 2026 10:21:44 +0200 Subject: [PATCH 10/10] otp_crypto: address review feedback for mbedtls 4 support - Use psa_hash_algorithm_table instead of atom_to_psa_hash_alg() if-chain - Use psa_cipher_table instead of atom_to_psa_cipher_alg() if-chain - Cast to const uint8_t* in psa_hash_fold_fun - Remove unnecessary void* cast - Widen preprocessor guards for PSA table definitions Signed-off-by: Peter M --- src/libAtomVM/otp_crypto.c | 242 ++++++++++++------------------------- 1 file changed, 77 insertions(+), 165 deletions(-) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index 43079f3c13..e74ee4eb44 100644 --- a/src/libAtomVM/otp_crypto.c +++ b/src/libAtomVM/otp_crypto.c @@ -55,7 +55,7 @@ #ifdef HAVE_PSA_CRYPTO #include #endif -#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +#if defined(HAVE_PSA_CRYPTO) || MBEDTLS_VERSION_NUMBER >= 0x04000000 #include #endif @@ -98,7 +98,7 @@ #define MAX_MD_SIZE 64 -#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +#if defined(HAVE_PSA_CRYPTO) || MBEDTLS_VERSION_NUMBER >= 0x04000000 static void do_psa_init(void) { if (UNLIKELY(psa_crypto_init() != PSA_SUCCESS)) { @@ -293,38 +293,28 @@ 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; - } -#ifdef PSA_ALG_RIPEMD160 - if (type == globalcontext_make_atom(global, ATOM_STR("\x9", "ripemd160"))) { - return PSA_ALG_RIPEMD160; - } +#if defined(HAVE_PSA_CRYPTO) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +static const AtomStringIntPair psa_hash_algorithm_table[] = { + { ATOM_STR("\x3", "sha"), PSA_ALG_SHA_1 }, + { ATOM_STR("\x6", "sha224"), PSA_ALG_SHA_224 }, + { ATOM_STR("\x6", "sha256"), PSA_ALG_SHA_256 }, + { ATOM_STR("\x6", "sha384"), PSA_ALG_SHA_384 }, + { ATOM_STR("\x6", "sha512"), PSA_ALG_SHA_512 }, + { ATOM_STR("\x8", "sha3_224"), PSA_ALG_SHA3_224 }, + { ATOM_STR("\x8", "sha3_256"), PSA_ALG_SHA3_256 }, + { ATOM_STR("\x8", "sha3_384"), PSA_ALG_SHA3_384 }, + { ATOM_STR("\x8", "sha3_512"), PSA_ALG_SHA3_512 }, + { ATOM_STR("\x3", "md5"), PSA_ALG_MD5 }, + { ATOM_STR("\x9", "ripemd160"), PSA_ALG_RIPEMD160 }, + + SELECT_INT_DEFAULT(PSA_ALG_NONE) +}; #endif - return PSA_ALG_NONE; -} +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 static InteropFunctionResult psa_hash_fold_fun(term t, void *accum) { - psa_hash_operation_t *operation = (psa_hash_operation_t *) accum; + psa_hash_operation_t *operation = accum; if (term_is_integer(t)) { avm_int64_t tmp = term_maybe_unbox_int64(t); if (tmp < 0 || tmp > 255) { @@ -335,7 +325,7 @@ static InteropFunctionResult psa_hash_fold_fun(term t, void *accum) 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)) { + if (UNLIKELY(psa_hash_update(operation, (const uint8_t *) term_binary_data(t), term_binary_size(t)) != PSA_SUCCESS)) { return InteropBadArg; } } @@ -355,7 +345,7 @@ static term nif_crypto_hash(Context *ctx, int argc, term argv[]) #if MBEDTLS_VERSION_NUMBER >= 0x04000000 do_psa_init(); - psa_algorithm_t alg = atom_to_psa_hash_alg(type, ctx->global); + psa_algorithm_t alg = interop_atom_term_select_int(psa_hash_algorithm_table, type, ctx->global); if (alg == PSA_ALG_NONE) { TRACE("crypto:hash unknown algorithm\n"); RAISE_ERROR(BADARG_ATOM); @@ -509,72 +499,7 @@ 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 +#if MBEDTLS_VERSION_NUMBER < 0x04000000 static bool bool_to_mbedtls_operation(term encrypt_flag, mbedtls_operation_t *operation) { switch (encrypt_flag) { @@ -590,6 +515,54 @@ static bool bool_to_mbedtls_operation(term encrypt_flag, mbedtls_operation_t *op } #endif +#if defined(HAVE_PSA_CRYPTO) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +struct PsaCipherParams +{ + const char *atom_str; + psa_key_type_t key_type; + psa_algorithm_t algorithm; + uint16_t key_bits; + uint8_t block_size; + uint8_t iv_len; +}; + +static const struct PsaCipherParams psa_cipher_table[] = { + // ECB modes - block cipher, no IV + { ATOM_STR("\xB", "aes_128_ecb"), PSA_KEY_TYPE_AES, PSA_ALG_ECB_NO_PADDING, 128, 16, 0 }, + { ATOM_STR("\xB", "aes_192_ecb"), PSA_KEY_TYPE_AES, PSA_ALG_ECB_NO_PADDING, 192, 16, 0 }, + { ATOM_STR("\xB", "aes_256_ecb"), PSA_KEY_TYPE_AES, PSA_ALG_ECB_NO_PADDING, 256, 16, 0 }, + // CBC modes - block cipher, with IV + { ATOM_STR("\xB", "aes_128_cbc"), PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, 128, 16, 16 }, + { ATOM_STR("\xB", "aes_192_cbc"), PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, 192, 16, 16 }, + { ATOM_STR("\xB", "aes_256_cbc"), PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, 256, 16, 16 }, + // CFB128 modes - stream-like, with IV + { ATOM_STR("\xE", "aes_128_cfb128"), PSA_KEY_TYPE_AES, PSA_ALG_CFB, 128, 0, 16 }, + { ATOM_STR("\xE", "aes_192_cfb128"), PSA_KEY_TYPE_AES, PSA_ALG_CFB, 192, 0, 16 }, + { ATOM_STR("\xE", "aes_256_cfb128"), PSA_KEY_TYPE_AES, PSA_ALG_CFB, 256, 0, 16 }, + // CTR modes - stream cipher, with IV + { ATOM_STR("\xB", "aes_128_ctr"), PSA_KEY_TYPE_AES, PSA_ALG_CTR, 128, 0, 16 }, + { ATOM_STR("\xB", "aes_192_ctr"), PSA_KEY_TYPE_AES, PSA_ALG_CTR, 192, 0, 16 }, + { ATOM_STR("\xB", "aes_256_ctr"), PSA_KEY_TYPE_AES, PSA_ALG_CTR, 256, 0, 16 }, + // OFB modes - stream-like, with IV + { ATOM_STR("\xB", "aes_128_ofb"), PSA_KEY_TYPE_AES, PSA_ALG_OFB, 128, 0, 16 }, + { ATOM_STR("\xB", "aes_192_ofb"), PSA_KEY_TYPE_AES, PSA_ALG_OFB, 192, 0, 16 }, + { ATOM_STR("\xB", "aes_256_ofb"), PSA_KEY_TYPE_AES, PSA_ALG_OFB, 256, 0, 16 }, +}; + +#define PSA_CIPHER_TABLE_LEN (sizeof(psa_cipher_table) / sizeof(psa_cipher_table[0])) + +static const struct PsaCipherParams *psa_cipher_table_lookup(GlobalContext *glb, term cipher_atom) +{ + for (size_t i = 0; i < PSA_CIPHER_TABLE_LEN; i++) { + AtomString atom_str = (AtomString) psa_cipher_table[i].atom_str; + if (globalcontext_is_term_equal_to_atom_string(glb, cipher_atom, atom_str)) { + return &psa_cipher_table[i]; + } + } + return NULL; +} +#endif + static term make_crypto_error_tag( const char *file, int line, const char *message, term tag, Context *ctx) { @@ -642,12 +615,13 @@ static term nif_crypto_crypto_one_time(Context *ctx, int argc, term argv[]) #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); - if (UNLIKELY(alg == PSA_ALG_NONE)) { + const struct PsaCipherParams *cipher_params = psa_cipher_table_lookup(ctx->global, cipher_term); + if (IS_NULL_PTR(cipher_params)) { RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown cipher", ctx)); } + psa_key_type_t key_type = cipher_params->key_type; + size_t key_bits = cipher_params->key_bits; + psa_algorithm_t alg = cipher_params->algorithm; #else mbedtls_cipher_type_t cipher = interop_atom_term_select_int(cipher_table, cipher_term, ctx->global); @@ -1602,22 +1576,6 @@ static term nif_crypto_compute_key(Context *ctx, int argc, term argv[]) return result; } -static const AtomStringIntPair psa_hash_algorithm_table[] = { - { ATOM_STR("\x3", "sha"), PSA_ALG_SHA_1 }, - { ATOM_STR("\x6", "sha224"), PSA_ALG_SHA_224 }, - { ATOM_STR("\x6", "sha256"), PSA_ALG_SHA_256 }, - { ATOM_STR("\x6", "sha384"), PSA_ALG_SHA_384 }, - { ATOM_STR("\x6", "sha512"), PSA_ALG_SHA_512 }, - { ATOM_STR("\x8", "sha3_224"), PSA_ALG_SHA3_224 }, - { ATOM_STR("\x8", "sha3_256"), PSA_ALG_SHA3_256 }, - { ATOM_STR("\x8", "sha3_384"), PSA_ALG_SHA3_384 }, - { ATOM_STR("\x8", "sha3_512"), PSA_ALG_SHA3_512 }, - { ATOM_STR("\x3", "md5"), PSA_ALG_MD5 }, - { ATOM_STR("\x9", "ripemd160"), PSA_ALG_RIPEMD160 }, - - SELECT_INT_DEFAULT(PSA_ALG_NONE) -}; - #ifdef CRYPTO_SIGN_AVAILABLE static term nif_crypto_sign(Context *ctx, int argc, term argv[]) @@ -2699,52 +2657,6 @@ const ErlNifResourceTypeInit psa_cipher_op_resource_type_init = { .dtor = psa_cipher_op_dtor }; -struct PsaCipherParams -{ - const char *atom_str; - psa_key_type_t key_type; - psa_algorithm_t algorithm; - uint16_t key_bits; - uint8_t block_size; - uint8_t iv_len; -}; - -static const struct PsaCipherParams psa_cipher_table[] = { - // ECB modes - block cipher, no IV - { ATOM_STR("\xB", "aes_128_ecb"), PSA_KEY_TYPE_AES, PSA_ALG_ECB_NO_PADDING, 128, 16, 0 }, - { ATOM_STR("\xB", "aes_192_ecb"), PSA_KEY_TYPE_AES, PSA_ALG_ECB_NO_PADDING, 192, 16, 0 }, - { ATOM_STR("\xB", "aes_256_ecb"), PSA_KEY_TYPE_AES, PSA_ALG_ECB_NO_PADDING, 256, 16, 0 }, - // CBC modes - block cipher, with IV - { ATOM_STR("\xB", "aes_128_cbc"), PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, 128, 16, 16 }, - { ATOM_STR("\xB", "aes_192_cbc"), PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, 192, 16, 16 }, - { ATOM_STR("\xB", "aes_256_cbc"), PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, 256, 16, 16 }, - // CFB128 modes - stream-like, with IV - { ATOM_STR("\xE", "aes_128_cfb128"), PSA_KEY_TYPE_AES, PSA_ALG_CFB, 128, 0, 16 }, - { ATOM_STR("\xE", "aes_192_cfb128"), PSA_KEY_TYPE_AES, PSA_ALG_CFB, 192, 0, 16 }, - { ATOM_STR("\xE", "aes_256_cfb128"), PSA_KEY_TYPE_AES, PSA_ALG_CFB, 256, 0, 16 }, - // CTR modes - stream cipher, with IV - { ATOM_STR("\xB", "aes_128_ctr"), PSA_KEY_TYPE_AES, PSA_ALG_CTR, 128, 0, 16 }, - { ATOM_STR("\xB", "aes_192_ctr"), PSA_KEY_TYPE_AES, PSA_ALG_CTR, 192, 0, 16 }, - { ATOM_STR("\xB", "aes_256_ctr"), PSA_KEY_TYPE_AES, PSA_ALG_CTR, 256, 0, 16 }, - // OFB modes - stream-like, with IV - { ATOM_STR("\xB", "aes_128_ofb"), PSA_KEY_TYPE_AES, PSA_ALG_OFB, 128, 0, 16 }, - { ATOM_STR("\xB", "aes_192_ofb"), PSA_KEY_TYPE_AES, PSA_ALG_OFB, 192, 0, 16 }, - { ATOM_STR("\xB", "aes_256_ofb"), PSA_KEY_TYPE_AES, PSA_ALG_OFB, 256, 0, 16 }, -}; - -#define PSA_CIPHER_TABLE_LEN (sizeof(psa_cipher_table) / sizeof(psa_cipher_table[0])) - -static const struct PsaCipherParams *psa_cipher_table_lookup(GlobalContext *glb, term cipher_atom) -{ - for (size_t i = 0; i < PSA_CIPHER_TABLE_LEN; i++) { - AtomString atom_str = (AtomString) psa_cipher_table[i].atom_str; - if (globalcontext_is_term_equal_to_atom_string(glb, cipher_atom, atom_str)) { - return &psa_cipher_table[i]; - } - } - return NULL; -} - /* * Accepts: * - true / false (boolean shorthand for {encrypt, true/false}) @@ -3520,7 +3432,7 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) 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); + psa_algorithm_t hash_alg = interop_atom_term_select_int(psa_hash_algorithm_table, digest_type_term, glb); if (UNLIKELY(hash_alg == PSA_ALG_NONE)) { RAISE_ERROR(make_crypto_error(__FILE__, __LINE__, "Unknown digest type", ctx)); }