GitHub Security Advisory: GHSA-5x2r-hc65-25f9
Package: ml-dsa (RUST)
Severity: MODERATE (CVSS 5.3)
CWE: CWE-347: Improper Verification of Cryptographic Signature
Vulnerable versions: >= 0.0.4, < 0.1.0-rc.4
Patched in: 0.1.0-rc.4

Summary: ML-DSA Signature Verification Accepts Signatures with Repeated Hint Indices

Details:
**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`:

```rust
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 (`<`):

```rust
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 Conce