microsoft / microsoft/mu_crypto_release

Enforce digest strength pairing for ML-DSA pre-hash (ML-DSA-MU) to prevent downgrade attacks

Open
#281 0 comments 0 reactions 0 assignees View on GitHub
impact:security
Dominant language
Assembly
Stars
21
Forks
23
Avg merge
8d 16h
Merged PRs (30d)
3

Description

## Context

Follow-up from review of PR #252 (OpensslPkg: Update openssl submodule to 4.0.0), specifically the changes in `OpensslPkg/Library/OpensslLib/OpensslStub/uefiprov.c`:
https://github.com/microsoft/mu_crypto_release/pull/252/changes#diff-dc489f55d9f86f846dfaf6cfbf238cb6dc0b1211ed48611ba8841cf1fdeb75c2

That PR registers `ossl_ml_dsa_mu_functions` (the ML-DSA "external mu" / pre-hash signature path) along with SHAKE-128/256 digests in the default provider's algorithm tables:

```c
#ifndef OPENSSL_NO_SHAKE
{ PROV_NAMES_SHAKE_128, "provider=default", ossl_shake_128_functions },
{ PROV_NAMES_SHAKE_256, "provider=default", ossl_shake_256_functions },
#endif
#ifndef OPENSSL_NO_ML_DSA
{ PROV_NAMES_ML_DSA_MU, "provider=default", ossl_ml_dsa_mu_functions },
#endif
```

as well as ML-DSA-44/65/87 signature and keymgmt functions, and ML-KEM/composite KEM (`deflt_asym_kem`) support.

## Problem

With ML-DSA-MU (pre-hash) enabled, a signer/verifier could pair a high-security-level ML-DSA key (e.g., ML-DSA-87, nominally NIST Category 5 / ~256-bit strength) with a weak pre-hash digest (e.g., SHA-1, MD5, or even SHA-256) in a CMS/PKCS#7 `SignerInfo`. Because the overall signature's effective security is bounded by `min(security_strength(ML-DSA level), security_strength(digest))`, this allows a silent downgrade of the claimed security strength — the same class of "hash substitution" / weak-digest attack that has historically affected RSA-PSS/ECDSA/PKCS#1v1.5 constructions.

Today there does not appear to be any enforced pairing/allow-list between the ML-DSA parameter set in use and the digest algorithm used for pre-hashing, either at the OpenSSL provider layer or in the UEFI PKCS#7 verification wrapper.

For reference, a similar (but simpler, non-ML-DSA-aware) enforcement pattern already exists for classical algorithms in `mu_basecore`'s `SecurityPkg/Library/AuthVariableLib/AuthService.c`, which restricts `SignedData.digestAlgorithms` to SHA-256/384/512, with parsing handled in `WrapPkcs7Data()` in `CryptPkcs7VerifyCommon.c`. That pattern is a flat allow-list independent of the signing algorithm/key strength — it would need to become key-strength-aware to properly cover ML-DSA.

## Proposed Fix (two complementary layers)

1. **Provider-level allow-list/strength check** near `ossl_ml_dsa_mu_functions` (or a thin wrapping dispatch table in `uefiprov.c` that intercepts sign/verify init calls before delegating): reject digests weaker than what's appropriate for the ML-DSA parameter set in use (e.g., reject SHA-1, MD5, SHA-224, and arguably SHA-256 for ML-DSA-87), mirroring NIST SP 800-57 strength-pairing guidance. This is the most robust fix since it can't be bypassed by whatever constructs the CMS `SignerInfo`.
2. **UEFI crypto wrapper check** in `CryptPkcs7VerifyCommon.c` (present in `OpensslPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c` and the `MbedTlsPkg` equivalent) — parse both `SignerInfo.digestAlgorithm` and the signature algorithm/key type, and enforce a minimum digest strength conditional on the ML-DSA level (44/65/87) or composite OID in use, extending the existing flat SHA-256/384/512 allow-list pattern seen in `AuthService.c` to be ML-DSA-parameter-set-aware. Since this file is duplicated across BaseCryptLib instances, the fix likely needs to be applied in each instance (or consolidated).

## Additional considerations
- If IETF composite signature OIDs (draft-ietf-lamps-pq-composite-sigs) are used instead of raw ML-DSA-MU, the digest is already fixed per-OID (e.g., `id-MLDSA87-ECDSA-P384-SHA512` implies SHA-512) — validation there just needs to confirm the OID matches the actual digest used, rather than allowing arbitrary pairing.
- This should be tracked as a hardening/security follow-up before ML-DSA pre-hash paths are relied upon in production signing/verification flows.

## Acceptance Criteria
- [ ] Define and document a strength-pairing table between ML-DSA parameter sets (44/65/87) and minimum acceptable pre-hash digest algorithms.
- [ ] Add enforcement at the OpenSSL provider layer (or UEFI stub wrapper) for ML-DSA-MU sign/verify operations.
- [ ] Add/extend enforcement in `CryptPkcs7VerifyCommon.c` (all BaseCryptLib instances) to validate digest algorithm strength against the ML-DSA/composite signature algorithm in the SignerInfo.
- [ ] Add unit tests covering rejection of weak digest + strong ML-DSA key combinations.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the ML-DSA-MU registration in OpensslPkg/Library/OpensslLib/OpensslStub/uefiprov.c, then compare the existing digest handling in CryptPkcs7VerifyCommon.c and AuthService.c. Define the 44/65/87 digest-strength table, enforce it in the provider or UEFI wrapper and all relevant BaseCryptLib instances, and add tests covering weak digests with strong ML-DSA keys.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
cryptography, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.