Skip to content

evmctl ima_sign truncates ML‑DSA‑65 signatures to 1023 bytes #31

@arusubra

Description

@arusubra

Component: ima-evm-utils / evmctl
Version: 1.6.2 (packaged build)

Environment:

  • OpenSSL: 3.5.4 (default provider active; ML‑DSA algorithms exposed)
  • OS/arch: Debian/amd64 (userspace), kernel supports IMA v2 sigs
  • Command(s): evmctl ima_sign -f --key <ML‑DSA‑65 key>
  • Also attempted: evmctl --provider default … (same result)

Summary

When signing a file with ML‑DSA‑65 keys via evmctl ima_sign, the tool reports “evm/ima signature: 1023 bytes” and writes a 1024‑byte .sig file. The signature payload appears truncated/garbled and is not a valid ML‑DSA‑65 signature (which should be ~3309 bytes). This occurs before any xattr write and persists even when forcing the default provider.

  • ML‑DSA‑65 expected signature size: 3309 bytes (per NIST FIPS 204 and common implementations).
  • MA v2 xattr format uses a 16‑bit sig_size and is intended to carry multi‑KB signatures; ext4 xattrs commonly support values up to a block (e.g., ~4 KiB), so size is not the limiting factor.
    This strongly suggests a fixed‑buffer truncation or untested code path in evmctl for non‑RSA/ECC EVP signatures.

Reproducer

1) Verify versions and provider:

evmctl --version
# evmctl 1.6.2

openssl version -a
# OpenSSL 3.5.4 30 Sep 2025 (Library: OpenSSL 3.5.4 30 Sep 2025)
# Providers show "default" active

openssl list -signature-algorithms | grep -i mldsa
# { … id-ml-dsa-65, ML-DSA-65, MLDSA65 } @ default

openssl pkey -in mldsa_65_private.pem -text -noout
# ML-DSA-65 Private-Key: (key parses fine)

(OpenSSL clearly exposes ML‑DSA‑65 in the default provider and can parse the private key.)

2) Sign any test file (e.g., a kernel module):

evmctl ima_sign -f --key mldsa_65_private.pem test_mod.ko
# hash(sha256): b6679ffc...
# evm/ima signature: 1023 bytes
# Writing to test_mod.ko.sig

ls -l test_mod.ko.sig
# -rw-r--r-- 1 root root 1024 ... test_mod.ko.sig

hexdump -C test_mod.ko.sig | head
# 00000000 03 02 04 d9 ...  (starts like IMA v2 header: type=0x03, ver=0x02)
# ... (subsequent bytes look like pointers/stack words, not ML‑DSA signature)

3) Try forcing the provider (same result):

evmctl --provider default -v ima_sign -f --key mldsa_65_private.pem test_mod.ko
# read_keyid_from_cert: ...: x509 certificate not found
# calc_keyid_v2: keyid: d97bb346
# evm/ima signature: 1023 bytes
# Writing to test_mod.ko.sig

(“1023 bytes” is reported at sign time, before any xattr operation, and the .sig on disk is 1024 bytes—indicating truncation happens inside evmctl’s signing/packaging path.)


Expected vs Actual

Expected: evmctl should produce an IMA v2 signature blob whose sig_size and sig[] reflect the ~3309‑byte ML‑DSA‑65 signature from OpenSSL’s EVP, and write a .sig around 3.3 KB (+ header).
Actual: evmctl reports 1023 bytes and writes a 1024‑byte file. The payload after the initial bytes does not resemble a valid ML‑DSA signature, and appears consistent with a 1 KiB fixed buffer fill/truncation.


Why this is not an IMA/xattr limitation

  • The IMA v2 xattr header (struct signature_v2_hdr) includes a 16‑bit sig_size and is designed for large signatures.
  • The ext4 xattr implementation commonly allows values up to a full block (e.g., ~4 KiB), so a ~3.3 KB ML‑DSA‑65 signature fits.

Analysis / Hypothesis

  • evmctl’s signing path likely assumes RSA/ECDSA‑sized outputs and uses a ~1 KiB fixed buffer (or copies into a fixed structure), truncating the EVP result.
  • The hexdumps show a plausible IMA v2 header prefix (0x03 0x02 …) followed by 64‑bit pointer‑like words (.. .. .. 00 7f 00 00) rather than a contiguous ML‑DSA signature—consistent with copying from an internal struct rather than the actual EVP output buffer.
  • OpenSSL 3.5.4’s default provider exposes and supports ML‑DSA; openssl pkey -text and openssl list -signature-algorithms prove the algorithm is available on this host

References


Suggested resolution

  1. Audit the ML‑DSA path in evmctl:

    • Ensure the EVP signature output is written into a dynamically sized buffer and that the full length returned by EVP_PKEY_sign/EVP_PKEY_sign_message (or equivalent) is preserved.
    • Avoid fixed‑size arrays for the signature payload and IMA v2 blob assembly.
  2. Add a unit/integration test that:

    • Generates an ML‑DSA‑65 key with OpenSSL,
    • Signs a short file,
    • Asserts that the resulting .sig (IMA v2) has sig_size ≈ 3309 and total file size ≈ header + 3309.
  3. (Optional) Provide a provider override flag defaulting to default for OpenSSL 3.x, and document PQC coverage expectations in the README/man page.


Attachments (from the reporter)

  • openssl version -a and provider listing (showing ML‑DSA in default provider). [man.archlinux.org]
  • evmctl verbose logs showing “evm/ima signature: 1023 bytes” and the 1024‑byte .sig.
  • First 10 lines of hexdump -C test_mod.ko.sig showing 0x03 0x02 … followed by non‑signature‑looking data (available on request).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions