diff --git a/doc/designs/ml-dsa.md b/doc/designs/ml-dsa.md new file mode 100644 index 0000000000..acadcf8d29 --- /dev/null +++ b/doc/designs/ml-dsa.md @@ -0,0 +1,126 @@ +ML-DSA Design +============== + +This document covers OpenSSL specific ML-DSA implementation details. +FIPS 204 clearly states most of the requirements of ML-DSA and has comprehensive +pseudo code for all its algorithms. + +The base code for OpenSSL has been derived from the BoringSSL C++ code. +As OpenSSL is c code, templates can not be used. The OpenSSL code instead uses +parameters to pass algorithm specific constants, and also uses these constants +to run different conditional functions when required. + +ML_DSA Parameters & Per algorithm Functions +------------------------------------------- + +FIPS 204 contains 3 algorithms for different security strengths. +FIPS 204 Section 4 Table 1 & Table 2 specifies different constants that are +stored in a table of ML_DSA_PARAM objects. +The constants include key sizes and coefficient ranges. + +OpenSSL uses 3 key managers and 3 signature functions corresponding to the algorithms +ML-DSA-44, ML-DSA-65 and ML-DSA-87. + +ML-DSA only uses SHAKE-128 and SHAKE-256 for digest operations, so these values +are pre-fetched and stored within a ML_DSA key. Any functions that require these +pre-fetched objects must pass either the key or the pre-fetched object within the key +as a parameter. A temporary EVP_MD_CTX is created as needed and the shake object(s) +are set into this ctx. + +Initially a separate object called ML_DSA_CTX object was passed around that +contained 2 EVP_MD_CTX's containing the pre-fetched EVP_MD shake objects. It was +modified to match the ML-KEM code. + +ML-DSA keys +------------ + +Once loaded an 'ML-DSA-KEY' object contains either a public key or a +public/private key pair. +When loading a private key, the public key is always generated. In the event +that the public key is also supplied, an error will occur if the generated public +key does not match the supplied public key. + +An ML_DSA polynomial contains 256 32 bit values which is 1K of space. +Keys store vectors of size 'k' or 'l' plus a matrix of size 'k' * 'l', +where (k, l) correspond to (4,4), (6,5) or (8,7). The key data could be large +if buffers of the maximum size of (8,7) are used for the (4,4) use case. +To save space rather than use fixed size buffers, allocations are used instead, +for both the public and private elements. (i.e. The private allocations are not +done when only a public key is loaded). + +Since keys consist of vectors and a matrix of polynomials, a single block +is used to allocate all polynomials, and then the polynomial blocks are +assigned to the individual vectors and matrix. This approach is also used when temporary +vectors, matrices or polynomials are required + +Keys are not allowed to mutate, so checks are done during load to check that the +public and private key components are not changed once set. + +ossl_ml_dsa_key_get_pub() and ossl_ml_dsa_key_get_priv() return the +encoded forms of the key components (which are stored within the key). +The hash of the encoded public key is also stored in the key. + +The key generation process uses a seed to create a private key, and the public +key is then generated using this private key. + +For ACVP testing the seed may be supplied. + +Pure vs Pre Hashed Signature Generation +---------------------------------------- + +The normal signing process (called Pure ML-DSA Signature Generation) +encodes the message internally as 0x00 || len(ctx) || ctx || message. +where B is some optional value of size 0x00..0xFF. + +ACVP Testing requires the ability to process raw messages without the above encoding. +This will be controlled by settable parameters. + +Pre Hash ML-DSA Signature Generation encode the message as +0x01 || len(ctx) || ctx || digest_OID || H(message). +The scenario that is stated that this is useful for is when this encoded message +is supplied from an external source. +This ensures domain separation between signature variants + +Currently I do not support the Pre Hash variant as this does not sit well with the +OpenSSL API's. The user could do the encoding themselves and then set the settable +to not encode the passed in message. + +Signing API +------------- + +As only the one-shot implementation is required and the message is not digested +the API's used should be + +EVP_PKEY_sign_message_init(), EVP_PKEY_sign(), +EVP_PKEY_verify_message_init(), EVP_PKEY_verify(). + +Encoding/Decoding +----------------- + +Where it makes sense to, WPACKET is used for output (such as signature generation) +and PACKET for reading signature data. + +Constant Time Considerations +---------------------------- + +Similar code to BoringSSL will be added that allows ctgrind to be used to +detect constant time issues. + +There are many places that do hashing in the code, and these are capable (although +it is not likely) of returning errors. There is not attempt to deal with these cases. + +Changes from BoringSSL +---------------------- + +At the time of writing, BoringSSL code only supported ML-DSA-65. Since there +is specialized code for encoding and decoding of different sizes of +polynomial coefficients, code was added to support these different sizes +(e.g hints have 1 bit coefficients so 8 coefficients can be packed into 1 byte) + +Differences between BoringSSL and FIPS 204 pseudo code +------------------------------------------------------ + +The symmetric modulus operation normally gives a result in the range -a/2 ... a/2. +BoringSSL chose to keep the result positive by adding q and reducing once is required. + +Montgomery multiplication is used to speed up multiplications (See FIPS 204 Appendix A). diff --git a/doc/man7/EVP_PKEY-ML-DSA.pod b/doc/man7/EVP_PKEY-ML-DSA.pod index 39bc6215fb..bb8802000f 100644 --- a/doc/man7/EVP_PKEY-ML-DSA.pod +++ b/doc/man7/EVP_PKEY-ML-DSA.pod @@ -42,12 +42,19 @@ Use EVP_PKEY_CTX_set_params() after calling EVP_PKEY_keygen_init(). =head2 Common ML-DSA parameters In addition to the common parameters that all keytypes should support (see -L), the implementation of these key types -support the following. +L, the implementation of +these key types support the following. The following parameters are gettable using EVP_PKEY_get_octet_string_param(), and settable when using EVP_PKEY_fromdata(). +The following octet string parameters are gettable using +L or L. +They can be set into L, and are returned by +L given a suitable I. +Once a public or private key is configured, it can no longer be modified, +nor can another key component be added. + =over 4 =item "pub" (B) diff --git a/doc/man7/EVP_SIGNATURE-ML-DSA.pod b/doc/man7/EVP_SIGNATURE-ML-DSA.pod index 010690cfb4..4fed0d9e19 100644 --- a/doc/man7/EVP_SIGNATURE-ML-DSA.pod +++ b/doc/man7/EVP_SIGNATURE-ML-DSA.pod @@ -20,7 +20,7 @@ There are 3 different security categories also depending on the type. L can be used to explicitely fetch one of the 3 algorithms which can then be used with L, L, L, and -L to sign or verify one-shot messages. +L to perform one-shot message signing or signature verification. The normal signing process (called Pure ML-DSA Signature Generation) encodes the message internally as 0x00 || len(ctx) || ctx || message. diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod index 8544e3263d..02c6271c04 100644 --- a/doc/man7/OSSL_PROVIDER-default.pod +++ b/doc/man7/OSSL_PROVIDER-default.pod @@ -191,7 +191,11 @@ The OpenSSL default provider supports these operations and algorithms: =item SM2 -=item ML-DSA, see L +=item ML-DSA-44, see L + +=item ML-DSA-65, see L + +=item ML-DSA-87, see L =item HMAC, see L @@ -267,7 +271,11 @@ The OpenSSL default provider supports these operations and algorithms: =item SM2, see L -=item ML-DSA, see L +=item ML-DSA-44, see L + +=item ML-DSA-65, see L + +=item ML-DSA-87, see L =back