From: Emil Velikov Date: Sun, 15 Feb 2026 21:11:58 +0000 (+0000) Subject: mbedlts: add as alternative to openssl X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c3765cdce287f1bd22034c6ed25e803a1ab8b01a;p=thirdparty%2Fkmod.git mbedlts: add as alternative to openssl In a recent conversation improving the depmod runtime, we noticed that kmod_module_signature_info() normally takes 20-25% of the total runtime for uncompressed modules. The related attempts at using threads, also indicated that OpenSSL may not be thread-safe... At least, not the way we use it. Enter Mbed TLS (formerly known as polarssl), self described as: Mbed TLS is a C library that implements X.509 certificate manipulation ... ... Its small code footprint makes it suitable for embedded systems. ... There are 3 major/LTS versions in the wild, - v2 released in 2015 - Debian 12- and Ubuntu 24.04- - v3 released in 2021 - Debian 13+, Ubuntu 25.04+ and everywhere* - v4 released in 2025 - close to non-existing adoption In practical terms, using MbedTLS has proven to be a much shorter and simpler implementation. Effectively eliminating the time spent in kmod_module_signature_info() to practically zero. With the overall execution time dropping respectively - 0.43s -> 0.35s on my machine. From instructions/memory statistics POV, we have (per module): - ~50k fewer instructions - ~55 fewer memory allocations - ~2KB less memory is allocated Also, Helgrind seems quite happy. Availability aside, there are a few IMHO minor downsides: - MD4 support was removed with v3 - SM3 support is not available - neither in v3 nor v4 - No algo to string API I wasn't able to find any distribution/builds that use MD4 or SM3 so this aspect shouldn't be too critical. Whereas for the algo-to-string API - it's a trivial mapping, that we can (should?) copy from the kernel alongside the enum itself... But that's an exercise for another day - alongside fixing the no openssl/mbedtls code path. Signed-off-by: Emil Velikov Link: https://github.com/kmod-project/kmod/pull/426 Signed-off-by: Lucas De Marchi --- diff --git a/README.md b/README.md index a5ef3d3d..84af2981 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Optional dependencies, required with the default build configuration: - LZMA library - ZSTD library - OPENSSL library (signature handling in modinfo) +- MBEDTLS library (signature handling in modinfo) All the optional dependencies are loaded on demand via `dlopen` by default. This can be changed via the `dlopen` build option. Typical configuration and installation: diff --git a/build-dev.ini b/build-dev.ini index 804893f4..62ada834 100644 --- a/build-dev.ini +++ b/build-dev.ini @@ -11,6 +11,7 @@ zstd = 'enabled' xz = 'enabled' zlib = 'enabled' openssl = 'enabled' +mbedtls = 'disabled' werror = true b_sanitize = 'address,undefined' diff --git a/libkmod/libkmod-internal-signature.h b/libkmod/libkmod-internal-signature.h index b58193d5..0fd51229 100644 --- a/libkmod/libkmod-internal-signature.h +++ b/libkmod/libkmod-internal-signature.h @@ -13,3 +13,14 @@ static inline bool kmod_fill_pkcs7_openssl(const char *mem, off_t size, size_t s return false; } #endif + +#if ENABLE_MBEDTLS +bool kmod_fill_pkcs7_mbedtls(const char *mem, off_t size, size_t sig_len, + struct kmod_signature_info **out_sig_info); +#else +static inline bool kmod_fill_pkcs7_mbedtls(const char *mem, off_t size, size_t sig_len, + struct kmod_signature_info **out_sig_info) +{ + return false; +} +#endif diff --git a/libkmod/libkmod-signature-mbedtls.c b/libkmod/libkmod-signature-mbedtls.c new file mode 100644 index 00000000..7794de1e --- /dev/null +++ b/libkmod/libkmod-signature-mbedtls.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright (C) 2026 Emil Velikov + */ + +#define DLSYM_LOCALLY_ENABLED ENABLE_MBEDTLS_DLOPEN + +#include +#include // for MBEDTLS_OID_DIGEST_ALG_* +#include +#include +#include +#include + +#include +#include + +#include "libkmod-internal-signature.h" + +/* XXX: temporary copy from libkmod-signature.c */ +enum pkey_hash_algo { + PKEY_HASH_MD4, + PKEY_HASH_MD5, + PKEY_HASH_SHA1, + PKEY_HASH_RIPE_MD_160, + PKEY_HASH_SHA256, + PKEY_HASH_SHA384, + PKEY_HASH_SHA512, + PKEY_HASH_SHA224, + PKEY_HASH_SM3, + PKEY_HASH__LAST, +}; + +/* XXX: ditto */ +static const char *const pkey_hash_algo[PKEY_HASH__LAST] = { + // clang-format off + [PKEY_HASH_MD4] = "md4", + [PKEY_HASH_MD5] = "md5", + [PKEY_HASH_SHA1] = "sha1", + [PKEY_HASH_RIPE_MD_160] = "rmd160", + [PKEY_HASH_SHA256] = "sha256", + [PKEY_HASH_SHA384] = "sha384", + [PKEY_HASH_SHA512] = "sha512", + [PKEY_HASH_SHA224] = "sha224", + [PKEY_HASH_SM3] = "sm3", + // clang-format on +}; + +#define DL_SYMBOL_TABLE(M) \ + M(mbedtls_pkcs7_init) \ + M(mbedtls_pkcs7_parse_der) \ + M(mbedtls_pkcs7_free) + +DL_SYMBOL_TABLE(DECLARE_SYM) + +static int dlopen_mbedx509(void) +{ +#if !DLSYM_LOCALLY_ENABLED + return 0; +#else + static void *dl; + + ELF_NOTE_DLOPEN("mbedtls", "Support for reading module signatures", + ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, "libmbedx509.so.7"); + + return dlsym_many(&dl, "libmbedx509.so.7", DL_SYMBOL_TABLE(DLSYM_ARG) NULL); +#endif +} + +static int to_hash_algo(const mbedtls_pkcs7_buf *oid) +{ + /* MBEDTLS_OID_DIGEST_ALG_MD4 was removed around mbedtls v3.0 */ + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_MD5, oid)) + return PKEY_HASH_MD5; + + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_SHA1, oid)) + return PKEY_HASH_SHA1; + + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_RIPEMD160, oid)) + return PKEY_HASH_RIPE_MD_160; + + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_SHA256, oid)) + return PKEY_HASH_SHA256; + + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_SHA384, oid)) + return PKEY_HASH_SHA384; + + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_SHA512, oid)) + return PKEY_HASH_SHA512; + + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_DIGEST_ALG_SHA224, oid)) + return PKEY_HASH_SHA224; + + /* MBEDTLS_OID_DIGEST_ALG_SM3: Not available, as of 2026-02-14 */ + return -1; +} + +bool kmod_fill_pkcs7_mbedtls(const char *mem, off_t size, size_t sig_len, + struct kmod_signature_info **out_sig_info) +{ + struct kmod_signature_info *sig_info; + char *p; + const struct mbedtls_pkcs7_signed_data *signed_data; + const struct mbedtls_pkcs7_signer_info *signers; + struct mbedtls_pkcs7 pkcs7; + const unsigned char *pkcs7_raw; + size_t total_len; + int hash_algo, ret; + + ret = dlopen_mbedx509(); + if (ret < 0) + return false; + + size -= sig_len; + pkcs7_raw = (const unsigned char *)mem + size; + + sym_mbedtls_pkcs7_init(&pkcs7); + ret = sym_mbedtls_pkcs7_parse_der(&pkcs7, pkcs7_raw, sig_len); + if (ret < 0) + goto err; + + signed_data = &pkcs7.MBEDTLS_PRIVATE(signed_data); + signers = &signed_data->MBEDTLS_PRIVATE(signers); + + hash_algo = to_hash_algo(&signed_data->MBEDTLS_PRIVATE(digest_alg_identifiers)); + if (hash_algo < 0) + goto err; + + /* Calculate the total length */ + total_len = sizeof(struct kmod_signature_info); + + /* signer */ + total_len += signers->MBEDTLS_PRIVATE(issuer).val.len; + + /* key_id */ + total_len += signers->MBEDTLS_PRIVATE(serial).len; + + /* hash_algo */ + /* XXX: using an incomplete and not-fully correct static table */ + + /* sig */ + total_len += signers->MBEDTLS_PRIVATE(sig).len; + + sig_info = calloc(1, total_len); + if (sig_info == NULL) + goto err; + + p = (char *)sig_info; + + p += sizeof(struct kmod_signature_info); + + /* signer */ + sig_info->signer = p; + sig_info->signer_len = signers->MBEDTLS_PRIVATE(issuer).val.len; + + memcpy(p, signers->MBEDTLS_PRIVATE(issuer).val.p, sig_info->signer_len); + p += sig_info->signer_len; + + /* key_id */ + sig_info->key_id = p; + sig_info->key_id_len = signers->MBEDTLS_PRIVATE(serial).len; + + memcpy(p, signers->MBEDTLS_PRIVATE(serial).p, sig_info->key_id_len); + p += sig_info->key_id_len; + + /* hash_algo */ + sig_info->hash_algo = pkey_hash_algo[hash_algo]; + + /* sig */ + sig_info->sig = p; + sig_info->sig_len = signers->MBEDTLS_PRIVATE(sig).len; + + memcpy(p, signers->MBEDTLS_PRIVATE(sig).p, sig_info->sig_len); + + sym_mbedtls_pkcs7_free(&pkcs7); + *out_sig_info = sig_info; + + return true; + +err: + sym_mbedtls_pkcs7_free(&pkcs7); + return false; +} diff --git a/libkmod/libkmod-signature.c b/libkmod/libkmod-signature.c index 9f9181ee..4e251e63 100644 --- a/libkmod/libkmod-signature.c +++ b/libkmod/libkmod-signature.c @@ -168,6 +168,8 @@ bool kmod_module_signature_info(const struct kmod_file *file, switch (modsig.id_type) { case PKEY_ID_PKCS7: ret = kmod_fill_pkcs7_openssl(mem, size, sig_len, sig_info); + if (!ret) + ret = kmod_fill_pkcs7_mbedtls(mem, size, sig_len, sig_info); if (!ret) ret = kmod_fill_pkcs7_dummy(mem, size, sig_len, sig_info); break; diff --git a/meson.build b/meson.build index 076c552b..6af3027c 100644 --- a/meson.build +++ b/meson.build @@ -274,6 +274,7 @@ _moar_deps = [ ['xz', 'liblzma', '>= 4.99'], ['zlib', 'zlib', '>= 0'], ['openssl', 'libcrypto', '>= 3.0.0'], + ['mbedtls', 'mbedx509', '>= 3.6.0'], ] foreach tuple : _moar_deps @@ -296,7 +297,7 @@ foreach tuple : _moar_deps cdata.set10('ENABLE_' + opt.to_upper() + '_DLOPEN', have and dlopen) if have - if opt == 'openssl' + if opt == 'openssl' or opt == 'mbedtls' signatures += ['PKCS7'] else compressions += [f'@opt@'] @@ -392,6 +393,11 @@ if dep_map.get('openssl').found() libkmod_deps += dep_map['openssl'] endif +if dep_map.get('mbedtls').found() + libkmod_files += files('libkmod/libkmod-signature-mbedtls.c') + libkmod_deps += dep_map['mbedtls'] +endif + install_headers('libkmod/libkmod.h') libkmod = shared_library( diff --git a/meson_options.txt b/meson_options.txt index 9c215f50..581c420a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -60,6 +60,13 @@ option( description : 'Openssl support, PKCS7 signatures. Default: enabled', ) +option( + 'mbedtls', + type : 'feature', + value : 'disabled', + description : 'MbedTLS support, PKCS7 signatures. Default: disabled', +) + option( 'tools', type : 'boolean', @@ -70,7 +77,7 @@ option( option( 'dlopen', type : 'array', - choices : ['zstd', 'xz', 'zlib', 'openssl', 'all'], + choices : ['zstd', 'xz', 'zlib', 'openssl', 'mbedtls', 'all'], value : ['all'], description : 'Libraries to dlopen rather than linking. Default: "all"', ) diff --git a/testsuite/test-modinfo.c b/testsuite/test-modinfo.c index dc6644ba..401aa810 100644 --- a/testsuite/test-modinfo.c +++ b/testsuite/test-modinfo.c @@ -30,7 +30,7 @@ #define DEFINE_MODINFO_GENERIC_TEST(_field) \ DEFINE_MODINFO_TEST(_field, , "/mod-simple.ko") -#if ENABLE_OPENSSL +#if ENABLE_OPENSSL || ENABLE_MBEDTLS #define DEFINE_MODINFO_SIGN_TEST(_field) \ DEFINE_MODINFO_TEST(_field, -openssl, "/mod-simple-sha1.ko", \ "/mod-simple-sha256.ko", "/mod-simple-pkcs7.ko")