From 82f33260d72f2bc8a2ea3d6940ba7814de69e260 Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Mon, 1 Jun 2026 17:50:09 +0530 Subject: [PATCH 1/3] mbedtls: add mbedTLS 4 / PSA Crypto support (WIP, gated) Adds a parallel PSA Crypto code path for the mbedTLS backend, gated under `#if MBEDTLS_VERSION_MAJOR >= 4`. The legacy mbedTLS 3 path stays unchanged and remains the default. Covers crypto/cipher/aes_icm_mbedtls.c, aes_gcm_mbedtls.c, crypto/hash/hmac_mbedtls.c, the matching struct headers, plus cmake/FindMbedTLS.cmake (additionally finds libtfpsacrypto) and meson.build. CI matrix adds an mbedtls4 entry that builds mbedtls-4.0.0 from source on Ubuntu + macOS. Local: tested against system mbedTLS 3.6.4 (brew) and mbedTLS 4.0.0 (built from source), 10/10 ctest passing on both. For cisco/libsrtp#812. --- .github/workflows/cmake.yml | 33 +++- .github/workflows/meson.yml | 33 +++- cmake/FindMbedTLS.cmake | 11 +- crypto/cipher/aes_gcm_mbedtls.c | 327 +++++++++++++++++++++++++++++--- crypto/cipher/aes_icm_mbedtls.c | 145 +++++++++++++- crypto/hash/hmac_mbedtls.c | 159 +++++++++++++++- crypto/include/aes_gcm.h | 26 +++ crypto/include/aes_icm_ext.h | 23 +++ meson.build | 8 + 9 files changed, 723 insertions(+), 42 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ad470109a..749bc1e1b 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - crypto: [internal, openssl, openssl3, nss, mbedtls] + crypto: [internal, openssl, openssl3, nss, mbedtls, mbedtls4] exclude: - os: windows-latest crypto: openssl @@ -24,6 +24,8 @@ jobs: crypto: nss - os: windows-latest crypto: mbedtls + - os: windows-latest + crypto: mbedtls4 - os: ubuntu-latest crypto: openssl3 include: @@ -37,6 +39,8 @@ jobs: cmake-crypto-enable: "-DENABLE_NSS=ON" - crypto: mbedtls cmake-crypto-enable: "-DENABLE_MBEDTLS=ON" + - crypto: mbedtls4 + cmake-crypto-enable: "-DENABLE_MBEDTLS=ON" runs-on: ${{ matrix.os }} @@ -54,6 +58,19 @@ jobs: if: matrix.os == 'ubuntu-latest' && matrix.crypto == 'mbedtls' run: sudo apt-get install libmbedtls-dev + - name: Setup Ubuntu MbedTLS 4 + if: matrix.os == 'ubuntu-latest' && matrix.crypto == 'mbedtls4' + run: | + python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 + git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src + cd mbedtls4-src + git checkout mbedtls-4.0.0 + git submodule update --init --recursive + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF + cmake --build build -j + sudo cmake --install build + cd .. + - name: Setup macOS OpenSSL if: matrix.os == 'macos-latest' && matrix.crypto == 'openssl' run: echo "cmake-crypto-dir=-DOPENSSL_ROOT_DIR=$(brew --prefix openssl@1.1)" >> $GITHUB_ENV @@ -74,6 +91,20 @@ jobs: brew install mbedtls@3 echo "CMAKE_PREFIX_PATH=$(brew --prefix mbedtls@3)" >> $GITHUB_ENV + - name: Setup macOS MbedTLS 4 + if: matrix.os == 'macos-latest' && matrix.crypto == 'mbedtls4' + run: | + python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 + git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src + cd mbedtls4-src + git checkout mbedtls-4.0.0 + git submodule update --init --recursive + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/mbedtls4-prefix" + cmake --build build -j + cmake --install build + echo "CMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/mbedtls4-prefix" >> $GITHUB_ENV + cd .. + - uses: actions/checkout@v2 - name: Create Build Environment diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index 988eccbff..cbdd11e44 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - crypto: [internal, openssl, openssl3, nss, mbedtls] + crypto: [internal, openssl, openssl3, nss, mbedtls, mbedtls4] exclude: - os: windows-latest crypto: openssl @@ -24,6 +24,8 @@ jobs: crypto: nss - os: windows-latest crypto: mbedtls + - os: windows-latest + crypto: mbedtls4 - os: ubuntu-latest crypto: openssl3 include: @@ -37,6 +39,8 @@ jobs: meson-crypto-enable: "-Dcrypto-library=nss" - crypto: mbedtls meson-crypto-enable: "-Dcrypto-library=mbedtls" + - crypto: mbedtls4 + meson-crypto-enable: "-Dcrypto-library=mbedtls" runs-on: ${{ matrix.os }} @@ -71,6 +75,19 @@ jobs: if: matrix.os == 'ubuntu-latest' && matrix.crypto == 'mbedtls' run: sudo apt-get install libmbedtls-dev + - name: Setup Ubuntu MbedTLS 4 + if: matrix.os == 'ubuntu-latest' && matrix.crypto == 'mbedtls4' + run: | + python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 + git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src + cd mbedtls4-src + git checkout mbedtls-4.0.0 + git submodule update --init --recursive + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF + cmake --build build -j + sudo cmake --install build + cd .. + - name: Setup macOS OpenSSL if: matrix.os == 'macos-latest' && matrix.crypto == 'openssl' run: echo "PKG_CONFIG_PATH=$(brew --prefix openssl@1.1)/lib/pkgconfig" >> $GITHUB_ENV @@ -91,6 +108,20 @@ jobs: brew install mbedtls@3 echo "PKG_CONFIG_PATH=$(brew --prefix mbedtls@3)/lib/pkgconfig" >> $GITHUB_ENV + - name: Setup macOS MbedTLS 4 + if: matrix.os == 'macos-latest' && matrix.crypto == 'mbedtls4' + run: | + python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 + git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src + cd mbedtls4-src + git checkout mbedtls-4.0.0 + git submodule update --init --recursive + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/mbedtls4-prefix" + cmake --build build -j + cmake --install build + echo "PKG_CONFIG_PATH=$GITHUB_WORKSPACE/mbedtls4-prefix/lib/pkgconfig" >> $GITHUB_ENV + cd .. + - uses: actions/checkout@v2 - name: Create Build Environment diff --git a/cmake/FindMbedTLS.cmake b/cmake/FindMbedTLS.cmake index a6e8a365e..384a84a60 100644 --- a/cmake/FindMbedTLS.cmake +++ b/cmake/FindMbedTLS.cmake @@ -4,13 +4,22 @@ find_library(MBEDTLS_LIBRARY mbedtls) find_library(MBEDX509_LIBRARY mbedx509) find_library(MBEDCRYPTO_LIBRARY mbedcrypto) +# mbedTLS 4.x splits the PSA Crypto implementation into its own library +# (libtfpsacrypto). libmbedcrypto remains as a thin wrapper but the actual +# psa_* symbols live in tfpsacrypto, so we must link it when present. Older +# 3.x installs do not ship this library; the find is best-effort. +find_library(MBEDTFPSACRYPTO_LIBRARY tfpsacrypto) + set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}") +if(MBEDTFPSACRYPTO_LIBRARY) + list(APPEND MBEDTLS_LIBRARIES "${MBEDTFPSACRYPTO_LIBRARY}") +endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MbedTLS DEFAULT_MSG MBEDTLS_LIBRARY MBEDTLS_INCLUDE_DIRS MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) -mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) +mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY MBEDTFPSACRYPTO_LIBRARY) if(NOT TARGET MbedTLS) message("in mbedtls ${MBEDTLS_LIBRARY}") diff --git a/crypto/cipher/aes_gcm_mbedtls.c b/crypto/cipher/aes_gcm_mbedtls.c index 05b83804b..5bd15d4f6 100644 --- a/crypto/cipher/aes_gcm_mbedtls.c +++ b/crypto/cipher/aes_gcm_mbedtls.c @@ -46,7 +46,12 @@ #ifdef HAVE_CONFIG_H #include #endif +#include +#if MBEDTLS_VERSION_MAJOR >= 4 +#include +#else #include +#endif #include "aes_gcm.h" #include "alloc.h" #include "err.h" /* for srtp_debug */ @@ -133,6 +138,18 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_alloc(srtp_cipher_t **c, return (srtp_err_status_alloc_fail); } +#if MBEDTLS_VERSION_MAJOR >= 4 + gcm->ctx = + (psa_aes_gcm_ctx_t *)srtp_crypto_alloc(sizeof(psa_aes_gcm_ctx_t)); + if (gcm->ctx == NULL) { + srtp_crypto_free(gcm); + srtp_crypto_free(*c); + *c = NULL; + return srtp_err_status_alloc_fail; + } + gcm->ctx->key_id = PSA_KEY_ID_NULL; + gcm->ctx->op = psa_aead_operation_init(); +#else gcm->ctx = (mbedtls_gcm_context *)srtp_crypto_alloc(sizeof(mbedtls_gcm_context)); if (gcm->ctx == NULL) { @@ -142,6 +159,7 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_alloc(srtp_cipher_t **c, return srtp_err_status_alloc_fail; } mbedtls_gcm_init(gcm->ctx); +#endif /* set pointers */ (*c)->state = gcm; @@ -177,7 +195,12 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_dealloc(srtp_cipher_t *c) FUNC_ENTRY(); ctx = (srtp_aes_gcm_ctx_t *)c->state; if (ctx) { +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_aead_abort(&ctx->ctx->op); + psa_destroy_key(ctx->ctx->key_id); +#else mbedtls_gcm_free(ctx->ctx); +#endif srtp_crypto_free(ctx->ctx); /* zeroize the key material */ octet_string_set_to_zero(ctx, sizeof(srtp_aes_gcm_ctx_t)); @@ -196,7 +219,11 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_context_init(void *cv, FUNC_ENTRY(); srtp_aes_gcm_ctx_t *c = (srtp_aes_gcm_ctx_t *)cv; uint32_t key_len_in_bits; +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_status_t status = PSA_SUCCESS; +#else int errCode = 0; +#endif c->dir = srtp_direction_any; c->aad_size = 0; @@ -212,12 +239,42 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_context_init(void *cv, break; } +#if MBEDTLS_VERSION_MAJOR >= 4 + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_crypto_init failed: %d", status); + return srtp_err_status_init_fail; + } + + { + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_usage_flags(&attr, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm( + &attr, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, c->tag_len)); + psa_set_key_type(&attr, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attr, key_len_in_bits); + + if (c->ctx->key_id != PSA_KEY_ID_NULL) { + psa_destroy_key(c->ctx->key_id); + c->ctx->key_id = PSA_KEY_ID_NULL; + } + + status = psa_import_key(&attr, key, key_len_in_bits / 8, + &c->ctx->key_id); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_import_key failed: %d", status); + return srtp_err_status_init_fail; + } + } +#else errCode = mbedtls_gcm_setkey(c->ctx, MBEDTLS_CIPHER_ID_AES, (const unsigned char *)key, key_len_in_bits); if (errCode != 0) { debug_print(srtp_mod_aes_gcm, "mbedtls error code: %d", errCode); return srtp_err_status_init_fail; } +#endif return (srtp_err_status_ok); } @@ -236,6 +293,13 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_set_iv( } c->dir = direction; + /* set_iv marks the start of a fresh packet. Reset accumulated AAD so + * a prior packet's set_aad() bytes do not bleed into this packet's + * auth tag (legitimate caller sequence: set_iv -> set_aad -> drop -> + * set_iv -> set_aad -> encrypt; without this reset the second + * packet's tag covers the first packet's AAD too). */ + c->aad_size = 0; + debug_print(srtp_mod_aes_gcm, "setting iv: %s", srtp_octet_string_hex_string(iv, GCM_IV_LEN)); c->iv_len = GCM_IV_LEN; @@ -285,23 +349,136 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_encrypt(void *cv, { FUNC_ENTRY(); srtp_aes_gcm_ctx_t *c = (srtp_aes_gcm_ctx_t *)cv; - int errCode = 0; if (c->dir != srtp_direction_encrypt && c->dir != srtp_direction_decrypt) { return (srtp_err_status_bad_param); } - errCode = mbedtls_gcm_crypt_and_tag(c->ctx, MBEDTLS_GCM_ENCRYPT, *enc_len, - c->iv, c->iv_len, c->aad, c->aad_size, - buf, buf, c->tag_len, c->tag); - - c->aad_size = 0; - if (errCode != 0) { - debug_print(srtp_mod_aes_gcm, "mbedtls error code: %d", errCode); +#if MBEDTLS_VERSION_MAJOR >= 4 + { + /* + * libsrtp 2.x AES-GCM encrypt semantics: + * - ciphertext is written in-place into `buf` (no tag appended) + * - the auth tag is stashed in c->tag and later returned via get_tag() + * PSA's psa_aead_encrypt writes ciphertext+tag concatenated, so we use + * the multipart API which exposes the tag as a separate output, allowing + * us to keep the in-place ciphertext layout the 2.x callers expect. + */ + psa_status_t status; + psa_aead_operation_t op = psa_aead_operation_init(); + size_t out_off = 0; + size_t out_len = 0; + size_t tag_out_len = 0; + + status = psa_aead_encrypt_setup( + &op, c->ctx->key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, c->tag_len)); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_encrypt_setup failed: %d", + status); + goto enc_fail; + } + + status = psa_aead_set_lengths(&op, c->aad_size, *enc_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_set_lengths failed: %d", + status); + goto enc_fail; + } + + status = psa_aead_set_nonce(&op, c->iv, c->iv_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_set_nonce failed: %d", + status); + goto enc_fail; + } + + if (c->aad_size > 0) { + status = psa_aead_update_ad(&op, c->aad, c->aad_size); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, + "psa_aead_update_ad failed: %d", status); + goto enc_fail; + } + } + + /* + * psa_aead_update output buffer must be at least + * PSA_AEAD_UPDATE_OUTPUT_SIZE bytes (block-aligned for GCM); pass the + * full payload length which is always sufficient. In-place src==dst is + * permitted for PSA AEAD. + * + * AAD-only authentication is legal: *enc_len == 0 and buf may be + * NULL. Some PSA backends reject a NULL output pointer even when + * output_size is 0, so guard symmetrically with the decrypt path. + */ + out_len = 0; + if (*enc_len > 0) { + status = + psa_aead_update(&op, buf, *enc_len, buf, *enc_len, &out_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_update failed: %d", + status); + goto enc_fail; + } + } + out_off = out_len; + + /* + * psa_aead_finish writes any trailing ciphertext to a separate buffer. + * For GCM the trailing part is always 0 bytes, but the spec allows the + * implementation to defer up to one block, so we route it through a + * 16-byte scratch and copy any returned bytes back into buf at out_off. + */ + { + uint8_t finish_scratch[16]; + status = psa_aead_finish(&op, finish_scratch, sizeof(finish_scratch), + &out_len, c->tag, sizeof(c->tag), + &tag_out_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_finish failed: %d", + status); + goto enc_fail; + } + if (out_len > 0) { + if (out_off + out_len > *enc_len) { + psa_aead_abort(&op); + c->aad_size = 0; + return srtp_err_status_cipher_fail; + } + memcpy(buf + out_off, finish_scratch, out_len); + } + } + + if (tag_out_len != (size_t)c->tag_len) { + psa_aead_abort(&op); + c->aad_size = 0; + return srtp_err_status_cipher_fail; + } + + c->aad_size = 0; + return srtp_err_status_ok; + + enc_fail: + psa_aead_abort(&op); + c->aad_size = 0; return srtp_err_status_bad_param; } - - return (srtp_err_status_ok); +#else + { + int errCode = mbedtls_gcm_crypt_and_tag( + c->ctx, MBEDTLS_GCM_ENCRYPT, *enc_len, c->iv, c->iv_len, c->aad, + c->aad_size, buf, buf, c->tag_len, c->tag); + + c->aad_size = 0; + if (errCode != 0) { + debug_print(srtp_mod_aes_gcm, "mbedtls error code: %d", errCode); + return srtp_err_status_bad_param; + } + + return (srtp_err_status_ok); + } +#endif } /* @@ -341,7 +518,6 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_decrypt(void *cv, { FUNC_ENTRY(); srtp_aes_gcm_ctx_t *c = (srtp_aes_gcm_ctx_t *)cv; - int errCode = 0; if (c->dir != srtp_direction_encrypt && c->dir != srtp_direction_decrypt) { return (srtp_err_status_bad_param); @@ -350,21 +526,122 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_decrypt(void *cv, debug_print(srtp_mod_aes_gcm, "AAD: %s", srtp_octet_string_hex_string(c->aad, c->aad_size)); - errCode = mbedtls_gcm_auth_decrypt( - c->ctx, (*enc_len - c->tag_len), c->iv, c->iv_len, c->aad, c->aad_size, - buf + (*enc_len - c->tag_len), c->tag_len, buf, buf); - c->aad_size = 0; - if (errCode != 0) { - return (srtp_err_status_auth_fail); +#if MBEDTLS_VERSION_MAJOR >= 4 + { + /* + * libsrtp 2.x AES-GCM decrypt semantics: + * - input buf holds ciphertext || tag of total length *enc_len + * - plaintext is written in-place starting at buf + * - on success *enc_len is reduced by c->tag_len + * + * PSA exposes the tag as a separate input to psa_aead_verify, so we + * use the multipart API and feed buf[..ct_len] to update and the + * trailing c->tag_len bytes to verify. + */ + psa_status_t status; + psa_aead_operation_t op = psa_aead_operation_init(); + size_t ct_len; + size_t out_off = 0; + size_t out_len = 0; + + if ((unsigned int)c->tag_len > *enc_len) { + c->aad_size = 0; + return srtp_err_status_bad_param; + } + ct_len = (size_t)(*enc_len - (unsigned int)c->tag_len); + + status = psa_aead_decrypt_setup( + &op, c->ctx->key_id, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, c->tag_len)); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_decrypt_setup failed: %d", + status); + goto dec_fail; + } + + status = psa_aead_set_lengths(&op, c->aad_size, ct_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_set_lengths failed: %d", + status); + goto dec_fail; + } + + status = psa_aead_set_nonce(&op, c->iv, c->iv_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_set_nonce failed: %d", + status); + goto dec_fail; + } + + if (c->aad_size > 0) { + status = psa_aead_update_ad(&op, c->aad, c->aad_size); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, + "psa_aead_update_ad failed: %d", status); + goto dec_fail; + } + } + + /* In-place src==dst is permitted for PSA AEAD. */ + if (ct_len > 0) { + status = + psa_aead_update(&op, buf, ct_len, buf, ct_len, &out_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_gcm, "psa_aead_update failed: %d", + status); + goto dec_fail; + } + out_off = out_len; + } + + { + uint8_t finish_scratch[16]; + status = psa_aead_verify(&op, finish_scratch, + sizeof(finish_scratch), &out_len, + buf + ct_len, (size_t)c->tag_len); + if (status != PSA_SUCCESS) { + psa_aead_abort(&op); + c->aad_size = 0; + return srtp_err_status_auth_fail; + } + if (out_len > 0) { + if (out_off + out_len > ct_len) { + psa_aead_abort(&op); + c->aad_size = 0; + return srtp_err_status_cipher_fail; + } + memcpy(buf + out_off, finish_scratch, out_len); + } + } + + c->aad_size = 0; + *enc_len -= c->tag_len; + return srtp_err_status_ok; + + dec_fail: + psa_aead_abort(&op); + c->aad_size = 0; + return srtp_err_status_auth_fail; } - - /* - * Reduce the buffer size by the tag length since the tag - * is not part of the original payload - */ - *enc_len -= c->tag_len; - - return (srtp_err_status_ok); +#else + { + int errCode = mbedtls_gcm_auth_decrypt( + c->ctx, (*enc_len - c->tag_len), c->iv, c->iv_len, c->aad, + c->aad_size, buf + (*enc_len - c->tag_len), c->tag_len, buf, buf); + c->aad_size = 0; + if (errCode != 0) { + return (srtp_err_status_auth_fail); + } + + /* + * Reduce the buffer size by the tag length since the tag + * is not part of the original payload + */ + *enc_len -= c->tag_len; + + return (srtp_err_status_ok); + } +#endif } /* diff --git a/crypto/cipher/aes_icm_mbedtls.c b/crypto/cipher/aes_icm_mbedtls.c index bb5c4b79f..65fe03b01 100644 --- a/crypto/cipher/aes_icm_mbedtls.c +++ b/crypto/cipher/aes_icm_mbedtls.c @@ -45,7 +45,13 @@ #ifdef HAVE_CONFIG_H #include #endif +#include +#if MBEDTLS_VERSION_MAJOR >= 4 +#include +#include +#else #include +#endif #include "aes_icm_ext.h" #include "crypto_types.h" #include "err.h" /* for srtp_debug */ @@ -145,6 +151,19 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_alloc(srtp_cipher_t **c, return srtp_err_status_alloc_fail; } +#if MBEDTLS_VERSION_MAJOR >= 4 + icm->ctx = + (psa_aes_icm_ctx_t *)srtp_crypto_alloc(sizeof(psa_aes_icm_ctx_t)); + if (icm->ctx == NULL) { + srtp_crypto_free(icm); + srtp_crypto_free(*c); + *c = NULL; + return srtp_err_status_alloc_fail; + } + + icm->ctx->key_id = PSA_KEY_ID_NULL; + icm->ctx->op = psa_cipher_operation_init(); +#else icm->ctx = (mbedtls_aes_context *)srtp_crypto_alloc(sizeof(mbedtls_aes_context)); if (icm->ctx == NULL) { @@ -155,6 +174,7 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_alloc(srtp_cipher_t **c, } mbedtls_aes_init(icm->ctx); +#endif /* set pointers */ (*c)->state = icm; @@ -200,7 +220,12 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_dealloc(srtp_cipher_t *c) */ ctx = (srtp_aes_icm_ctx_t *)c->state; if (ctx != NULL) { +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_cipher_abort(&ctx->ctx->op); + psa_destroy_key(ctx->ctx->key_id); +#else mbedtls_aes_free(ctx->ctx); +#endif srtp_crypto_free(ctx->ctx); /* zeroize the key material */ octet_string_set_to_zero(ctx, sizeof(srtp_aes_icm_ctx_t)); @@ -218,7 +243,18 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_context_init(void *cv, { srtp_aes_icm_ctx_t *c = (srtp_aes_icm_ctx_t *)cv; uint32_t key_size_in_bits = (c->key_size << 3); +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_status_t status = PSA_SUCCESS; + + /* psa_crypto_init() is idempotent and required before any PSA use. */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_icm, "psa_crypto_init failed: %d", status); + return srtp_err_status_init_fail; + } +#else int errcode = 0; +#endif /* * set counter and initial values to 'offset' value, being careful not to @@ -246,10 +282,39 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_context_init(void *cv, break; } +#if MBEDTLS_VERSION_MAJOR >= 4 + { + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_type(&attr, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attr, key_size_in_bits); + psa_set_key_usage_flags(&attr, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attr, PSA_ALG_CTR); + + if (c->ctx->key_id != PSA_KEY_ID_NULL) { + /* Abort any in-flight cipher op before destroying its key: + * a leftover op from a prior set_iv() still references key_id, + * and on PSA implementations that eagerly invalidate op key + * handles, destroy_key would leave the op in a bad state. */ + psa_cipher_abort(&c->ctx->op); + c->ctx->op = psa_cipher_operation_init(); + psa_destroy_key(c->ctx->key_id); + c->ctx->key_id = PSA_KEY_ID_NULL; + } + + status = psa_import_key(&attr, key, key_size_in_bits / 8, + &(c->ctx->key_id)); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_icm, "psa_import_key failed: %d", status); + return srtp_err_status_init_fail; + } + } +#else errcode = mbedtls_aes_setkey_enc(c->ctx, key, key_size_in_bits); if (errcode != 0) { debug_print(srtp_mod_aes_icm, "errCode: %d", errcode); } +#endif return srtp_err_status_ok; } @@ -278,6 +343,33 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_set_iv( debug_print(srtp_mod_aes_icm, "set_counter: %s", v128_hex_string(&c->counter)); +#if MBEDTLS_VERSION_MAJOR >= 4 + { + psa_status_t status; + + /* Reset any prior in-flight cipher operation. */ + psa_cipher_abort(&c->ctx->op); + c->ctx->op = psa_cipher_operation_init(); + + status = psa_cipher_encrypt_setup(&c->ctx->op, c->ctx->key_id, + PSA_ALG_CTR); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_icm, + "psa_cipher_encrypt_setup failed: %d", status); + psa_cipher_abort(&c->ctx->op); + return srtp_err_status_cipher_fail; + } + + status = psa_cipher_set_iv(&c->ctx->op, c->counter.v8, 16); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_icm, "psa_cipher_set_iv failed: %d", + status); + psa_cipher_abort(&c->ctx->op); + return srtp_err_status_cipher_fail; + } + } +#endif + return srtp_err_status_ok; } @@ -295,16 +387,55 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_encrypt(void *cv, { srtp_aes_icm_ctx_t *c = (srtp_aes_icm_ctx_t *)cv; - int errCode = 0; debug_print(srtp_mod_aes_icm, "rs0: %s", v128_hex_string(&c->counter)); - errCode = - mbedtls_aes_crypt_ctr(c->ctx, *enc_len, &(c->nc_off), c->counter.v8, - c->stream_block.v8, buf, buf); - if (errCode != 0) { - debug_print(srtp_mod_aes_icm, "encrypt error: %d", errCode); - return srtp_err_status_cipher_fail; +#if MBEDTLS_VERSION_MAJOR >= 4 + { + psa_status_t status; + size_t out_len = 0; + + /* + * libsrtp's AES-ICM interface is incremental: encrypt() may be called + * multiple times after a single set_iv(). psa_cipher_update can be + * called repeatedly on the same operation handle to match that + * semantics; psa_cipher_finish is only invoked at dealloc/set_iv-reset + * time via psa_cipher_abort. In-place src==dst is supported by PSA. + */ + status = psa_cipher_update(&c->ctx->op, buf, *enc_len, buf, *enc_len, + &out_len); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_aes_icm, "psa_cipher_update failed: %d", + status); + psa_cipher_abort(&c->ctx->op); + return srtp_err_status_cipher_fail; + } + /* Mainline mbedTLS PSA implements CTR as a stream cipher and returns + * out_len == in_len, but the PSA spec lets a backend defer bytes to + * psa_cipher_finish. libsrtp's set_iv-then-encrypt model has no + * finish() call between packets, so a deferring backend would leak + * partial output. If this ever fires in the wild, the proper fix is + * to call psa_cipher_finish() and accumulate; until then, log and + * fail loudly so we notice rather than silently corrupting data. */ + if (out_len != *enc_len) { + debug_print2(srtp_mod_aes_icm, + "psa_cipher_update short write: %zu of %u " + "(PSA backend defers stream-cipher output; " + "unsupported in this wrapper)", + out_len, *enc_len); + return srtp_err_status_cipher_fail; + } } +#else + { + int errCode = + mbedtls_aes_crypt_ctr(c->ctx, *enc_len, &(c->nc_off), c->counter.v8, + c->stream_block.v8, buf, buf); + if (errCode != 0) { + debug_print(srtp_mod_aes_icm, "encrypt error: %d", errCode); + return srtp_err_status_cipher_fail; + } + } +#endif return srtp_err_status_ok; } diff --git a/crypto/hash/hmac_mbedtls.c b/crypto/hash/hmac_mbedtls.c index d109791fa..f1f426208 100644 --- a/crypto/hash/hmac_mbedtls.c +++ b/crypto/hash/hmac_mbedtls.c @@ -49,7 +49,17 @@ #include "alloc.h" #include "err.h" /* for srtp_debug */ #include "auth_test_cases.h" +#include +#if MBEDTLS_VERSION_MAJOR >= 4 +#include + +typedef struct { + psa_mac_operation_t op; + psa_key_id_t key_id; +} psa_hmac_ctx_t; +#else #include +#endif #define SHA1_DIGEST_SIZE 20 @@ -80,6 +90,27 @@ static srtp_err_status_t srtp_hmac_mbedtls_alloc(srtp_auth_t **a, if (*a == NULL) { return srtp_err_status_alloc_fail; } +#if MBEDTLS_VERSION_MAJOR >= 4 + { + psa_hmac_ctx_t *state; + psa_status_t status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_hmac, "psa_crypto_init failed: %d", status); + srtp_crypto_free(*a); + *a = NULL; + return srtp_err_status_init_fail; + } + state = (psa_hmac_ctx_t *)srtp_crypto_alloc(sizeof(psa_hmac_ctx_t)); + if (state == NULL) { + srtp_crypto_free(*a); + *a = NULL; + return srtp_err_status_alloc_fail; + } + state->key_id = PSA_KEY_ID_NULL; + state->op = psa_mac_operation_init(); + (*a)->state = state; + } +#else // allocate the buffer of mbedtls context. (*a)->state = srtp_crypto_alloc(sizeof(mbedtls_md_context_t)); if ((*a)->state == NULL) { @@ -88,6 +119,7 @@ static srtp_err_status_t srtp_hmac_mbedtls_alloc(srtp_auth_t **a, return srtp_err_status_alloc_fail; } mbedtls_md_init((mbedtls_md_context_t *)(*a)->state); +#endif /* set pointers */ (*a)->type = &srtp_hmac; @@ -100,10 +132,19 @@ static srtp_err_status_t srtp_hmac_mbedtls_alloc(srtp_auth_t **a, static srtp_err_status_t srtp_hmac_mbedtls_dealloc(srtp_auth_t *a) { +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_hmac_ctx_t *hmac_ctx = (psa_hmac_ctx_t *)a->state; + if (hmac_ctx) { + psa_mac_abort(&hmac_ctx->op); + psa_destroy_key(hmac_ctx->key_id); + srtp_crypto_free(hmac_ctx); + } +#else mbedtls_md_context_t *hmac_ctx; hmac_ctx = (mbedtls_md_context_t *)a->state; mbedtls_md_free(hmac_ctx); srtp_crypto_free(hmac_ctx); +#endif /* zeroize entire state*/ octet_string_set_to_zero(a, sizeof(srtp_auth_t)); @@ -115,17 +156,81 @@ static srtp_err_status_t srtp_hmac_mbedtls_dealloc(srtp_auth_t *a) static srtp_err_status_t srtp_hmac_mbedtls_start(void *statev) { +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_hmac_ctx_t *state = (psa_hmac_ctx_t *)statev; + /* + * Reset any in-flight MAC operation before re-priming. psa_mac_abort is + * idempotent on a freshly-initialised handle so this is safe on first use. + */ + psa_mac_abort(&state->op); + state->op = psa_mac_operation_init(); + + if (psa_mac_sign_setup(&state->op, state->key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_1)) != PSA_SUCCESS) { + psa_mac_abort(&state->op); + return srtp_err_status_auth_fail; + } + return srtp_err_status_ok; +#else mbedtls_md_context_t *state = (mbedtls_md_context_t *)statev; if (mbedtls_md_hmac_reset(state) != 0) return srtp_err_status_auth_fail; return srtp_err_status_ok; +#endif } static srtp_err_status_t srtp_hmac_mbedtls_init(void *statev, const uint8_t *key, int key_len) { +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_hmac_ctx_t *state = (psa_hmac_ctx_t *)statev; + psa_status_t status; + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + + if (key_len < 0) { + return srtp_err_status_bad_param; + } + + psa_set_key_type(&attr, PSA_KEY_TYPE_HMAC); + /* SIGN_HASH is the PSA 1.0 baseline accepted by psa_mac_sign_setup; + * SIGN_MESSAGE was added in 1.1 as a superset. Set both so the import + * works on PSA 1.0 drivers (TF-M secure-image builds) as well as on + * mainline mbedTLS 4. */ + psa_set_key_usage_flags(&attr, + PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_1)); + + if (state->key_id != PSA_KEY_ID_NULL) { + psa_destroy_key(state->key_id); + state->key_id = PSA_KEY_ID_NULL; + } + + status = psa_import_key(&attr, key, (size_t)key_len, &state->key_id); + if (status != PSA_SUCCESS) { + debug_print(srtp_mod_hmac, "psa_import_key failed: %d", status); + return srtp_err_status_auth_fail; + } + + /* Prime the operation so update()/finish() can be called without start(). */ + psa_mac_abort(&state->op); + state->op = psa_mac_operation_init(); + status = psa_mac_sign_setup(&state->op, state->key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_1)); + if (status != PSA_SUCCESS) { + psa_mac_abort(&state->op); + /* Free the freshly imported key so we don't leak the slot if the + * caller treats auth_fail as fatal and skips dealloc. */ + psa_destroy_key(state->key_id); + state->key_id = PSA_KEY_ID_NULL; + debug_print(srtp_mod_hmac, "psa_mac_sign_setup failed: %d", status); + return srtp_err_status_auth_fail; + } + + return srtp_err_status_ok; +#else mbedtls_md_context_t *state = (mbedtls_md_context_t *)statev; const mbedtls_md_info_t *info = NULL; @@ -145,12 +250,30 @@ static srtp_err_status_t srtp_hmac_mbedtls_init(void *statev, return srtp_err_status_auth_fail; return srtp_err_status_ok; +#endif } static srtp_err_status_t srtp_hmac_mbedtls_update(void *statev, const uint8_t *message, int msg_octets) { +#if MBEDTLS_VERSION_MAJOR >= 4 + psa_hmac_ctx_t *state = (psa_hmac_ctx_t *)statev; + + if (msg_octets < 0) { + return srtp_err_status_bad_param; + } + + debug_print(srtp_mod_hmac, "input: %s", + srtp_octet_string_hex_string(message, msg_octets)); + + if (psa_mac_update(&state->op, message, (size_t)msg_octets) != + PSA_SUCCESS) { + psa_mac_abort(&state->op); + return srtp_err_status_auth_fail; + } + return srtp_err_status_ok; +#else mbedtls_md_context_t *state = (mbedtls_md_context_t *)statev; debug_print(srtp_mod_hmac, "input: %s", @@ -160,6 +283,7 @@ static srtp_err_status_t srtp_hmac_mbedtls_update(void *statev, return srtp_err_status_auth_fail; return srtp_err_status_ok; +#endif } static srtp_err_status_t srtp_hmac_mbedtls_compute(void *statev, @@ -168,7 +292,6 @@ static srtp_err_status_t srtp_hmac_mbedtls_compute(void *statev, int tag_len, uint8_t *result) { - mbedtls_md_context_t *state = (mbedtls_md_context_t *)statev; uint8_t hash_value[SHA1_DIGEST_SIZE]; int i; @@ -177,12 +300,34 @@ static srtp_err_status_t srtp_hmac_mbedtls_compute(void *statev, return srtp_err_status_bad_param; } - /* hash message, copy output into H */ - if (mbedtls_md_hmac_update(statev, message, msg_octets) != 0) - return srtp_err_status_auth_fail; - - if (mbedtls_md_hmac_finish(state, hash_value) != 0) - return srtp_err_status_auth_fail; +#if MBEDTLS_VERSION_MAJOR >= 4 + { + psa_hmac_ctx_t *state = (psa_hmac_ctx_t *)statev; + size_t out_len = 0; + + if (psa_mac_update(&state->op, message, (size_t)msg_octets) != + PSA_SUCCESS) { + psa_mac_abort(&state->op); + return srtp_err_status_auth_fail; + } + + if (psa_mac_sign_finish(&state->op, hash_value, sizeof(hash_value), + &out_len) != PSA_SUCCESS) { + psa_mac_abort(&state->op); + return srtp_err_status_auth_fail; + } + } +#else + { + mbedtls_md_context_t *state = (mbedtls_md_context_t *)statev; + /* hash message, copy output into H */ + if (mbedtls_md_hmac_update(statev, message, msg_octets) != 0) + return srtp_err_status_auth_fail; + + if (mbedtls_md_hmac_finish(state, hash_value) != 0) + return srtp_err_status_auth_fail; + } +#endif /* copy hash_value to *result */ for (i = 0; i < tag_len; i++) { diff --git a/crypto/include/aes_gcm.h b/crypto/include/aes_gcm.h index cd0dceed3..caff67216 100644 --- a/crypto/include/aes_gcm.h +++ b/crypto/include/aes_gcm.h @@ -66,6 +66,30 @@ typedef struct { #ifdef MBEDTLS #define MAX_AD_SIZE 2048 +#include + +#if MBEDTLS_VERSION_MAJOR >= 4 +#include + +typedef struct { + psa_key_id_t key_id; + psa_aead_operation_t op; +} psa_aes_gcm_ctx_t; + +typedef struct { + int key_size; + int tag_len; + int aad_size; + int iv_len; + uint8_t iv[12]; + uint8_t tag[16]; + uint8_t aad[MAX_AD_SIZE]; + psa_aes_gcm_ctx_t *ctx; + srtp_cipher_direction_t dir; +} srtp_aes_gcm_ctx_t; + +#else /* MBEDTLS_VERSION_MAJOR >= 4 */ + #include #include @@ -81,6 +105,8 @@ typedef struct { srtp_cipher_direction_t dir; } srtp_aes_gcm_ctx_t; +#endif /* MBEDTLS_VERSION_MAJOR >= 4 */ + #endif /* MBEDTLS */ #ifdef NSS diff --git a/crypto/include/aes_icm_ext.h b/crypto/include/aes_icm_ext.h index 5b21c95dd..b33caf508 100644 --- a/crypto/include/aes_icm_ext.h +++ b/crypto/include/aes_icm_ext.h @@ -65,6 +65,27 @@ typedef struct { #ifdef MBEDTLS +#include + +#if MBEDTLS_VERSION_MAJOR >= 4 +#include + +typedef struct { + psa_key_id_t key_id; + psa_cipher_operation_t op; +} psa_aes_icm_ctx_t; + +typedef struct { + v128_t counter; /* holds the counter value */ + v128_t offset; /* initial offset value */ + v128_t stream_block; + size_t nc_off; + int key_size; + psa_aes_icm_ctx_t *ctx; +} srtp_aes_icm_ctx_t; + +#else /* MBEDTLS_VERSION_MAJOR >= 4 */ + #include typedef struct { v128_t counter; /* holds the counter value */ @@ -75,6 +96,8 @@ typedef struct { mbedtls_aes_context *ctx; } srtp_aes_icm_ctx_t; +#endif /* MBEDTLS_VERSION_MAJOR >= 4 */ + #endif /* MBEDTLS */ #ifdef NSS diff --git a/meson.build b/meson.build index ff8a21009..08b00697b 100644 --- a/meson.build +++ b/meson.build @@ -159,6 +159,14 @@ elif crypto_library == 'mbedtls' mbedtls_dep = cc.find_library('mbedcrypto', has_headers: ['mbedtls/aes.h'], required: true) endif srtp2_deps += [mbedtls_dep] + # mbedTLS 4.x ships the PSA Crypto implementation as a separate library + # (libtfpsacrypto); libmbedcrypto on 4.x is a thin wrapper that does not + # contain the psa_* symbols. Link it when present; older 3.x installs + # simply won't have it and this is a no-op. + mbedtls_psa_dep = cc.find_library('tfpsacrypto', required: false) + if mbedtls_psa_dep.found() + srtp2_deps += [mbedtls_psa_dep] + endif cdata.set('GCM', true) cdata.set('MBEDTLS', true) cdata.set('USE_EXTERNAL_CRYPTO', true) From 6d6c04e0c28c0b63827b13ce11d850d8de700fa7 Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Mon, 1 Jun 2026 17:52:50 +0530 Subject: [PATCH 2/3] fixup: tighten GCM dir check + status code; document AES-ICM SRTP counter - GCM encrypt/decrypt now require exact dir match (catches crossed-ctx bugs PSA does not, since the key carries both ENCRYPT and DECRYPT usage flags) - GCM enc_fail returns cipher_fail (not bad_param) for genuine PSA crypto failures - AES-ICM: comment that PSA_ALG_CTR uses a full 128-bit counter, same as the legacy mbedtls_aes_crypt_ctr path; SRTP packet sizes (< 1 MiB) are unaffected --- crypto/cipher/aes_gcm_mbedtls.c | 12 +++++++++--- crypto/cipher/aes_icm_mbedtls.c | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/crypto/cipher/aes_gcm_mbedtls.c b/crypto/cipher/aes_gcm_mbedtls.c index 5bd15d4f6..39cc244fc 100644 --- a/crypto/cipher/aes_gcm_mbedtls.c +++ b/crypto/cipher/aes_gcm_mbedtls.c @@ -350,7 +350,11 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_encrypt(void *cv, FUNC_ENTRY(); srtp_aes_gcm_ctx_t *c = (srtp_aes_gcm_ctx_t *)cv; - if (c->dir != srtp_direction_encrypt && c->dir != srtp_direction_decrypt) { + /* Require dir == encrypt to catch cross-call bugs (caller passing a + * decrypt-configured ctx into the encrypt entry point). PSA does not + * catch this on its own since the imported key has both ENCRYPT and + * DECRYPT usage flags. */ + if (c->dir != srtp_direction_encrypt) { return (srtp_err_status_bad_param); } @@ -462,7 +466,7 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_encrypt(void *cv, enc_fail: psa_aead_abort(&op); c->aad_size = 0; - return srtp_err_status_bad_param; + return srtp_err_status_cipher_fail; } #else { @@ -519,7 +523,9 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_decrypt(void *cv, FUNC_ENTRY(); srtp_aes_gcm_ctx_t *c = (srtp_aes_gcm_ctx_t *)cv; - if (c->dir != srtp_direction_encrypt && c->dir != srtp_direction_decrypt) { + /* Require dir == decrypt to catch cross-call bugs (see encrypt for + * rationale). */ + if (c->dir != srtp_direction_decrypt) { return (srtp_err_status_bad_param); } diff --git a/crypto/cipher/aes_icm_mbedtls.c b/crypto/cipher/aes_icm_mbedtls.c index 65fe03b01..7fc62dcc0 100644 --- a/crypto/cipher/aes_icm_mbedtls.c +++ b/crypto/cipher/aes_icm_mbedtls.c @@ -289,6 +289,13 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_context_init(void *cv, psa_set_key_bits(&attr, key_size_in_bits); psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + /* SRTP AES-ICM spec only allows the low 16 bits of the counter to + * vary per packet (top 14 bytes are the salted nonce, see RFC 3711). + * PSA_ALG_CTR increments the full 128-bit counter, identical to the + * legacy mbedtls_aes_crypt_ctr path. For SRTP packet sizes + * (< 2^16 blocks = 1 MiB) the keystreams agree, so all conformant + * RTP/RTCP payloads are correct. Buffers > 1 MiB are not + * SRTP-conformant on either backend. */ psa_set_key_algorithm(&attr, PSA_ALG_CTR); if (c->ctx->key_id != PSA_KEY_ID_NULL) { From bd11b49bca9ec08ebc1c856580a118bc52f0c01d Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Mon, 1 Jun 2026 18:00:46 +0530 Subject: [PATCH 3/3] ci: fix mbedtls 2.x include + mbedtls4 PIC + macOS prefix - mbedtls 2.x ships only mbedtls/version.h, not mbedtls/build_info.h; wrap the include with __has_include so the gate resolves on 2.x too - mbedtls4 install needs -DCMAKE_POSITION_INDEPENDENT_CODE=ON so static libtfpsacrypto can be linked into libsrtp2.so on Ubuntu - macOS mbedtls4 install path moved out of $GITHUB_WORKSPACE which the later actions/checkout@v2 step wipes; install to /tmp/ instead --- .github/workflows/cmake.yml | 26 +++++++++++++------- .github/workflows/meson.yml | 22 ++++++++++------- crypto/cipher/aes_gcm_mbedtls.c | 43 +++++++++++++++++++-------------- crypto/cipher/aes_icm_mbedtls.c | 19 ++++++++++----- crypto/hash/hmac_mbedtls.c | 15 +++++++++--- crypto/include/aes_gcm.h | 7 ++++++ crypto/include/aes_icm_ext.h | 7 ++++++ 7 files changed, 93 insertions(+), 46 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 749bc1e1b..662b81bef 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -62,14 +62,18 @@ jobs: if: matrix.os == 'ubuntu-latest' && matrix.crypto == 'mbedtls4' run: | python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 - git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src - cd mbedtls4-src + git clone https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls4-src + cd /tmp/mbedtls4-src git checkout mbedtls-4.0.0 git submodule update --init --recursive - cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF + # PIC is required to link the static libtfpsacrypto into libsrtp2's + # shared library; without it the final shared link fails with + # "relocation R_X86_64_PC32 ... can not be used when making a shared + # object; recompile with -fPIC". + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build -j sudo cmake --install build - cd .. - name: Setup macOS OpenSSL if: matrix.os == 'macos-latest' && matrix.crypto == 'openssl' @@ -94,16 +98,20 @@ jobs: - name: Setup macOS MbedTLS 4 if: matrix.os == 'macos-latest' && matrix.crypto == 'mbedtls4' run: | + # Install under /tmp/ (outside $GITHUB_WORKSPACE) so the install + # survives the actions/checkout@v2 step that runs later in this job + # and clears the workspace. python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 - git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src - cd mbedtls4-src + git clone https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls4-src + cd /tmp/mbedtls4-src git checkout mbedtls-4.0.0 git submodule update --init --recursive - cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/mbedtls4-prefix" + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_INSTALL_PREFIX=/tmp/mbedtls4-prefix cmake --build build -j cmake --install build - echo "CMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/mbedtls4-prefix" >> $GITHUB_ENV - cd .. + echo "CMAKE_PREFIX_PATH=/tmp/mbedtls4-prefix" >> $GITHUB_ENV - uses: actions/checkout@v2 diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index cbdd11e44..c45059eb3 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -79,14 +79,15 @@ jobs: if: matrix.os == 'ubuntu-latest' && matrix.crypto == 'mbedtls4' run: | python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 - git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src - cd mbedtls4-src + git clone https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls4-src + cd /tmp/mbedtls4-src git checkout mbedtls-4.0.0 git submodule update --init --recursive - cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF + # PIC: static libtfpsacrypto must be linkable into libsrtp2.so. + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build -j sudo cmake --install build - cd .. - name: Setup macOS OpenSSL if: matrix.os == 'macos-latest' && matrix.crypto == 'openssl' @@ -111,16 +112,19 @@ jobs: - name: Setup macOS MbedTLS 4 if: matrix.os == 'macos-latest' && matrix.crypto == 'mbedtls4' run: | + # Install outside $GITHUB_WORKSPACE so the install survives the + # actions/checkout@v2 step that wipes the workspace later in the job. python3 -m pip install --break-system-packages --upgrade jsonschema jinja2 - git clone https://github.com/Mbed-TLS/mbedtls.git mbedtls4-src - cd mbedtls4-src + git clone https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls4-src + cd /tmp/mbedtls4-src git checkout mbedtls-4.0.0 git submodule update --init --recursive - cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/mbedtls4-prefix" + cmake -S . -B build -DENABLE_TESTING=OFF -DENABLE_PROGRAMS=OFF \ + -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ + -DCMAKE_INSTALL_PREFIX=/tmp/mbedtls4-prefix cmake --build build -j cmake --install build - echo "PKG_CONFIG_PATH=$GITHUB_WORKSPACE/mbedtls4-prefix/lib/pkgconfig" >> $GITHUB_ENV - cd .. + echo "PKG_CONFIG_PATH=/tmp/mbedtls4-prefix/lib/pkgconfig" >> $GITHUB_ENV - uses: actions/checkout@v2 diff --git a/crypto/cipher/aes_gcm_mbedtls.c b/crypto/cipher/aes_gcm_mbedtls.c index 39cc244fc..83389abd9 100644 --- a/crypto/cipher/aes_gcm_mbedtls.c +++ b/crypto/cipher/aes_gcm_mbedtls.c @@ -46,7 +46,14 @@ #ifdef HAVE_CONFIG_H #include #endif +/* build_info.h was added in mbedtls 3.0; mbedtls 2.x ships version.h only. + * Use __has_include so the gate still resolves on 2.x (the legacy code path). + */ +#if defined(__has_include) && __has_include() #include +#else +#include +#endif #if MBEDTLS_VERSION_MAJOR >= 4 #include #else @@ -260,8 +267,8 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_context_init(void *cv, c->ctx->key_id = PSA_KEY_ID_NULL; } - status = psa_import_key(&attr, key, key_len_in_bits / 8, - &c->ctx->key_id); + status = + psa_import_key(&attr, key, key_len_in_bits / 8, &c->ctx->key_id); if (status != PSA_SUCCESS) { debug_print(srtp_mod_aes_gcm, "psa_import_key failed: %d", status); return srtp_err_status_init_fail; @@ -363,10 +370,11 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_encrypt(void *cv, /* * libsrtp 2.x AES-GCM encrypt semantics: * - ciphertext is written in-place into `buf` (no tag appended) - * - the auth tag is stashed in c->tag and later returned via get_tag() - * PSA's psa_aead_encrypt writes ciphertext+tag concatenated, so we use - * the multipart API which exposes the tag as a separate output, allowing - * us to keep the in-place ciphertext layout the 2.x callers expect. + * - the auth tag is stashed in c->tag and later returned via + * get_tag() PSA's psa_aead_encrypt writes ciphertext+tag concatenated, + * so we use the multipart API which exposes the tag as a separate + * output, allowing us to keep the in-place ciphertext layout the 2.x + * callers expect. */ psa_status_t status; psa_aead_operation_t op = psa_aead_operation_init(); @@ -400,8 +408,8 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_encrypt(void *cv, if (c->aad_size > 0) { status = psa_aead_update_ad(&op, c->aad, c->aad_size); if (status != PSA_SUCCESS) { - debug_print(srtp_mod_aes_gcm, - "psa_aead_update_ad failed: %d", status); + debug_print(srtp_mod_aes_gcm, "psa_aead_update_ad failed: %d", + status); goto enc_fail; } } @@ -436,9 +444,9 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_encrypt(void *cv, */ { uint8_t finish_scratch[16]; - status = psa_aead_finish(&op, finish_scratch, sizeof(finish_scratch), - &out_len, c->tag, sizeof(c->tag), - &tag_out_len); + status = + psa_aead_finish(&op, finish_scratch, sizeof(finish_scratch), + &out_len, c->tag, sizeof(c->tag), &tag_out_len); if (status != PSA_SUCCESS) { debug_print(srtp_mod_aes_gcm, "psa_aead_finish failed: %d", status); @@ -582,16 +590,15 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_decrypt(void *cv, if (c->aad_size > 0) { status = psa_aead_update_ad(&op, c->aad, c->aad_size); if (status != PSA_SUCCESS) { - debug_print(srtp_mod_aes_gcm, - "psa_aead_update_ad failed: %d", status); + debug_print(srtp_mod_aes_gcm, "psa_aead_update_ad failed: %d", + status); goto dec_fail; } } /* In-place src==dst is permitted for PSA AEAD. */ if (ct_len > 0) { - status = - psa_aead_update(&op, buf, ct_len, buf, ct_len, &out_len); + status = psa_aead_update(&op, buf, ct_len, buf, ct_len, &out_len); if (status != PSA_SUCCESS) { debug_print(srtp_mod_aes_gcm, "psa_aead_update failed: %d", status); @@ -602,9 +609,9 @@ static srtp_err_status_t srtp_aes_gcm_mbedtls_decrypt(void *cv, { uint8_t finish_scratch[16]; - status = psa_aead_verify(&op, finish_scratch, - sizeof(finish_scratch), &out_len, - buf + ct_len, (size_t)c->tag_len); + status = + psa_aead_verify(&op, finish_scratch, sizeof(finish_scratch), + &out_len, buf + ct_len, (size_t)c->tag_len); if (status != PSA_SUCCESS) { psa_aead_abort(&op); c->aad_size = 0; diff --git a/crypto/cipher/aes_icm_mbedtls.c b/crypto/cipher/aes_icm_mbedtls.c index 7fc62dcc0..3e6046e06 100644 --- a/crypto/cipher/aes_icm_mbedtls.c +++ b/crypto/cipher/aes_icm_mbedtls.c @@ -45,7 +45,14 @@ #ifdef HAVE_CONFIG_H #include #endif +/* build_info.h was added in mbedtls 3.0; mbedtls 2.x ships version.h only. + * Use __has_include so the gate still resolves on 2.x (the legacy code path). + */ +#if defined(__has_include) && __has_include() #include +#else +#include +#endif #if MBEDTLS_VERSION_MAJOR >= 4 #include #include @@ -309,8 +316,8 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_context_init(void *cv, c->ctx->key_id = PSA_KEY_ID_NULL; } - status = psa_import_key(&attr, key, key_size_in_bits / 8, - &(c->ctx->key_id)); + status = + psa_import_key(&attr, key, key_size_in_bits / 8, &(c->ctx->key_id)); if (status != PSA_SUCCESS) { debug_print(srtp_mod_aes_icm, "psa_import_key failed: %d", status); return srtp_err_status_init_fail; @@ -358,11 +365,11 @@ static srtp_err_status_t srtp_aes_icm_mbedtls_set_iv( psa_cipher_abort(&c->ctx->op); c->ctx->op = psa_cipher_operation_init(); - status = psa_cipher_encrypt_setup(&c->ctx->op, c->ctx->key_id, - PSA_ALG_CTR); + status = + psa_cipher_encrypt_setup(&c->ctx->op, c->ctx->key_id, PSA_ALG_CTR); if (status != PSA_SUCCESS) { - debug_print(srtp_mod_aes_icm, - "psa_cipher_encrypt_setup failed: %d", status); + debug_print(srtp_mod_aes_icm, "psa_cipher_encrypt_setup failed: %d", + status); psa_cipher_abort(&c->ctx->op); return srtp_err_status_cipher_fail; } diff --git a/crypto/hash/hmac_mbedtls.c b/crypto/hash/hmac_mbedtls.c index f1f426208..ff86c62c8 100644 --- a/crypto/hash/hmac_mbedtls.c +++ b/crypto/hash/hmac_mbedtls.c @@ -49,7 +49,14 @@ #include "alloc.h" #include "err.h" /* for srtp_debug */ #include "auth_test_cases.h" +/* build_info.h was added in mbedtls 3.0; mbedtls 2.x ships version.h only. + * Use __has_include so the gate still resolves on 2.x (the legacy code path). + */ +#if defined(__has_include) && __has_include() #include +#else +#include +#endif #if MBEDTLS_VERSION_MAJOR >= 4 #include @@ -198,9 +205,8 @@ static srtp_err_status_t srtp_hmac_mbedtls_init(void *statev, * SIGN_MESSAGE was added in 1.1 as a superset. Set both so the import * works on PSA 1.0 drivers (TF-M secure-image builds) as well as on * mainline mbedTLS 4. */ - psa_set_key_usage_flags(&attr, - PSA_KEY_USAGE_SIGN_HASH | - PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_SIGN_MESSAGE); psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_1)); if (state->key_id != PSA_KEY_ID_NULL) { @@ -214,7 +220,8 @@ static srtp_err_status_t srtp_hmac_mbedtls_init(void *statev, return srtp_err_status_auth_fail; } - /* Prime the operation so update()/finish() can be called without start(). */ + /* Prime the operation so update()/finish() can be called without start(). + */ psa_mac_abort(&state->op); state->op = psa_mac_operation_init(); status = psa_mac_sign_setup(&state->op, state->key_id, diff --git a/crypto/include/aes_gcm.h b/crypto/include/aes_gcm.h index caff67216..86d910a3b 100644 --- a/crypto/include/aes_gcm.h +++ b/crypto/include/aes_gcm.h @@ -66,7 +66,14 @@ typedef struct { #ifdef MBEDTLS #define MAX_AD_SIZE 2048 +/* build_info.h was added in mbedtls 3.0; mbedtls 2.x ships version.h only. + * Use __has_include so the gate still resolves on 2.x (the legacy code path). + */ +#if defined(__has_include) && __has_include() #include +#else +#include +#endif #if MBEDTLS_VERSION_MAJOR >= 4 #include diff --git a/crypto/include/aes_icm_ext.h b/crypto/include/aes_icm_ext.h index b33caf508..406ad782e 100644 --- a/crypto/include/aes_icm_ext.h +++ b/crypto/include/aes_icm_ext.h @@ -65,7 +65,14 @@ typedef struct { #ifdef MBEDTLS +/* build_info.h was added in mbedtls 3.0; mbedtls 2.x ships version.h only. + * Use __has_include so the gate still resolves on 2.x (the legacy code path). + */ +#if defined(__has_include) && __has_include() #include +#else +#include +#endif #if MBEDTLS_VERSION_MAJOR >= 4 #include