diff --git a/.github/workflows/build-and-test-macos.yaml b/.github/workflows/build-and-test-macos.yaml index 744dfbe4f0..6a9d0f644b 100644 --- a/.github/workflows/build-and-test-macos.yaml +++ b/.github/workflows/build-and-test-macos.yaml @@ -40,7 +40,7 @@ jobs: matrix: os: ["macos-14", "macos-15", "macos-15-intel", "macos-26"] otp: ["26", "27", "28"] - mbedtls: ["mbedtls@3"] + mbedtls: ["mbedtls@3", "mbedtls@4"] cmake_opts_other: [""] include: diff --git a/.github/workflows/build-and-test-on-freebsd.yaml b/.github/workflows/build-and-test-on-freebsd.yaml index f22718ecc2..b56311fc5f 100644 --- a/.github/workflows/build-and-test-on-freebsd.yaml +++ b/.github/workflows/build-and-test-on-freebsd.yaml @@ -35,7 +35,7 @@ concurrency: jobs: build-and-test-on-freebsd: runs-on: ubuntu-24.04 - name: Build and test AtomVM on FreeBSD + name: Build and test AtomVM on FreeBSD ${{ matrix.os_release }} (${{ matrix.mbedtls }}) env: ATOMVM_EXAMPLE: "atomvm-example" @@ -44,6 +44,10 @@ jobs: matrix: os_release: ["13.5", "14.3", "15.0"] + mbedtls: ["mbedtls@3"] + include: + - os_release: "14.3" + mbedtls: "mbedtls@4" steps: @@ -60,10 +64,25 @@ jobs: sync: rsync copyback: false + - name: "Use latest pkg repo for MbedTLS 4" + if: matrix.mbedtls == 'mbedtls@4' + shell: freebsd {0} + run: | + mkdir -p /usr/local/etc/pkg/repos + echo 'FreeBSD: { url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf + pkg update -f + - name: "Install deps" + if: matrix.mbedtls == 'mbedtls@3' + shell: freebsd {0} + run: | + pkg install -y curl cmake gperf erlang elixir rebar3 ninja mbedtls3 socat + + - name: "Install deps (MbedTLS 4)" + if: matrix.mbedtls == 'mbedtls@4' shell: freebsd {0} run: | - pkg install -y curl cmake gperf erlang elixir rebar3 mbedtls3 ninja socat + pkg install -y curl cmake gperf erlang elixir rebar3 ninja mbedtls4 socat - name: "Add hostname to /etc/hosts for distribution tests" shell: freebsd {0} @@ -102,6 +121,15 @@ jobs: mkdir build - name: "Build: run cmake" + if: matrix.mbedtls == 'mbedtls@3' + shell: freebsd {0} + run: | + cd $GITHUB_WORKSPACE; + cd build + cmake .. -DAVM_WARNINGS_ARE_ERRORS=ON + + - name: "Build: run cmake (MbedTLS 4)" + if: matrix.mbedtls == 'mbedtls@4' shell: freebsd {0} run: | cd $GITHUB_WORKSPACE; diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 3bee12be9d..40a63a3727 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -59,6 +59,7 @@ jobs: # We only test several OTP versions with default compilers for supported OSes (gcc 11, gcc 13, clang 14, clang 18) cc: ["gcc-11", "gcc-13", "clang-14", "clang-18"] otp: ["26", "27", "28"] + mbedtls: ["default"] include: ### gcc @@ -159,6 +160,12 @@ jobs: otp: "master" elixir_version: "main" + # Additional mbedtls@4 coverage with the default Linux toolchain + - cc: "cc" + cxx: "c++" + otp: "28" + mbedtls: "mbedtls@4" + # Additional latest & -Os compiler builds - cc: "gcc-14" cxx: "g++-14" @@ -540,13 +547,26 @@ jobs: run: sudo apt update -y - name: "Install deps" - if: matrix.container != '' - run: sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen valgrind libmbedtls-dev socat + if: matrix.container != '' && matrix.mbedtls != 'mbedtls@4' + run: | + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen valgrind libmbedtls-dev socat + + - name: "Install deps (MbedTLS 4)" + if: matrix.container != '' && matrix.mbedtls == 'mbedtls@4' + run: | + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen valgrind socat - name: "Install deps" - if: matrix.container == '' + if: matrix.container == '' && matrix.mbedtls != 'mbedtls@4' + run: | + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen libc6-dbg libmbedtls-dev socat + # Get a more recent valgrind + sudo snap install valgrind --classic + + - name: "Install deps (MbedTLS 4)" + if: matrix.container == '' && matrix.mbedtls == 'mbedtls@4' run: | - sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen libmbedtls-dev libc6-dbg socat + sudo apt install -y ${{ matrix.compiler_pkgs}} cmake gperf zlib1g-dev doxygen libc6-dbg socat # Get a more recent valgrind sudo snap install valgrind --classic @@ -582,6 +602,19 @@ jobs: https://repo.hex.pm https://cdn.jsdelivr.net/hex + - name: "Install specific MbedTLS version" + if: matrix.mbedtls == 'mbedtls@4' + run: | + git clone --depth 1 --branch mbedtls-4.0.0 --recurse-submodules https://github.com/Mbed-TLS/mbedtls + cd mbedtls + mkdir build + cd build + cmake -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=On -DCMAKE_INSTALL_PREFIX=/usr/local .. + make -j$(nproc) + sudo make install + sudo ldconfig + echo "MBEDTLS_ROOT_DIR=/usr/local" >> $GITHUB_ENV + # Builder info - name: "System info" run: | @@ -619,6 +652,7 @@ jobs: key: ${{ matrix.otp || env.DEFAULT_OTP_VERSION }}-${{ hashFiles('**/build-and-test.yaml', 'tests/**/*.erl', 'tests/**/*.hrl', 'tests/**/*.ex') }}-${{ matrix.jit_target_arch || 'nojit' }}-${{ contains(matrix.cmake_opts_other, 'AVM_DISABLE_JIT_DWARF=OFF') && 'dwarf' || 'nodwarf' }} - name: "Build: run cmake" + if: matrix.mbedtls != 'mbedtls@4' working-directory: build run: | cmake ${{ matrix.cmake_opts_fp }} ${{ matrix.cmake_opts_smp }} ${{ matrix.cmake_opts_other || env.DEFAULT_CMAKE_OPTS_OTHER }} .. @@ -626,6 +660,15 @@ jobs: # touch them so we can benefit from the cache and avoid costly beam file rebuild. find . -name '*.beam' -exec touch {} \; + - name: "Build: run cmake (MbedTLS 4)" + if: matrix.mbedtls == 'mbedtls@4' + working-directory: build + run: | + cmake -DMBEDTLS_ROOT_DIR=/usr/local ${{ matrix.cmake_opts_fp }} ${{ matrix.cmake_opts_smp }} ${{ matrix.cmake_opts_other || env.DEFAULT_CMAKE_OPTS_OTHER }} .. + # git clone will use more recent timestamps than cached beam files + # touch them so we can benefit from the cache and avoid costly beam file rebuild. + find . -name '*.beam' -exec touch {} \; + - name: "Build: run make" working-directory: build run: make -j3 diff --git a/.github/workflows/build-libraries.yaml b/.github/workflows/build-libraries.yaml index bb8894f83b..20eb26189d 100644 --- a/.github/workflows/build-libraries.yaml +++ b/.github/workflows/build-libraries.yaml @@ -19,6 +19,8 @@ jobs: runs-on: "ubuntu-22.04" strategy: fail-fast: false + matrix: + mbedtls: ["default", "mbedtls@4"] steps: - name: "Checkout repo" @@ -36,10 +38,26 @@ jobs: - name: "Install deps" run: | - sudo apt install -y build-essential cmake gperf zlib1g-dev libmbedtls-dev + sudo apt install -y build-essential cmake gperf zlib1g-dev + if [[ "${{ matrix.mbedtls }}" == "default" ]]; then + sudo apt install -y libmbedtls-dev + fi # Get a more recent valgrind sudo snap install valgrind --classic + - name: "Install specific MbedTLS version" + if: matrix.mbedtls == 'mbedtls@4' + run: | + git clone --depth 1 --branch mbedtls-4.0.0 --recurse-submodules https://github.com/Mbed-TLS/mbedtls + cd mbedtls + mkdir build + cd build + cmake -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=On -DCMAKE_INSTALL_PREFIX=/usr/local .. + make -j$(nproc) + sudo make install + sudo ldconfig + echo "MBEDTLS_ROOT_DIR=/usr/local" >> $GITHUB_ENV + # Builder info - name: "System info" run: | @@ -64,7 +82,7 @@ jobs: - name: "Build: run cmake" working-directory: build run: | - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. + cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ${MBEDTLS_ROOT_DIR:+-DMBEDTLS_ROOT_DIR=$MBEDTLS_ROOT_DIR} .. - name: "Build: run make" working-directory: build @@ -124,7 +142,7 @@ jobs: - name: Release uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls == 'default' with: draft: true fail_on_unmatched_files: true diff --git a/.github/workflows/wasm-build.yaml b/.github/workflows/wasm-build.yaml index a3be9405cb..14a6d8826e 100644 --- a/.github/workflows/wasm-build.yaml +++ b/.github/workflows/wasm-build.yaml @@ -107,13 +107,21 @@ jobs: needs: compile_tests runs-on: ubuntu-24.04 container: emscripten/emsdk + strategy: + fail-fast: false + matrix: + include: + - mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - mbedtls_label: "mbedtls4" + mbedtls_git_tag: "v4.0.0" steps: - name: Checkout repo uses: actions/checkout@v4 - name: "Install deps" - run: sudo apt update -y && sudo apt install -y cmake gperf + run: sudo apt update -y && sudo apt install -y cmake gperf python3-jinja2 python3-jsonschema - name: Build shell: bash @@ -122,7 +130,7 @@ jobs: set -euo pipefail mkdir build cd build - emcmake cmake .. + emcmake cmake .. -DAVM_FETCH_MBEDTLS_GIT_TAG=${{ matrix.mbedtls_git_tag }} emmake make -j - name: Download AtomVM and test modules @@ -147,7 +155,7 @@ jobs: node src/AtomVM.js ../../../../build/tests/erlang_tests/test_crypto.beam - name: "Rename and write sha256sum (node)" - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' shell: bash working-directory: src/platforms/emscripten/build/src run: | @@ -160,7 +168,7 @@ jobs: - name: "Release (node)" uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' with: draft: true fail_on_unmatched_files: true @@ -181,14 +189,20 @@ jobs: strategy: fail-fast: false matrix: - language: ["javascript-typescript"] + include: + - language: "javascript-typescript" + mbedtls_label: "mbedtls3" + mbedtls_git_tag: "v3.6.3.1" + - language: "javascript-typescript" + mbedtls_label: "mbedtls4" + mbedtls_git_tag: "v4.0.0" steps: - name: Checkout repo uses: actions/checkout@v4 - name: "Install deps" - run: sudo apt update -y && sudo apt install -y cmake gperf + run: sudo apt update -y && sudo apt install -y cmake gperf python3-jinja2 python3-jsonschema - name: "Initialize CodeQL" uses: github/codeql-action/init@v3 @@ -204,7 +218,7 @@ jobs: set -euo pipefail mkdir build cd build - emcmake cmake .. -DAVM_EMSCRIPTEN_ENV=web + emcmake cmake .. -DAVM_EMSCRIPTEN_ENV=web -DAVM_FETCH_MBEDTLS_GIT_TAG=${{ matrix.mbedtls_git_tag }} emmake make -j - name: "Perform CodeQL Analysis" @@ -213,7 +227,7 @@ jobs: - name: Upload wasm build for web uses: actions/upload-artifact@v4 with: - name: atomvm-js-web + name: atomvm-js-web-${{ matrix.mbedtls_label }} path: | src/platforms/emscripten/build/**/*.wasm src/platforms/emscripten/build/**/*.js @@ -222,6 +236,12 @@ jobs: wasm_test_web: needs: [compile_tests, wasm_build_web] runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - mbedtls_label: "mbedtls3" + - mbedtls_label: "mbedtls4" steps: - name: Checkout repo uses: actions/checkout@v4 @@ -235,7 +255,7 @@ jobs: - name: Download wasm build for web uses: actions/download-artifact@v4 with: - name: atomvm-js-web + name: atomvm-js-web-${{ matrix.mbedtls_label }} path: src/platforms/emscripten/build - name: Download emscripten test modules @@ -270,7 +290,7 @@ jobs: retention-days: 7 - name: "Rename and write sha256sum (web)" - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' shell: bash working-directory: src/platforms/emscripten/build/src run: | @@ -283,7 +303,7 @@ jobs: - name: "Release (web)" uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/') && matrix.mbedtls_label == 'mbedtls3' with: draft: true fail_on_unmatched_files: true diff --git a/CMakeModules/FetchMbedTLS.cmake b/CMakeModules/FetchMbedTLS.cmake index 81154b4b86..c53405b8f0 100644 --- a/CMakeModules/FetchMbedTLS.cmake +++ b/CMakeModules/FetchMbedTLS.cmake @@ -20,10 +20,12 @@ include(FetchContent) +set(AVM_FETCH_MBEDTLS_GIT_TAG "v3.6.3.1" CACHE STRING "MbedTLS git tag to fetch for Emscripten builds") + FetchContent_Declare( mbedtls GIT_REPOSITORY http://github.com/mbed-TLS/mbedtls.git - GIT_TAG v3.6.3.1 + GIT_TAG ${AVM_FETCH_MBEDTLS_GIT_TAG} GIT_SHALLOW 1 ) diff --git a/src/libAtomVM/otp_crypto.c b/src/libAtomVM/otp_crypto.c index 224c93a432..43079f3c13 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,10 +54,12 @@ #ifdef HAVE_PSA_CRYPTO #include +#endif +#if defined(HAVE_PSA_CRYPTO) || defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 #include #endif -#ifdef MBEDTLS_PKCS5_C +#if MBEDTLS_VERSION_NUMBER < 0x04000000 && defined(MBEDTLS_PKCS5_C) #include #include #endif @@ -63,13 +68,16 @@ #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 #include #define AVM_HAVE_MBEDTLS_CT_MEMCMP 1 #endif - // #define ENABLE_TRACE #include "trace.h" @@ -90,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, @@ -101,6 +118,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 +128,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 +244,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 +290,57 @@ 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; + } +#ifdef PSA_ALG_RIPEMD160 + if (type == globalcontext_make_atom(global, ATOM_STR("\x9", "ripemd160"))) { + return PSA_ALG_RIPEMD160; + } +#endif + 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 +353,34 @@ 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 + 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"); + 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 +428,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 +436,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 +458,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) { @@ -384,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; } @@ -407,6 +509,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 +588,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 +639,22 @@ 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); + 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 +688,171 @@ 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; + size_t output_size = 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); + 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); + error_atom = make_crypto_error(__FILE__, __LINE__, err_msg, ctx); + goto psa_error; + } + + 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 ? output_size : 1); + 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); + secure_free(temp_buf, output_size); + secure_free(allocated_key_data, key_len); + secure_free(allocated_iv_data, iv_len); + 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); + } + 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); + 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)) { + 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); + secure_free(temp_buf, output_size); + return out; + +psa_error: + psa_cipher_abort(&operation); + if (key_id != 0) { + psa_destroy_key(key_id); + } + secure_free(temp_buf, output_size); + goto raise_error; +#else mbedtls_operation_t operation; mbedtls_cipher_padding_t padding = MBEDTLS_PADDING_NONE; bool padding_has_been_set = false; @@ -625,6 +970,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); + secure_free(allocated_data_data, data_size); + RAISE_ERROR(error_atom); +#endif } #ifdef HAVE_PSA_CRYPTO @@ -712,13 +1066,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. @@ -1369,7 +1716,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; @@ -1377,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); @@ -1432,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); @@ -1554,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); @@ -1601,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); @@ -1655,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; @@ -1722,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); @@ -1762,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); @@ -1797,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 @@ -1808,7 +2160,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); @@ -1964,23 +2319,33 @@ 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)); } 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; + 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)); } + SMP_MUTEX_UNLOCK(mac_state->mutex); + secure_free(maybe_allocated_data, data_len); return argv[0]; } @@ -2000,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; @@ -2017,6 +2386,9 @@ 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; + mac_state->finalized = true; SMP_MUTEX_UNLOCK(mac_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); @@ -2049,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)); @@ -2076,6 +2452,9 @@ 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; + mac_state->finalized = true; SMP_MUTEX_UNLOCK(mac_state->mutex); if (UNLIKELY(status != PSA_SUCCESS)) { result = make_crypto_error(__FILE__, __LINE__, "Unexpected error", ctx); @@ -2304,7 +2683,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); @@ -2611,12 +2993,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); @@ -2630,7 +3013,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 */ } @@ -2644,11 +3027,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; @@ -2659,8 +3047,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); @@ -2714,6 +3102,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) { @@ -2760,7 +3150,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); @@ -2841,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; @@ -2916,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); @@ -2996,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; } @@ -3004,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: @@ -3037,8 +3431,8 @@ static term nif_crypto_crypto_one_time_aead(Context *ctx, int argc, term argv[]) cleanup: psa_destroy_key(key_id); - free(maybe_allocated_intext); - free(maybe_allocated_aad); + secure_free(maybe_allocated_intext, intext_len); + secure_free(maybe_allocated_aad, aad_len); secure_free(out_buf, out_buf_size); if (UNLIKELY(!success)) { @@ -3091,7 +3485,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 }, @@ -3103,6 +3498,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[]) { @@ -3113,12 +3509,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; @@ -3129,6 +3519,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 @@ -3154,6 +3557,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))) { @@ -3168,6 +3576,48 @@ static term nif_crypto_pbkdf2_hmac(Context *ctx, int argc, term argv[]) goto cleanup; } +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + do_psa_init(); + psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + + 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); + 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( @@ -3192,6 +3642,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; @@ -3231,6 +3682,18 @@ term nif_crypto_strong_rand_bytes(Context *ctx, int argc, term argv[]) RAISE_ERROR(OUT_OF_MEMORY_ATOM); } +#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); + + 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 +3709,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) @@ -3420,7 +3884,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 @@ -3533,7 +3997,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; diff --git a/src/libAtomVM/otp_ssl.c b/src/libAtomVM/otp_ssl.c index b74d2cb8af..5bdf1a337f 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,16 @@ 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 + // mbedtls 4.x uses PSA for randomness; no explicit RNG configuration needed. + 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/CMakeLists.txt b/src/platforms/emscripten/src/lib/CMakeLists.txt index c91b0c87d9..e87ad049c4 100644 --- a/src/platforms/emscripten/src/lib/CMakeLists.txt +++ b/src/platforms/emscripten/src/lib/CMakeLists.txt @@ -47,19 +47,24 @@ target_compile_features(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC c_std_11) target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC libAtomVM) target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC ATOMVM_HAS_MBEDTLS) -target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC MbedTLS::mbedcrypto) +target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC MbedTLS::mbedtls) include(CheckCSourceCompiles) -get_target_property(_mbedcrypto_includes MbedTLS::mbedcrypto INTERFACE_INCLUDE_DIRECTORIES) -set(CMAKE_REQUIRED_INCLUDES ${_mbedcrypto_includes}) +get_target_property(_mbedtls_includes MbedTLS::mbedtls INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_REQUIRED_INCLUDES ${_mbedtls_includes}) +set(_avm_try_compile_target_type ${CMAKE_TRY_COMPILE_TARGET_TYPE}) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) check_c_source_compiles(" #include #ifndef MBEDTLS_PSA_CRYPTO_C #error PSA Crypto not available #endif + #include int main(void) { return 0; } " HAVE_PSA_CRYPTO) +set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_avm_try_compile_target_type}) unset(CMAKE_REQUIRED_INCLUDES) -unset(_mbedcrypto_includes) +unset(_avm_try_compile_target_type) +unset(_mbedtls_includes) if (HAVE_PSA_CRYPTO) target_compile_definitions(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC HAVE_PSA_CRYPTO) endif() diff --git a/src/platforms/emscripten/src/lib/emscripten_sys.h b/src/platforms/emscripten/src/lib/emscripten_sys.h index 1889b2ab97..68555d7050 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 "sys_mbedtls.h" +#include + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include - -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include #endif -#include "sys_mbedtls.h" - struct PromiseResource { em_promise_t promise; @@ -117,6 +114,7 @@ struct EmscriptenPlatformData ErlNifResourceType *htmlevent_user_data_resource_type; ErlNifResourceType *websocket_resource_type; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif @@ -128,6 +126,7 @@ struct EmscriptenPlatformData #endif mbedtls_ctr_drbg_context random_ctx; bool random_is_initialized; +#endif }; void sys_enqueue_emscripten_cast_message(GlobalContext *glb, const char *target, const char *message); diff --git a/src/platforms/emscripten/src/lib/sys.c b/src/platforms/emscripten/src/lib/sys.c index ab8c0404b7..0884468050 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); } @@ -760,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 @@ -826,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 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 diff --git a/src/platforms/esp32/components/avm_sys/include/esp32_sys.h b/src/platforms/esp32/components/avm_sys/include/esp32_sys.h index e38368d3db..becb45e1c9 100644 --- a/src/platforms/esp32/components/avm_sys/include/esp32_sys.h +++ b/src/platforms/esp32/components/avm_sys/include/esp32_sys.h @@ -30,8 +30,15 @@ #include #endif +// Include version.h to get MBEDTLS_VERSION_NUMBER (available in all versions) +#include +#if defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include +#endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include +#endif #include #include @@ -72,13 +79,17 @@ struct ESP32PlatformData #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_entropy_context entropy_ctx; +#endif bool entropy_is_initialized; #ifndef AVM_NO_SMP Mutex *random_mutex; #endif +#if MBEDTLS_VERSION_NUMBER < 0x04000000 mbedtls_ctr_drbg_context random_ctx; +#endif bool random_is_initialized; #ifdef CONFIG_AVM_ENABLE_STORAGE_NIFS diff --git a/src/platforms/esp32/components/avm_sys/platform_nifs.c b/src/platforms/esp32/components/avm_sys/platform_nifs.c index 3db18b6c53..8573a6efe7 100644 --- a/src/platforms/esp32/components/avm_sys/platform_nifs.c +++ b/src/platforms/esp32/components/avm_sys/platform_nifs.c @@ -42,11 +42,17 @@ #include #include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include #include #include #include +#endif +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include +#endif + #include #include diff --git a/src/platforms/esp32/components/avm_sys/sys.c b/src/platforms/esp32/components/avm_sys/sys.c index bd2eab6841..8bd4acdd2f 100644 --- a/src/platforms/esp32/components/avm_sys/sys.c +++ b/src/platforms/esp32/components/avm_sys/sys.c @@ -29,6 +29,8 @@ #include "otp_socket.h" #include "scheduler.h" #include "utils.h" +#include +#include // #define ENABLE_TRACE #include "trace.h" @@ -57,11 +59,7 @@ #include "soc/soc_caps.h" #endif -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include -#endif +#include // Platform uses listeners #include "listeners.h" @@ -285,6 +283,13 @@ void sys_init_platform(GlobalContext *glb) platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#if defined(MBEDTLS_PSA_CRYPTO_C) || MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#endif + ErlNifResourceFlags flags; ErlNifEnv env; erl_nif_env_partial_init_from_globalcontext(&env, glb); @@ -310,7 +315,7 @@ void sys_free_platform(GlobalContext *glb) AVM_ABORT(); } } - +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } @@ -318,6 +323,7 @@ void sys_free_platform(GlobalContext *glb) if (platform->entropy_is_initialized) { mbedtls_entropy_free(&platform->entropy_ctx); } +#endif #ifndef AVM_NO_SMP smp_mutex_destroy(platform->entropy_mutex); @@ -346,7 +352,7 @@ const void *esp32_sys_mmap_partition(const char *partition_name, spi_flash_mmap_ ESP_LOGE(TAG, "Failed to map BEAM partition for %s", partition_name); return NULL; } - ESP_LOGI(TAG, "Loaded BEAM partition %s at address 0x%"PRIx32" (size=%"PRIu32" bytes)", + ESP_LOGI(TAG, "Loaded BEAM partition %s at address 0x%" PRIx32 " (size=%" PRIu32 " bytes)", partition_name, partition->address, partition->size); return mapped_memory; @@ -773,6 +779,7 @@ term esp_err_to_term(GlobalContext *glb, esp_err_t status) } } +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #if !defined(MBEDTLS_THREADING_C) && !defined(AVM_NO_SMP) @@ -858,6 +865,7 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) UNUSED(global); #endif } +#endif #ifndef AVM_NO_JIT #include diff --git a/src/platforms/generic_unix/lib/CMakeLists.txt b/src/platforms/generic_unix/lib/CMakeLists.txt index e5734eaeef..8cc0d18114 100644 --- a/src/platforms/generic_unix/lib/CMakeLists.txt +++ b/src/platforms/generic_unix/lib/CMakeLists.txt @@ -87,7 +87,7 @@ if (MbedTLS_FOUND) set(CMAKE_REQUIRED_INCLUDES "${MBEDTLS_ROOT_DIR}/include") endif() include(CheckCSourceCompiles) - set(CMAKE_REQUIRED_LIBRARIES MbedTLS::mbedcrypto) + set(CMAKE_REQUIRED_LIBRARIES MbedTLS::mbedtls) check_c_source_compiles(" #include #ifndef MBEDTLS_PSA_CRYPTO_C diff --git a/src/platforms/generic_unix/lib/sys.c b/src/platforms/generic_unix/lib/sys.c index d88ff54f18..ba7646e4c8 100644 --- a/src/platforms/generic_unix/lib/sys.c +++ b/src/platforms/generic_unix/lib/sys.c @@ -32,13 +32,10 @@ #include "utils.h" #if ATOMVM_HAS_MBEDTLS +#include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include - -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include #endif #include "otp_ssl.h" @@ -114,6 +111,7 @@ struct GenericUnixPlatformData #endif #ifdef ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif @@ -126,6 +124,7 @@ struct GenericUnixPlatformData mbedtls_ctr_drbg_context random_ctx; bool random_is_initialized; #endif +#endif }; static void mapped_file_avm_pack_destructor(struct AVMPackData *obj, GlobalContext *global); @@ -579,6 +578,12 @@ void sys_init_platform(GlobalContext *global) otp_net_init(global); otp_socket_init(global); #if ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#else #ifndef AVM_NO_SMP platform->entropy_mutex = smp_mutex_create(); if (IS_NULL_PTR(platform->entropy_mutex)) { @@ -591,6 +596,7 @@ void sys_init_platform(GlobalContext *global) #endif platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#endif otp_ssl_init(global); #endif #ifndef AVM_NO_JIT @@ -624,11 +630,14 @@ void sys_free_platform(GlobalContext *global) #endif #if !defined(AVM_NO_SMP) && ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 smp_mutex_destroy(platform->entropy_mutex); smp_mutex_destroy(platform->random_mutex); #endif +#endif #if ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } @@ -636,6 +645,7 @@ void sys_free_platform(GlobalContext *global) if (platform->entropy_is_initialized) { mbedtls_entropy_free(&platform->entropy_ctx); } +#endif #endif free(platform); @@ -745,6 +755,7 @@ bool event_listener_is_event(EventListener *listener, listener_event_t event) } #ifdef ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #ifndef MBEDTLS_THREADING_C @@ -811,6 +822,7 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) struct GenericUnixPlatformData *platform = global->platform_data; SMP_MUTEX_UNLOCK(platform->random_mutex); } +#endif #endif diff --git a/src/platforms/rp2/src/lib/rp2_sys.h b/src/platforms/rp2/src/lib/rp2_sys.h index a7cd4413bc..7d5499a1b4 100644 --- a/src/platforms/rp2/src/lib/rp2_sys.h +++ b/src/platforms/rp2/src/lib/rp2_sys.h @@ -30,8 +30,11 @@ #include #include +#include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include +#endif #pragma GCC diagnostic pop @@ -82,6 +85,7 @@ struct RP2PlatformData #endif queue_t event_queue; +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP Mutex *entropy_mutex; #endif @@ -93,6 +97,7 @@ struct RP2PlatformData #endif mbedtls_ctr_drbg_context random_ctx; bool random_is_initialized; +#endif }; #endif diff --git a/src/platforms/rp2/src/lib/sys.c b/src/platforms/rp2/src/lib/sys.c index ac4c9c717c..c15c24e4eb 100644 --- a/src/platforms/rp2/src/lib/sys.c +++ b/src/platforms/rp2/src/lib/sys.c @@ -43,10 +43,9 @@ #include #endif -#if defined(MBEDTLS_VERSION_NUMBER) && (MBEDTLS_VERSION_NUMBER >= 0x03000000) -#include -#else -#include +#include +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 +#include #endif // libAtomVM @@ -92,6 +91,14 @@ void sys_init_platform(GlobalContext *glb) otp_socket_init(glb); #endif +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (UNLIKELY(status != PSA_SUCCESS)) { + AVM_ABORT(); + } +#endif + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #ifndef AVM_NO_SMP platform->entropy_mutex = smp_mutex_create(); if (IS_NULL_PTR(platform->entropy_mutex)) { @@ -105,6 +112,7 @@ void sys_init_platform(GlobalContext *glb) platform->entropy_is_initialized = false; platform->random_is_initialized = false; +#endif } void sys_free_platform(GlobalContext *glb) @@ -116,6 +124,7 @@ void sys_free_platform(GlobalContext *glb) struct RP2PlatformData *platform = glb->platform_data; queue_free(&platform->event_queue); +#if MBEDTLS_VERSION_NUMBER < 0x04000000 if (platform->random_is_initialized) { mbedtls_ctr_drbg_free(&platform->random_ctx); } @@ -127,6 +136,7 @@ void sys_free_platform(GlobalContext *glb) #ifndef AVM_NO_SMP smp_mutex_destroy(platform->entropy_mutex); smp_mutex_destroy(platform->random_mutex); +#endif #endif free(platform); @@ -408,6 +418,7 @@ void sys_unregister_listener_from_event(GlobalContext *global, listener_event_t // TODO: enable mbedtls threading support by defining MBEDTLS_THREADING_ALT // and remove this function. +#if MBEDTLS_VERSION_NUMBER < 0x04000000 int sys_mbedtls_entropy_func(void *entropy, unsigned char *buf, size_t size) { #ifndef MBEDTLS_THREADING_C @@ -474,6 +485,7 @@ void sys_mbedtls_ctr_drbg_context_unlock(GlobalContext *global) struct RP2PlatformData *platform = global->platform_data; SMP_MUTEX_UNLOCK(platform->random_mutex); } +#endif #ifndef AVM_NO_JIT ModuleNativeEntryPoint sys_map_native_code(const uint8_t *native_code, size_t size, size_t offset) diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index 9471abf063..81556f2466 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -26,6 +26,9 @@ #include #include #include +#if ATOMVM_HAS_MBEDTLS +#include +#endif // #define ENABLE_TRACE #include @@ -190,6 +193,14 @@ void sys_init_platform(GlobalContext *glb) AVM_LOGE(TAG, "Out of memory!"); AVM_ABORT(); } +#if ATOMVM_HAS_MBEDTLS +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + AVM_ABORT(); + } +#endif +#endif glb->platform_data = platform; list_init(&platform->locked_pins); }