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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions