xref: /freebsd/crypto/openssl/doc/designs/ml-dsa.md (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1ML-DSA Design
2==============
3
4This document covers OpenSSL specific ML-DSA implementation details.
5FIPS 204 clearly states most of the requirements of ML-DSA and has comprehensive
6pseudo code for all its algorithms.
7
8The base code for OpenSSL has been derived from the BoringSSL C++ code.
9As OpenSSL is C code, templates can not be used. The OpenSSL code instead uses
10parameters to pass algorithm specific constants, and also uses these constants
11to run different conditional functions when required.
12
13ML-DSA Parameters & Per algorithm Functions
14-------------------------------------------
15
16FIPS 204 contains 3 algorithms for different security strengths.
17FIPS 204 Section 4 Table 1 & Table 2 specifies different constants that are
18stored in a table of `ML_DSA_PARAM` objects.
19The constants include key sizes and coefficient ranges.
20
21OpenSSL uses 3 key managers and 3 signature functions corresponding to the algorithms
22ML-DSA-44, ML-DSA-65 and ML-DSA-87.
23
24ML-DSA only uses SHAKE-128 and SHAKE-256 for digest operations, so these digest
25algorithms are pre-fetched and stored within a `ML_DSA` key.
26Any functions that require these pre-fetched objects must pass either the key
27or the pre-fetched object within the key as a parameter.
28A temporary `EVP_MD_CTX` is created as needed and the shake object(s) are set
29into this ctx.
30
31Initially a separate object called `ML_DSA_CTX` object was passed around that
32contained 2 `EVP_MD_CTXs` with the pre-fetched `EVP_MD` shake objects. It was
33modified to match the ML-KEM code.
34
35ML-DSA keys
36------------
37
38Once loaded an 'ML-DSA-KEY' object contains either a public key or a
39public/private key pair.
40When loading a private key, the public key is always generated. In the event
41that the public key is also supplied, an error will occur if the generated public
42key does not match the supplied public key.
43
44An `ML_DSA` polynomial contains 256 32 bit values which is 1K of space.
45Keys store vectors of size 'k' or 'l' plus a matrix of size 'k' * 'l',
46where (k, l) correspond to (4,4), (6,5) or (8,7). The key data could be large
47if buffers of the maximum size of (8,7) are used for the (4,4) use case.
48To save space rather than use fixed size buffers, allocations are used instead,
49for both the public and private elements. (i.e. The private allocations are not
50done when only a public key is loaded).
51
52Since keys consist of vectors and a matrix of polynomials, a single block
53is used to allocate all polynomials, and then the polynomial blocks are
54assigned to the individual vectors and matrix. This approach is also used when temporary
55vectors, matrices or polynomials are required
56
57Keys are not allowed to mutate, so checks are done during load to check that the
58public and private key components are not changed once set.
59
60`ossl_ml_dsa_key_get_pub()` and `ossl_ml_dsa_key_get_priv()` return the
61encoded forms of the key components (which are stored within the key).
62The hash of the encoded public key is also stored in the key.
63
64The key generation process uses a seed to create a private key, and the public
65key is then generated using this private key.
66
67For ACVP testing the seed may be supplied.
68
69Pure vs Pre Hashed Signature Generation
70----------------------------------------
71
72The normal signing process (called Pure ML-DSA Signature Generation)
73encodes the message internally as 0x00 || len(ctx) || ctx || message.
74where B<ctx> is some optional value of size 0x00..0xFF.
75
76ACVP Testing requires the ability to process raw messages without the above encoding.
77This will be controlled by settable parameters.
78
79Pre Hash ML-DSA Signature Generation encode the message as
80
81```text
820x01 || len(ctx) || ctx || digest_OID || H(message).
83```
84
85The scenario that is stated that this is useful for is when this encoded message
86is supplied from an external source.
87This ensures domain separation between signature variants
88
89Currently OpenSSL does not support the Pre Hash variant as this does not sit
90well with the OpenSSL API's.
91The user could do the encoding themselves and then set the settable to not
92encode the passed in message.
93
94Signing API
95-------------
96
97As only the one-shot implementation is required and the message is not digested
98the API's used should be
99
100`EVP_PKEY_sign_message_init()`, `EVP_PKEY_sign()`,
101`EVP_PKEY_verify_message_init()`, `EVP_PKEY_verify()`.
102
103OpenSSL command line support
104----------------------------
105
106For backwards compatability reasons `EVP_DigestSignInit_ex()`,
107`EVP_DigestSign()`, `EVP_DigestVerifyInit_ex()` and `EVP_DigestVerify()` may
108also be used, but the digest passed in `mdname` must be NULL (i.e. it
109effectively behaves the same as above).
110Passing a non NULL digest results in an error.
111
112`OSSL_PKEY_PARAM_MANDATORY_DIGEST` must return "" in the key manager getter and
113`OSSL_SIGNATURE_PARAM_ALGORITHM_ID` in the signature context getter.
114
115Encoding/Decoding
116-----------------
117
118Where it makes sense to, WPACKET is used for output (such as signature generation)
119and PACKET for reading signature data.
120
121Constant Time Considerations
122----------------------------
123
124Similar code to BoringSSL will be added that allows ctgrind to be used to
125detect constant time issues.
126
127There are many places that do hashing in the code, and these are capable (although
128it is not likely) of returning errors. There is not attempt to deal with these cases.
129
130Changes from BoringSSL
131----------------------
132
133At the time of writing, BoringSSL code only supported ML-DSA-65. Since there
134is specialized code for encoding and decoding of different sizes of
135polynomial coefficients, code was added to support these different sizes
136(e.g hints have 1 bit coefficients so 8 coefficients can be packed into 1 byte)
137
138Differences between BoringSSL and FIPS 204 pseudo code
139------------------------------------------------------
140
141The symmetric modulus operation normally gives a result in the range -a/2 ...
142a/2.
143BoringSSL chose to keep the result positive by adding q and reducing once as
144required.
145
146Montgomery multiplication is used to speed up multiplications (See FIPS 204
147Appendix A).
148