Skip to content

ML-DSA Signature Verification Accepts Signatures with Repeated Hint Indices

Moderate severity GitHub Reviewed Published Jan 27, 2026 in RustCrypto/signatures • Updated Jan 28, 2026

Package

cargo ml-dsa (Rust)

Affected versions

>= 0.0.4, < 0.1.0-rc.4

Patched versions

0.1.0-rc.4

Description

Affected Crate: ml-dsa
Affected Versions: v0.1.0-rc.2 (and commits since b01c3b7)
Severity: Medium
Reporter: Oren Yomtov (Fireblocks)

Summary

The ML-DSA signature verification implementation in the RustCrypto ml-dsa crate incorrectly accepts signatures with repeated (duplicate) hint indices. According to the ML-DSA specification (FIPS 204 / RFC 9881), hint indices within each polynomial must be strictly increasing. The current implementation uses a non-strict monotonic check (<= instead of <), allowing duplicate indices.

Note: This is a regression bug. The original implementation was correct, but commit b01c3b7 ("Make ML-DSA signature decoding follow the spec (#895)", fixing issue #894) inadvertently changed the strict < comparison to <=, introducing the vulnerability.

Vulnerability Details

Root Cause

The vulnerability is located in the monotonic helper function in ml-dsa/src/hint.rs:

fn monotonic(a: &[usize]) -> bool {
    a.iter().enumerate().all(|(i, x)| i == 0 || a[i - 1] <= *x)
}

The comparison operator <= allows equal consecutive values, meaning duplicate hint indices are not rejected. The correct implementation should use strict less-than (<):

fn monotonic(a: &[usize]) -> bool {
    a.iter().enumerate().all(|(i, x)| i == 0 || a[i - 1] < *x)
}

Regression Analysis

  • Original correct code (commit 1d3a1d1 - "Add support for ML-DSA (#877)"): Used < (strict)
  • Bug introduced (commit b01c3b7 - "Make ML-DSA signature decoding follow the spec (#895)"): Changed to <=

The commit message suggests it was intended to fix issue #894 and make decoding follow the spec, but the change to the monotonic function was in the wrong direction. The other changes in that commit (to use_hint function) may have been correct, but this specific change introduced signature malleability.

Technical Impact

This vulnerability allows signature malleability - the same logical signature can have multiple valid byte-level encodings. An attacker can take a valid signature and create additional "valid" signatures by duplicating hint indices.

Per the ML-DSA specification (FIPS 204, Section 6.2 and Algorithm 26 HintBitUnpack), hint indices must be strictly increasing to ensure a unique, canonical encoding. Accepting non-canonical signatures can lead to:

  1. Signature Malleability: Multiple distinct byte sequences verify as valid for the same message/key pair
  2. Protocol-Level Vulnerabilities: Systems that rely on signature uniqueness (e.g., for transaction deduplication, replay protection, or signature-based identifiers) may be vulnerable
  3. Interoperability Issues: Non-compliant signatures may be rejected by other conforming implementations

Affected Security Levels

All ML-DSA parameter sets are affected:

  • ML-DSA-44 (NIST Security Level 2)
  • ML-DSA-65 (NIST Security Level 3)
  • ML-DSA-87 (NIST Security Level 5)

Proof of Concept

See the file poc_mldsa_repeated_hint.rs for a standalone proof of concept that demonstrates the vulnerability.

The PoC uses test vectors from the Wycheproof test suite that specifically test for this invalid encoding:

  • Test Vector Source: Wycheproof ML-DSA Test Vectors
  • Test Case ID 18: "signature with a repeated hint"
  • Expected Result: invalid
  • Actual Result: valid (BUG)

Remediation

Update the monotonic function in ml-dsa/src/hint.rs to use strict less-than comparison:

fn monotonic(a: &[usize]) -> bool {
    a.iter().enumerate().all(|(i, x)| i == 0 || a[i - 1] < *x)
}

Design Intent: ML-DSA is NOT Intended to Allow Malleability

While some cryptographic libraries intentionally permit signature malleability for compatibility or performance reasons, ML-DSA is explicitly designed to prevent it:

  1. FIPS 204 Specification: ML-DSA is designed to be strongly unforgeable under chosen message attacks (SUF-CMA). This security property explicitly prevents signature malleability.

  2. NIST PQC Forum Discussion: In February 2024, there was a discussion on the NIST PQC forum about potential malleability in ML-DSA's hint unpacking. The consensus was that ML-DSA is intended to be SUF-CMA, meaning any malleability issues should be considered bugs and fixed.

  3. No Documentation of Intentional Malleability: There is no documentation in the RustCrypto ml-dsa crate, FIPS 204, or RFC 9881 suggesting that signature malleability is an acceptable or intentional property.

  4. Regression Bug: The fact that the original implementation had strict ordering (<) and this was changed to non-strict (<=) in a "fix" commit suggests this was an unintentional regression, not a design decision.

References

@tarcieri tarcieri published to RustCrypto/signatures Jan 27, 2026
Published by the National Vulnerability Database Jan 28, 2026
Published to the GitHub Advisory Database Jan 28, 2026
Reviewed Jan 28, 2026
Last updated Jan 28, 2026

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
Low
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N

EPSS score

Exploit Prediction Scoring System (EPSS)

This score estimates the probability of this vulnerability being exploited within the next 30 days. Data provided by FIRST.
(2nd percentile)

Weaknesses

Improper Verification of Cryptographic Signature

The product does not verify, or incorrectly verifies, the cryptographic signature for data. Learn more on MITRE.

CVE ID

CVE-2026-24850

GHSA ID

GHSA-5x2r-hc65-25f9

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.