]> git.ipfire.org Git - thirdparty/kmod.git/commitdiff
mbedlts: add as alternative to openssl
authorEmil Velikov <emil.l.velikov@gmail.com>
Sun, 15 Feb 2026 21:11:58 +0000 (21:11 +0000)
committerLucas De Marchi <demarchi@kernel.org>
Sat, 20 Jun 2026 16:13:26 +0000 (11:13 -0500)
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 <emil.l.velikov@gmail.com>
Link: https://github.com/kmod-project/kmod/pull/426
Signed-off-by: Lucas De Marchi <demarchi@kernel.org>
README.md
build-dev.ini
libkmod/libkmod-internal-signature.h
libkmod/libkmod-signature-mbedtls.c [new file with mode: 0644]
libkmod/libkmod-signature.c
meson.build
meson_options.txt
testsuite/test-modinfo.c

index a5ef3d3d877ec02baa49aba39d9e4be420ed8913..84af2981291b62eafd5e2499830d9a891e6b4414 100644 (file)
--- 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:
index 804893f48201a99868cf2714212e58283ef02566..62ada8347c2c8e14483c2a6640148fcf2a5397b7 100644 (file)
@@ -11,6 +11,7 @@ zstd = 'enabled'
 xz = 'enabled'
 zlib = 'enabled'
 openssl = 'enabled'
+mbedtls = 'disabled'
 werror = true
 b_sanitize = 'address,undefined'
 
index b58193d539006530e6588e544ad68e440c4db467..0fd512299876cd1570b67a74bf4c7cc52605adaa 100644 (file)
@@ -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 (file)
index 0000000..7794de1
--- /dev/null
@@ -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 <inttypes.h>
+#include <mbedtls/oid.h> // for MBEDTLS_OID_DIGEST_ALG_*
+#include <mbedtls/pkcs7.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <shared/elf-note.h>
+#include <shared/util.h>
+
+#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;
+}
index 9f9181ee929861af5c185e63a5af07c5271caeda..4e251e63efb402915b6f0c9caf0a17c91c6d0ce1 100644 (file)
@@ -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;
index 076c552beb2bcf0e63a50fabf44a34cc2bbfa261..6af3027c42e32577ed1ee09e0500bf806f136f83 100644 (file)
@@ -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(
index 9c215f50da0f54070b2aa0e5dd139db931e797ee..581c420a38b0d4c6de64b6cfd438f2784d244bfd 100644 (file)
@@ -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"',
 )
index dc6644bafdbd6eaf991243cd30569ba63937133d..401aa8108cb144201113d5911c08960f90c054f1 100644 (file)
@@ -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")