Skip to content

Conversation

@jakemas
Copy link
Contributor

@jakemas jakemas commented Dec 12, 2025

Issues:

Related PRs:

  • Import-mldsa-native-NTT stress test on delocator #2903
  • AES-GCM: Add function pointer trampolines to avoid delocator issue #2919
  • Service Indicator: Add error call trampoline to avoid delocator issue #2920

Import mldsa-native

This imports mldsa-native (https://github.com/pq-code-package/mldsa-native) into AWS-LC.

This PR focuses on the minimal configuration of mldsa-native: No assembly and no FIPS-202 code are imported.

mldsa-native is a high-performance, high-assurance C90 implementation of ML-DSA developed under the Post-Quantum Cryptography Alliance (PQCA) and the Linux Foundation. It is a fork of the Dilithium reference implementation.

Import Mechanism

The mldsa-native source code is unmodified and imported using the importer script crypto/fipsmodule/ml_dsa/importer.sh; the details of the import are in META.yml.

A custom config is provided for mldsa-native which in particular includes a small 'compatibility layer' between AWS-LC/OpenSSL and mldsa-native -- see below.

Future imports (C-only)

Future updates of the C-only mldsa-native source tree should happen through a re-import of mldsa-native: That is, (a) delete crypto/fipsmodule/ml_dsa/mldsa and (b) re-run import.sh. This will re-import mldsa-native/main, though you can set the GITHUB_SHA and GITHUB_REPOSITORY environment variables to point to any other mldsa-native repository/fork.

Future imports (native code)

Once we have verified meaningful parts of the mldsa-native assembly backends, PRs will be filed to integrate those. The details for this integration are TBD and not necessary to finalize for this PR. The options are (a) extending import.sh to import larger parts of the mldsa-native upstream source tree, including native backends, (b) writing custom backends, backed by sources living in the s2n-bignum source tree. Both is possible and compatible with this PR.

Import Scope

mldsa-native has a C-only version as well as native 'backends' in AVX2 and Neon for high performance. This commit only imports the C-only version. Integration of native backends will be done separately.

mldsa-native offers its own FIPS-202 implementation, including fast versions of batched FIPS-202. However, this commit does not import those, but instead provides glue-code around AWS-LC's own FIPS-202 implementation. The path to leveraging the FIPS-202 performance improvements in mldsa-native would be to integrate them directly into crypto/fipsmodule/sha.

Impact on build

None. No build-files are modified. The multilevel build process remains unchanged.

Internal API changes

3 Removed functions:

  [D] 'function void ml_dsa_44_params_init(ml_dsa_params*)'    {ml_dsa_44_params_init}
  [D] 'function void ml_dsa_65_params_init(ml_dsa_params*)'    {ml_dsa_65_params_init}
  [D] 'function void ml_dsa_87_params_init(ml_dsa_params*)'    {ml_dsa_87_params_init}

Compatibility layer

The configuration file mldsa_native_config.h includes a compatibility layer between AWS-LC/OpenSSL and mldsa-native, covering:

  • FIPS/PCT: If AWSLC_FIPS is set, MLD_CONFIG_KEYGEN_PCT is enabled to include a PCT.
  • FIPS/PCT: If BORINGSSL_FIPS_BREAK_TESTS is set, MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST is set and mld_break_pct defined via boringssl_fips_break_test("MLDSA_PWCT"), to include runtime-breakage of the PCT for testing purposes.
  • CT: If BORINGSSL_CONSTANT_TIME_VALIDATION is set, then MLD_CONFIG_CT_TESTING_ENABLED is set to enable valgrind testing.
  • Zeroization: MLD_CONFIG_CUSTOM_ZEROIZE is set and mld_zeroize mapped to OPENSSL_cleanse to use OpenSSL's zeroization function.
  • Randombytes: MLD_CONFIG_CUSTOM_RANDOMBYTES is set and mld_randombytes mapped to RAND_bytes to use AWS-LC's randombytes function.

Side-channels

mldsa-native's CI uses a patched version of valgrind to check for various compilers and compile flags that there are no secret-dependent memory accesses, branches, or divisions. The relevant assertions have been kept but are unused unless MLD_CONFIG_CT_TESTING_ENABLED is set, which is the case if and only if BORINGSSL_CONSTANT_TIME_VALIDATION is set.

mldsa-native uses value barriers to block potentially harmful compiler reasoning and optimization. Where standard gcc/clang inline assembly is not available, mldsa-native falls back to a slower 'opt blocker' based on a volatile global -- both are described in ct.h.

Formal Verification

All C-code imported in this commit is formally verified using the C Bounded Model Checker (CBMC) to be free of various classes of undefined behaviour, including out-of-bounds memory accesses and arithmetic overflow; the latter is of particular interest for ML-DSA because of the use of lazy modular reduction for improved performance.

The heart of the CBMC proofs are function contract and loop annotations to the C-code. Function contracts are denoted __contract__(...) clauses and occur at the time of declaration, while loop contracts are denoted __loop__ and follow the for statement.

The function contract and loop statements are kept in the source, but removed by the preprocessor so long as the CBMC macro is undefined. Keeping them simplifies the import, and care has been taken to make them readable to the non-expert, and thereby serve as precise documentation of assumptions and guarantees upheld by the code.

FIPS Compliance

mldsa-native unconditionally includes stack zeroization. mldsa-native's default secure memset is replaced by OPENSSL_cleanse.

mldsa-native conditionally includes a PCT, guarded by MLD_CONFIG_KEYGEN_PCT. This is set in the config if and only if AWSLC_FIPS is set.

While not part of the FIPS standard, the pk_from_sk function includes validation of both t0 (low-order bits) and tr (hash of public key) using constant-time comparison functions (mld_ct_memcmp), providing strong assurance of key consistency.

Testing

We KAT ML-DSA with test vectors obtained from https://github.com/post-quantum-cryptography/KAT within PQDSAParameterTest.KAT. We select the KATs for the signing mode hedged, which derives the signing private random seed (rho) pseudorandomly from the signer's private key, the message to be signed, and a 256-bit string rnd which is generated at random. The pure variant of these KATs were used, as they provide test vector inputs for "pure" i.e., non-pre-hashed messages.

We also run the ACVP test vectors obtained from https://github.com/usnistgov/ACVP-Server within the three functions PerMLDSATest.ACVPKeyGen, PerMLDSATest.ACVPSigGen and PerMLDSATest.ACVPSigVer. These correspond to the tests found at ML-DSA-keyGen-FIPS204, ML-DSA-sigGen-FIPS204, and ML-DSA-sigVer-FIPS204.
To test ML-DSA pure, non-deterministic mode, we use tgId = 19, 21, 23 of sigGen and tgId = 7, 9, 11 of sigVer.
To test ML-DSA ExternalMu, non-deterministic mode, we use tgId = 20, 22, 24 of sigGen and tgId = 8, 10, 12 of sigVer.

Test Results:

  • ML-DSA Tests: 100% passing (61/61 tests)

Formatting

Code in crypto/fipsmodule/ml_dsa/mldsa is directly imported from mldsa-native and comes with its own crypto/fipsmodule/ml_dsa/mldsa/.clang-format.

Prefix build

The prefix build should not be affected by the import, since no definitions of external linkage are imported (everything is tagged either static directly, or MLD_EXTERNAL_API or MLD_INTERNAL_API, both of which are set to static in the context of the import, too).

Performance

Performance should be comparable to the previous integration as both are based on C-only code with AWS-LC's FIPS-202 implementation. The fast mldsa-native backends are not yet imported.

Multilevel build

At the core, mldsa-native is currently a 'single-level' implementation of ML-DSA: A build of the main source tree provides an implementation of exactly one of ML-DSA-44/65/87, depending on the MLD_CONFIG_PARAMETER_SET parameter.

To build all security levels, level-specific sources are built 3 times, once per security level, and linked with a single build of the level-independent code. The single-compilation-unit approach pursued by AWS-LC makes this process fairly simple since one merely needs to include the single-compilation-unit file provided by mldsa-native three times, and configure it so that the level-independent code is included only once. The final include moreover #undef'ines all macros defined by mldsa-native, reducing the risk of name clashes with other parts of crypto/fipsmodule/bcm.c.

Note that this process is entirely internal to ml_dsa.c, and does not affect the AWS-LC build.

HashML-DSA: mldsa-native includes lots of HashML-DSA functionality that we dont need in aws-lc. Perhaps we should add config upstream to mldsa-native to choose which of pure/externalmu/hash modes are imported to reduce unused code.

Main differences from reference implementation

mldsa-native is a fork of the ML-DSA reference implementation (Dilithium).

The following gives an overview of the major changes:

  • CBMC and debug annotations, and minor code restructurings or signature changes to facilitate the CBMC proofs. For example, functions are structured to make loop bounds and memory access patterns explicit for formal verification.
  • Introduction of 4x-batched versions of some functions from the reference implementation. This is to leverage 4x-batched Keccak-f1600 implementations if present. The batching happens at the C level even if no native backend for FIPS 202 is present.
  • FIPS 204 compliance: Introduced optional PCT (FIPS 204, Section 4.4, Pairwise Consistency) and zeroization of stack buffers as required by (FIPS 204, Section 3.6.3, Destruction of intermediate values).
  • Introduction of native backend implementations for AVX2. Those are drop-in replacements for the corresponding C functions and dispatched at compile-time. (Not in this PR, but the C code prep is in place).
  • Restructuring of files to separate level-specific from level-generic functionality. This is needed to enable a multi-level build of mldsa-native where level-generic code is shared between levels.
  • More pervasive use of value barriers to harden constant-time primitives, even when Link-Time-Optimization (LTO) is enabled. The use of LTO can lead to insecure compilation in case of the reference implementation.

License

mldsa-native (everything under crypto/fipsmodule/ml_dsa/mldsa/**) is imported under the Apache 2.0 license and the ISC license. The LICENSE file remains unchanged.

Integration-specific code (everything with direct parent crypto/fipsmodule/ml_dsa/*) is made under the terms of the Apache 2.0 license and the ISC license.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

github-actions[bot]

This comment was marked as duplicate.

@codecov-commenter
Copy link

codecov-commenter commented Dec 12, 2025

Codecov Report

❌ Patch coverage is 87.53754% with 166 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.08%. Comparing base (75a3e8b) to head (000c29b).

Files with missing lines Patch % Lines
crypto/fipsmodule/ml_dsa/mldsa/sign.c 68.98% 138 Missing ⚠️
crypto/fipsmodule/ml_dsa/ml_dsa.c 82.85% 18 Missing ⚠️
crypto/fipsmodule/ml_dsa/mldsa/poly.c 96.56% 7 Missing ⚠️
crypto/fipsmodule/ml_dsa/mldsa/poly_kl.c 99.17% 2 Missing ⚠️
crypto/fipsmodule/ml_dsa/mldsa/packing.c 98.59% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2902      +/-   ##
==========================================
- Coverage   78.11%   78.08%   -0.03%     
==========================================
  Files         679      682       +3     
  Lines      117949   118273     +324     
  Branches    16599    16606       +7     
==========================================
+ Hits        92130    92348     +218     
- Misses      24930    25037     +107     
+ Partials      889      888       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

github-actions[bot]

This comment was marked as duplicate.

github-actions[bot]

This comment was marked as duplicate.

@jakemas jakemas mentioned this pull request Dec 12, 2025
2 tasks
github-actions[bot]

This comment was marked as duplicate.

@jakemas jakemas marked this pull request as ready for review December 15, 2025 18:59
@jakemas jakemas requested a review from a team as a code owner December 15, 2025 18:59
This reverts commit fd8991a.
github-actions[bot]

This comment was marked as duplicate.

@jakemas jakemas force-pushed the import-mldsa-native-again branch from 2978380 to 07f1b84 Compare December 15, 2025 19:11
github-actions[bot]

This comment was marked as duplicate.

github-actions[bot]

This comment was marked as duplicate.

github-actions[bot]

This comment was marked as duplicate.

@jakemas
Copy link
Contributor Author

jakemas commented Dec 15, 2025

Opened awslabs/aws-lc-verification#180 for the SAW proof wrapper function changes.


## Testing

We KAT ML-DSA with test vectors obtained from https://github.com/post-quantum-cryptography/KAT within `PQDSAParameterTest.KAT`. We select the KATs for the signing mode `hedged`, which derives the signing private random seed (rho) pseudorandomly from the signer's private key, the message to be signed, and a 256-bit string `rnd` which is generated at random. The `pure` variant of these KATs were used, as they provide test vector inputs for "pure" i.e., non-pre-hashed messages. The KAT files have been modified to insert linebreaks between each test vector set.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
We KAT ML-DSA with test vectors obtained from https://github.com/post-quantum-cryptography/KAT within `PQDSAParameterTest.KAT`. We select the KATs for the signing mode `hedged`, which derives the signing private random seed (rho) pseudorandomly from the signer's private key, the message to be signed, and a 256-bit string `rnd` which is generated at random. The `pure` variant of these KATs were used, as they provide test vector inputs for "pure" i.e., non-pre-hashed messages. The KAT files have been modified to insert linebreaks between each test vector set.
We test ML-DSA with Known Answer Test (KAT) vectors obtained from https://github.com/post-quantum-cryptography/KAT within `PQDSAParameterTest.KAT`. We select the KATs for the signing mode `hedged`, which derives the signing private random seed (rho) pseudorandomly from the signer's private key, the message to be signed, and a 256-bit string `rnd` which is generated at random. The `pure` variant of these KATs were used, as they provide test vector inputs for "pure" i.e., non-pre-hashed messages. The KAT files have been modified to insert linebreaks between each test vector set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, added in afa0b3b

To test ML-DSA pure, non-deterministic mode, we use `tgId = 19, 21, 23` of sigGen and `tgId = 7, 9, 11` of sigVer.
To test ML-DSA ExternalMu, non-deterministic mode, we use `tgId = 20, 22, 24` of sigGen and `tgId = 8, 10, 12` of sigVer.

The test suite includes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it include testing the deterministic functions? Are these included in the KATs? The ACVP tests are not KATs? They test the non-deterministic version with random data?

Copy link
Contributor Author

@jakemas jakemas Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is referring to the deterministic variant of ML-DSA (as opposed to deterministically testing the APIs). We do not test the deterministic mode as we do not support the ML-DSA deterministic mode. Internally the API is available (we note this here:

/* Randomized variant of ML-DSA. If you need the deterministic variant,
* call crypto_sign_signature_internal directly with all-zero rnd. */
but it isn't exposed as an external API anywhere.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is referring to the deterministic variant of ML-DSA

Do you mean "non-deterministic"? If so, I'd like to know how it's tested in ACVP, with KATs or random data making sure it verifies the signature? If KATs, how are they set up to bypass randomness generation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two versions of ml-dsa, deterministic and non-deterministic, we only implement the non-deterministic variant. To test the non-deterministic variant we do it two ways, one using the ACVP test vectors, and one using the KATs. In both cases, we use the internal APIs which allow the tester to provide the randomness for each signature via the provided seed argument. So there is no need to bypass the random generation, we use the provided seeds and supply them directly the the API to get the expected known result.

- FIPS 204 compliance: Introduced optional PCT (FIPS 204, Section 4.4, Pairwise
Consistency) and zeroization of stack buffers as required by (FIPS 204,
Section 3.6.3, Destruction of intermediate values).
- Introduction of native backend implementations for AVX2. Those are drop-in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the introduction implemented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch -- not added in this PR now. Removed this line in afa0b3b

#if !defined(__ASSEMBLER__)
#include "../../internal.h"

// Define inline before mldsa-native headers are included
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe some of these definitions up until line 31 are in sys.h in the case of mlkem. Will they be put there when the assembly back end is imported? It doesn't seem to me they were moved in PR#2903.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct, they are already in sys.h

#if !defined(MLD_INLINE)
#if !defined(inline)
#if defined(_MSC_VER)
#define MLD_INLINE __inline
/* Don't combine __inline and __forceinline */
#define MLD_ALWAYS_INLINE __forceinline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define MLD_INLINE inline
#define MLD_ALWAYS_INLINE MLD_INLINE __attribute__((always_inline))
#else
#define MLD_INLINE __attribute__((unused))
#define MLD_ALWAYS_INLINE MLD_INLINE
#endif
#else /* !inline */
#define MLD_INLINE inline
#define MLD_ALWAYS_INLINE MLD_INLINE __attribute__((always_inline))
#endif /* inline */
#endif /* !MLD_INLINE */

I removed the duplicate definitions in 79c9a4c

Comment on lines +49 to +50
#define MLD_CONFIG_INTERNAL_API_QUALIFIER static __attribute__((unused))
#define MLD_CONFIG_EXTERNAL_API_QUALIFIER static __attribute__((unused))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added to prevent errors regarding the unused APIs -- unlike ML-KEM there are some additional API options for ML-DSA. Many of the internals are shared, so there isn't much redundant code, but I have opened an issue upstream pq-code-package/mldsa-native#740 to add to the import options to be more specific about exact APIs that are included.

aws-lc/crypto/fipsmodule/ml_dsa/mldsa/sign.c:1053:5: error: unused function 'mldsa87_verify_pre_hash_shake256' [-Werror,-Wunused-function]

static MLD_INLINE int mld_sys_check_capability(int cap)
{
#if defined(MLD_SYS_X86_64)
if (cap == 1) // MLD_SYS_CAP_AVX2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to the previous question, but also in PR#2903, it doesn't seem like the macro was used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've aligned crypto/fipsmodule/ml_dsa/mldsa_native_config.h to look more like crypto/fipsmodule/ml_kem/mlkem_native_config.h in 79c9a4c and removed


// Map memset function to the one used by AWS-LC
#define MLD_CONFIG_CUSTOM_MEMSET
#if !defined(__ASSEMBLER__) && !defined(MLD_CONFIG_MULTILEVEL_NO_SHARED)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if !defined(__ASSEMBLER__) && !defined(MLD_CONFIG_MULTILEVEL_NO_SHARED)
#if !defined(__ASSEMBLER__)

as in mlkem_native_config.h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've aligned crypto/fipsmodule/ml_dsa/mldsa_native_config.h to look more like crypto/fipsmodule/ml_kem/mlkem_native_config.h in 79c9a4c and removed.


// Map memcpy function to the one used by AWS-LC
#define MLD_CONFIG_CUSTOM_MEMCPY
#if !defined(__ASSEMBLER__) && !defined(MLD_CONFIG_MULTILEVEL_NO_SHARED)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if !defined(__ASSEMBLER__) && !defined(MLD_CONFIG_MULTILEVEL_NO_SHARED)
#if !defined(__ASSEMBLER__)

as in mlkem_native_config.h

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've aligned crypto/fipsmodule/ml_dsa/mldsa_native_config.h to look more like crypto/fipsmodule/ml_kem/mlkem_native_config.h in 79c9a4c and removed.

Resolved conflict: kept deletion of crypto/fipsmodule/ml_dsa/ml_dsa_ref/packing.c
github-actions[bot]

This comment was marked as duplicate.

github-actions[bot]

This comment was marked as duplicate.

- Removed inline macro definitions from config (now only in sys.h)
- Fixed capability function to use mld_sys_cap enum instead of int
- Added sys.h includes to all functions needing MLD_INLINE
- Removed MLD_CONFIG_MULTILEVEL_NO_SHARED guards from memcpy/memset
- Kept __attribute__((unused)) for API qualifiers (needed for ML-DSA)

Addresses code review comments about aligning with ML-KEM structure.
github-actions[bot]

This comment was marked as duplicate.

@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 170. Check the log or trigger a new build to see more.

@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 170. Check the log or trigger a new build to see more.

@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@aws aws deleted a comment from github-actions bot Jan 16, 2026
@jakemas jakemas enabled auto-merge (squash) January 17, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants