ML-DSA: Add a digest that can calculate external mu.
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Tim Hudson <tjh@openssl.org> Reviewed-by: Simo Sorce <simo@redhat.com> Reviewed-by: Paul Dale <paul.dale@oracle.com> (Merged from https://github.com/openssl/openssl/pull/29223)
This commit is contained in:
parent
faf48d097b
commit
175cda569d
23 changed files with 878 additions and 54 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -161,6 +161,7 @@ providers/implementations/digests/digestcommon.inc
|
|||
providers/implementations/digests/mdc2_prov.inc
|
||||
providers/implementations/digests/sha2_prov.inc
|
||||
providers/implementations/digests/sha3_prov.inc
|
||||
providers/implementations/digests/ml_dsa_mu_prov.inc
|
||||
providers/implementations/include/prov/blake2_params.inc
|
||||
providers/implementations/kdfs/snmpkdf.inc
|
||||
providers/implementations/macs/cmac_prov.inc
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ OpenSSL 4.0
|
|||
|
||||
### Changes between 3.6 and 4.0 [xx XXX xxxx]
|
||||
|
||||
* Added "ML-DSA-MU" digest algorithm support.
|
||||
|
||||
*Shane Lontis*
|
||||
|
||||
* Removed configure options can now only be disabled. You may continue to use
|
||||
`disable-<feature>`, which will remain supported. Using `enable-<feature>`
|
||||
for a removed feature is no longer permitted.
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ DEPEND[]=include/openssl/asn1.h \
|
|||
providers/implementations/digests/mdc2_prov.inc \
|
||||
providers/implementations/digests/sha2_prov.inc \
|
||||
providers/implementations/digests/sha3_prov.inc \
|
||||
providers/implementations/digests/ml_dsa_mu_prov.inc \
|
||||
providers/implementations/include/prov/blake2_params.inc \
|
||||
providers/implementations/macs/cmac_prov.inc \
|
||||
providers/implementations/macs/gmac_prov.inc \
|
||||
|
|
@ -232,6 +233,7 @@ DEPEND[providers/implementations/asymciphers/rsa_enc.inc \
|
|||
providers/implementations/ciphers/cipher_rc4_hmac_md5.inc \
|
||||
providers/implementations/ciphers/cipher_sm4_xts.inc \
|
||||
providers/implementations/digests/blake2_prov.inc \
|
||||
providers/implementations/digests/ml_dsa_mu_prov.inc \
|
||||
providers/implementations/digests/digestcommon.inc \
|
||||
providers/implementations/digests/mdc2_prov.inc \
|
||||
providers/implementations/digests/sha2_prov.inc \
|
||||
|
|
@ -397,6 +399,8 @@ GENERATE[providers/implementations/digests/sha3_prov.inc]=\
|
|||
providers/implementations/digests/sha3_prov.inc.in
|
||||
GENERATE[providers/implementations/include/prov/blake2_params.inc]=\
|
||||
providers/implementations/include/prov/blake2_params.inc.in
|
||||
GENERATE[providers/implementations/digests/ml_dsa_mu_prov.inc]=\
|
||||
providers/implementations/digests/ml_dsa_mu_prov.inc.in
|
||||
GENERATE[providers/implementations/macs/cmac_prov.inc]=\
|
||||
providers/implementations/macs/cmac_prov.inc.in
|
||||
GENERATE[providers/implementations/macs/gmac_prov.inc]=\
|
||||
|
|
|
|||
|
|
@ -65,32 +65,29 @@ static void signature_init(ML_DSA_SIG *sig,
|
|||
* @param ctx_len The size of |ctx|. It must be in the range 0..255
|
||||
* @returns an EVP_MD_CTX if the operation is successful, NULL otherwise.
|
||||
*/
|
||||
|
||||
EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
|
||||
EVP_MD_CTX *ossl_ml_dsa_mu_init_int(EVP_MD *shake256_md,
|
||||
const uint8_t *tr, size_t tr_len, int encode, int prehash,
|
||||
const uint8_t *ctx, size_t ctx_len)
|
||||
{
|
||||
EVP_MD_CTX *md_ctx;
|
||||
uint8_t itb[2];
|
||||
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
md_ctx = EVP_MD_CTX_new();
|
||||
if (md_ctx == NULL)
|
||||
return NULL;
|
||||
|
||||
/* H(.. */
|
||||
if (!EVP_DigestInit_ex2(md_ctx, key->shake256_md, NULL))
|
||||
if (!EVP_DigestInit_ex2(md_ctx, shake256_md, NULL))
|
||||
goto err;
|
||||
/* ..pk (= key->tr) */
|
||||
if (!EVP_DigestUpdate(md_ctx, key->tr, sizeof(key->tr)))
|
||||
if (!EVP_DigestUpdate(md_ctx, tr, tr_len))
|
||||
goto err;
|
||||
/* M' = .. */
|
||||
if (encode) {
|
||||
if (ctx_len > ML_DSA_MAX_CONTEXT_STRING_LEN)
|
||||
goto err;
|
||||
/* IntegerToBytes(0, 1) .. */
|
||||
itb[0] = 0;
|
||||
itb[0] = prehash ? 1 : 0;
|
||||
/* || IntegerToBytes(|ctx|, 1) || .. */
|
||||
itb[1] = (uint8_t)ctx_len;
|
||||
if (!EVP_DigestUpdate(md_ctx, itb, 2))
|
||||
|
|
@ -108,6 +105,15 @@ err:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
|
||||
const uint8_t *ctx, size_t ctx_len)
|
||||
{
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
return ossl_ml_dsa_mu_init_int(key->shake256_md, key->tr, sizeof(key->tr),
|
||||
encode, 0, ctx, ctx_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief: updates the internal ML-DSA hash with an additional message chunk.
|
||||
*
|
||||
|
|
@ -153,8 +159,7 @@ int ossl_ml_dsa_mu_finalize(EVP_MD_CTX *md_ctx, uint8_t *mu, size_t mu_len)
|
|||
* @returns 1 on success, 0 on error
|
||||
*/
|
||||
static int ml_dsa_sign_internal(const ML_DSA_KEY *priv,
|
||||
const uint8_t *mu, size_t mu_len,
|
||||
const uint8_t *rnd, size_t rnd_len,
|
||||
const uint8_t *mu, size_t mu_len, const uint8_t *rnd, size_t rnd_len,
|
||||
uint8_t *out_sig)
|
||||
{
|
||||
int ret = 0;
|
||||
|
|
@ -315,8 +320,7 @@ err:
|
|||
*/
|
||||
static int ml_dsa_verify_internal(const ML_DSA_KEY *pub,
|
||||
const uint8_t *mu, size_t mu_len,
|
||||
const uint8_t *sig_enc,
|
||||
size_t sig_enc_len)
|
||||
const uint8_t *sig_enc, size_t sig_enc_len)
|
||||
{
|
||||
int ret = 0;
|
||||
uint8_t *alloc = NULL, *w1_encoded;
|
||||
|
|
@ -412,8 +416,8 @@ err:
|
|||
*
|
||||
* @returns 1 on success, or 0 on error.
|
||||
*/
|
||||
int ossl_ml_dsa_sign(const ML_DSA_KEY *priv, int msg_is_mu,
|
||||
const uint8_t *msg, size_t msg_len,
|
||||
int ossl_ml_dsa_sign(const ML_DSA_KEY *priv,
|
||||
int msg_is_mu, const uint8_t *msg, size_t msg_len,
|
||||
const uint8_t *context, size_t context_len,
|
||||
const uint8_t *rand, size_t rand_len, int encode,
|
||||
unsigned char *sig, size_t *sig_len, size_t sig_size)
|
||||
|
|
@ -462,8 +466,8 @@ err:
|
|||
* See FIPS 203 Section 5.3 Algorithm 3 ML-DSA.Verify()
|
||||
* @returns 1 on success, or 0 on error.
|
||||
*/
|
||||
int ossl_ml_dsa_verify(const ML_DSA_KEY *pub, int msg_is_mu,
|
||||
const uint8_t *msg, size_t msg_len,
|
||||
int ossl_ml_dsa_verify(const ML_DSA_KEY *pub,
|
||||
int msg_is_mu, const uint8_t *msg, size_t msg_len,
|
||||
const uint8_t *context, size_t context_len, int encode,
|
||||
const uint8_t *sig, size_t sig_len)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4741,6 +4741,10 @@ DEPEND[html/man7/EVP_MD-MDC2.html]=man7/EVP_MD-MDC2.pod
|
|||
GENERATE[html/man7/EVP_MD-MDC2.html]=man7/EVP_MD-MDC2.pod
|
||||
DEPEND[man/man7/EVP_MD-MDC2.7]=man7/EVP_MD-MDC2.pod
|
||||
GENERATE[man/man7/EVP_MD-MDC2.7]=man7/EVP_MD-MDC2.pod
|
||||
DEPEND[html/man7/EVP_MD-ML-DSA-MU.html]=man7/EVP_MD-ML-DSA-MU.pod
|
||||
GENERATE[html/man7/EVP_MD-ML-DSA-MU.html]=man7/EVP_MD-ML-DSA-MU.pod
|
||||
DEPEND[man/man7/EVP_MD-ML-DSA-MU.7]=man7/EVP_MD-ML-DSA-MU.pod
|
||||
GENERATE[man/man7/EVP_MD-ML-DSA-MU.7]=man7/EVP_MD-ML-DSA-MU.pod
|
||||
DEPEND[html/man7/EVP_MD-NULL.html]=man7/EVP_MD-NULL.pod
|
||||
GENERATE[html/man7/EVP_MD-NULL.html]=man7/EVP_MD-NULL.pod
|
||||
DEPEND[man/man7/EVP_MD-NULL.7]=man7/EVP_MD-NULL.pod
|
||||
|
|
@ -5228,6 +5232,7 @@ html/man7/EVP_MD-MD4.html \
|
|||
html/man7/EVP_MD-MD5-SHA1.html \
|
||||
html/man7/EVP_MD-MD5.html \
|
||||
html/man7/EVP_MD-MDC2.html \
|
||||
html/man7/EVP_MD-ML-DSA-MU.html \
|
||||
html/man7/EVP_MD-NULL.html \
|
||||
html/man7/EVP_MD-RIPEMD160.html \
|
||||
html/man7/EVP_MD-SHA1.html \
|
||||
|
|
@ -5389,6 +5394,7 @@ man/man7/EVP_MD-MD4.7 \
|
|||
man/man7/EVP_MD-MD5-SHA1.7 \
|
||||
man/man7/EVP_MD-MD5.7 \
|
||||
man/man7/EVP_MD-MDC2.7 \
|
||||
man/man7/EVP_MD-ML-DSA-MU.7 \
|
||||
man/man7/EVP_MD-NULL.7 \
|
||||
man/man7/EVP_MD-RIPEMD160.7 \
|
||||
man/man7/EVP_MD-SHA1.7 \
|
||||
|
|
|
|||
150
doc/man7/EVP_MD-ML-DSA-MU.pod
Normal file
150
doc/man7/EVP_MD-ML-DSA-MU.pod
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
EVP_MD-ML-DSA-MU - The ML-DSA-MU EVP_MD implementation
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Support for computing the value of external mu for ML-DSA using the B<EVP_MD> API.
|
||||
|
||||
Normally the value of C<mu> is calculated internally as part of an ML-DSA
|
||||
sign or verify operation. C<mu> is defined as:
|
||||
mu = SHAKE256(tr || M', 64)
|
||||
|
||||
Where B<tr> is the hash of the encoded public key, and, for Pure (ML-DSA):
|
||||
M' = 0x00 || ctx_len || ctx || message
|
||||
|
||||
In cases where prehashing the message is required, FIPS 204 allows
|
||||
the C<mu> calculation to be done externally and then C<mu> can be passed to
|
||||
ML-DSA sign or verify operations.
|
||||
|
||||
PreHash (HASH-ML-DSA) is also supported and uses:
|
||||
M' = 0x01 || ctx_len || ctx || OID || HashedMessage
|
||||
|
||||
The output C<mu> value can then be supplied as an input to L<EVP_SIGNATURE-ML-DSA(7)>
|
||||
using the ML-DSA Signature Parameter B<OSSL_SIGNATURE_PARAM_MU> (i.e. C<mu>).
|
||||
This allows larger messages to be hashed (or hidden) before they are passed to
|
||||
pure ML-DSA sign or verify operations.
|
||||
|
||||
=head2 Identities
|
||||
|
||||
This implementation is available with the FIPS provider as well as the
|
||||
default provider, and is identified with the name "ML-DSA-MU".
|
||||
|
||||
=head2 Parameters
|
||||
|
||||
This implementation supports the following settable L<OSSL_PARAM(3)> parameters:
|
||||
|
||||
=over 4
|
||||
|
||||
=item "pub" (B<OSSL_DIGEST_PARAM_MU_PUB_KEY>) <octet string>
|
||||
|
||||
A B<ML-DSA> encoded public key value of size 1312, 1952 or 2592 bytes
|
||||
depending on the respective key type of B<ML-DSA-44>, B<ML-DSA-65> or B<ML-DSA-87>.
|
||||
This can be retrieved from a L<EVP_PKEY-ML-DSA(7)> key by calling
|
||||
EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PUB_KEY, pub, sizeof(pub), &publen)
|
||||
This parameter MUST be set or an error will occur.
|
||||
|
||||
=item "context-string" (B<OSSL_DIGEST_PARAM_MU_CONTEXT_STRING>) <octet string>
|
||||
|
||||
An optional string of octets with length at most 255. By default it is the empty string.
|
||||
|
||||
=item "digest" (B<OSSL_DIGEST_PARAM_MU_DIGEST>) <utf8 string>
|
||||
|
||||
An optional parameter related to "HASH-ML-DSA". If used it determines the OID in
|
||||
the definition of PreHash M' above.
|
||||
|
||||
When this parameter is not specified, pure ML-DSA C<mu> is computed, and the
|
||||
input data is expected to be the full message, otherwise the input data must
|
||||
be the result of prehashing the message with the corresponding digest algorithm.
|
||||
|
||||
The HASH-ML-DSA variant is available to enable specialised use-cases,
|
||||
in which signing the full message with pure ML-DSA is not practical, and
|
||||
the external-mu API is a viable alternative.
|
||||
HASH-ML-DSA is not used in protocols such as X509 & CMS (See RFC 9981 and 9982),
|
||||
and is not presently implemented as an independent OpenSSL signature algorithm.
|
||||
|
||||
OpenSSL accepts the following digest names: "SHAKE-256", "SHAKE-128", "SHA-224",
|
||||
"SHA-256", "SHA-384", "SHA-512", "SHA3-224", "SHA3-256", "SHA3-384" and "SHA3-512".
|
||||
The total size of the C<HashedMessage> passed to EVP_DigestUpdate() MUST match
|
||||
the size of the digest. For SHAKE-128 and SHAKE-256 the expected XOF digest
|
||||
lengths are 32 and 64 respectively.
|
||||
|
||||
=item "properties" (B<OSSL_DIGEST_PARAM_MU_PROPERTIES>) <utf8 string>
|
||||
|
||||
Sets the properties to be queried when trying to fetch the underlying digest.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Gettable Parameters
|
||||
|
||||
This implementation supports the common gettable parameters described
|
||||
in L<EVP_MD-common(7)>.
|
||||
|
||||
=head1 CONFORMING TO
|
||||
|
||||
FIPS 204 and
|
||||
https://csrc.nist.gov/csrc/media/Projects/post-quantum-cryptography/documents/faq/fips204-sec6-03192025.pdf
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
To generate external 'mu' given an existing ML-DSA key and a large message:
|
||||
|
||||
calculate_mu(EVP_PKEY *pkey, const unsigned char *msg, size_t msglen,
|
||||
const unsigned char *ctx, size_t ctxlen, unsigned char mu[64])
|
||||
{
|
||||
unsigned char pub[2592];
|
||||
size_t publen = 0, chunk;
|
||||
OSSL_PARAM params[4], *p = params;
|
||||
|
||||
/* Retrieve the ML-DSA encoded public key */
|
||||
EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
|
||||
pub, sizeof(pub), &publen);
|
||||
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_PUB_KEY, pub, publen);
|
||||
/* This is an optional parameter */
|
||||
if (ctx != NULL && ctxlen != 0)
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_CONTEXT_STRING, ctx, ctxlen);
|
||||
/*
|
||||
* Optionally we could also set the digest name for HASH-ML-DSA
|
||||
* *p++ = OSSL_PARAM_construct_utf8_string(OSSL_DIGEST_PARAM_MU_DIGEST, "SHA-512", 0);
|
||||
*/
|
||||
*p = OSSL_PARAM_construct_end();
|
||||
|
||||
mdctx = EVP_MD_CTX_new();
|
||||
md = EVP_MD_fetch(libctx, "ML-DSA-MU", NULL);
|
||||
EVP_DigestInit_ex2(mdctx, md, params);
|
||||
/* Call EVP_DigestUpdate() multiple times to stream the message */
|
||||
while (msglen != 0) {
|
||||
/* Account for the last chunk being less than 64 */
|
||||
chunk = (msglen >= 64) ? 64 : msglen;
|
||||
EVP_DigestUpdate(mdctx, msg, chunk);
|
||||
msg += chunk;
|
||||
msglen -= chunk;
|
||||
}
|
||||
EVP_DigestFinalXOF(mdctx, mu, sizeof(mu))
|
||||
}
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<EVP_SIGNATURE-ML-DSA(7)>,
|
||||
L<EVP_PKEY-ML-DSA(7)>,
|
||||
L<provider-digest(7)>,
|
||||
L<OSSL_PROVIDER-FIPS(7)>,
|
||||
L<OSSL_PROVIDER-default(7)>
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
EVP_MD-ML-DSA-MU was added in OpenSSL 4.0.0.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
this file except in compliance with the License. You can obtain a copy
|
||||
in the file LICENSE in the source distribution or at
|
||||
L<https://www.openssl.org/source/license.html>.
|
||||
|
||||
=cut
|
||||
|
|
@ -67,19 +67,25 @@ If set the size must be 32 bytes.
|
|||
=item "deterministic" (B<OSSL_SIGNATURE_PARAM_DETERMINISTIC>) <integer>
|
||||
|
||||
The default value of 0 causes the per message randomness to be randomly
|
||||
generated using a DRBG. Setting this to 1 causes the per message randomness
|
||||
to be set to 32 bytes of zeros. This value is ignored if "test-entropy" is set.
|
||||
generated using a DRBG. Setting this to a nonzero value causes the per message
|
||||
randomness to be set to 32 bytes of zeros. This value is ignored
|
||||
if "test-entropy" is provided.
|
||||
|
||||
=item "mu" (B<OSSL_SIGNATURE_PARAM_MU>) <integer>
|
||||
|
||||
The default value of 0 causes sign and verify operations to process a raw message.
|
||||
Setting this to 1 causes those operations to assume the input is the C<mu> value
|
||||
from L<FIPS 204|https://csrc.nist.gov/pubs/fips/204/final> Algorithm 7 step 6 and
|
||||
Algorithm 8 step 7.
|
||||
Setting this to a nonzero value causes those operations to assume the input is
|
||||
the C<mu> value from
|
||||
L<FIPS 204|https://csrc.nist.gov/pubs/fips/204/final> Algorithm 7 step 6 and Algorithm 8 step 7.
|
||||
|
||||
Note that the message encoding steps from
|
||||
L<FIPS 204|https://csrc.nist.gov/pubs/fips/204/final> Algorithm 2 step 10 and
|
||||
Algorithm 3 step 5 are omitted when this setting is 1.
|
||||
Algorithm 3 step 5 are omitted when this setting is nonzero.
|
||||
|
||||
See L<EVP_MD-ML-DSA-MU(7)> for more information on generating an
|
||||
external-mu value.
|
||||
|
||||
The "context-string" is ignored if this value is nonzero.
|
||||
|
||||
=back
|
||||
|
||||
|
|
@ -95,17 +101,17 @@ passed in I<mdname> must be NULL.
|
|||
|
||||
To sign a message using an ML-DSA EVP_PKEY structure:
|
||||
|
||||
void do_sign(EVP_PKEY *key, unsigned char *msg, size_t msg_len)
|
||||
void do_sign(EVP_PKEY *key, const unsigned char *msg, size_t msg_len)
|
||||
{
|
||||
size_t sig_len;
|
||||
unsigned char *sig = NULL;
|
||||
const OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_octet_string("context-string", (unsigned char *)"A context string", 16),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
OSSL_PARAM params[2];
|
||||
EVP_PKEY_CTX *sctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
|
||||
EVP_SIGNATURE *sig_alg = EVP_SIGNATURE_fetch(NULL, "ML-DSA-65", NULL);
|
||||
|
||||
/* The context string is an optional parameter */
|
||||
params[0] = OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, (unsigned char *)"A context string", 16),
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
EVP_PKEY_sign_message_init(sctx, sig_alg, params);
|
||||
/* Calculate the required size for the signature by passing a NULL buffer. */
|
||||
EVP_PKEY_sign(sctx, NULL, &sig_len, msg, msg_len);
|
||||
|
|
@ -117,9 +123,35 @@ To sign a message using an ML-DSA EVP_PKEY structure:
|
|||
EVP_PKEY_CTX_free(sctx);
|
||||
}
|
||||
|
||||
To sign a message using an ML-DSA EVP_PKEY structure and an external mu:
|
||||
|
||||
void do_sign(EVP_PKEY *key, const unsigned char mu[64])
|
||||
{
|
||||
int use_mu_instead_of_msg = 1;
|
||||
size_t sig_len;
|
||||
unsigned char *sig = NULL;
|
||||
OSSL_PARAM params[2];
|
||||
EVP_PKEY_CTX *sctx = EVP_PKEY_CTX_new_from_pkey(NULL, pkey, NULL);
|
||||
EVP_SIGNATURE *sig_alg = EVP_SIGNATURE_fetch(NULL, "ML-DSA-65", NULL);
|
||||
|
||||
params[0] = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_MU, &use_mu_instead_of_msg);
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
|
||||
EVP_PKEY_sign_message_init(sctx, sig_alg, params);
|
||||
/* Calculate the required size for the signature by passing a NULL buffer. */
|
||||
EVP_PKEY_sign(sctx, NULL, &sig_len, mu, 64);
|
||||
sig = OPENSSL_malloc(sig_len);
|
||||
EVP_PKEY_sign(sctx, sig, &sig_len, mu, 64);
|
||||
...
|
||||
OPENSSL_free(sig);
|
||||
EVP_SIGNATURE_free(sig_alg);
|
||||
EVP_PKEY_CTX_free(sctx);
|
||||
}
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<EVP_PKEY-ML-DSA(7)>
|
||||
L<EVP_PKEY-ML-DSA(7)>,
|
||||
L<EVP_MD-ML-DSA-MU(7)>,
|
||||
L<provider-signature(7)>,
|
||||
L<EVP_PKEY_sign(3)>,
|
||||
L<EVP_PKEY_verify(3)>,
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ The OpenSSL FIPS provider supports these operations and algorithms:
|
|||
|
||||
KECCAK-KMAC is only used internally as a sub algorithm of KMAC.
|
||||
|
||||
=item ML-DSA-MU, see L<EVP_MD-ML-DSA-MU(7)>
|
||||
|
||||
=back
|
||||
|
||||
=head2 Symmetric Ciphers
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ The OpenSSL default provider supports these operations and algorithms:
|
|||
|
||||
=item NULL, see L<EVP_MD-NULL(7)>
|
||||
|
||||
=item ML-DSA-MU, see L<EVP_MD-ML-DSA-MU(7)>
|
||||
|
||||
=back
|
||||
|
||||
=head2 Symmetric Ciphers
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ L<EVP_MD-MD4(7)>, L<EVP_MD-MD5(7)>, L<EVP_MD-MD5-SHA1(7)>,
|
|||
L<EVP_MD-MDC2(7)>, L<EVP_MD-RIPEMD160(7)>, L<EVP_MD-SHA1(7)>,
|
||||
L<EVP_MD-SHA2(7)>, L<EVP_MD-SHA3(7)>, L<EVP_MD-KECCAK(7)>
|
||||
L<EVP_MD-SHAKE(7)>, L<EVP_MD-SM3(7)>, L<EVP_MD-WHIRLPOOL(7)>,
|
||||
L<EVP_MD-NULL(7)>,
|
||||
L<EVP_MD-NULL(7)>, L<EVP_MD-ML-DSA-MU(7)>,
|
||||
L<life_cycle-digest(7)>, L<EVP_DigestInit(3)>
|
||||
|
||||
=head1 HISTORY
|
||||
|
|
|
|||
|
|
@ -109,7 +109,11 @@ __owur int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key);
|
|||
__owur int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len);
|
||||
__owur int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len);
|
||||
|
||||
EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
|
||||
__owur EVP_MD_CTX *ossl_ml_dsa_mu_init(const ML_DSA_KEY *key, int encode,
|
||||
const uint8_t *ctx, size_t ctx_len);
|
||||
|
||||
__owur EVP_MD_CTX *ossl_ml_dsa_mu_init_int(EVP_MD *shake256_md,
|
||||
const uint8_t *tr, size_t tr_len, int encode, int prehash,
|
||||
const uint8_t *ctx, size_t ctx_len);
|
||||
__owur int ossl_ml_dsa_mu_update(EVP_MD_CTX *md_ctx, const uint8_t *msg, size_t msg_len);
|
||||
__owur int ossl_ml_dsa_mu_finalize(EVP_MD_CTX *md_ctx, uint8_t *mu, size_t mu_len);
|
||||
|
|
|
|||
|
|
@ -12,30 +12,67 @@
|
|||
* internal use.
|
||||
*/
|
||||
#include "internal/deprecated.h"
|
||||
|
||||
#include <openssl/obj_mac.h>
|
||||
#include <openssl/evp.h>
|
||||
#include "internal/packet.h"
|
||||
#include "prov/der_ml_dsa.h"
|
||||
#include "prov/der_pq_dsa.h"
|
||||
#include "prov/der_digests.h"
|
||||
|
||||
#define SET_OID(oid, oidlen, oidname) \
|
||||
(oid) = ossl_der_oid_id_##oidname; \
|
||||
(oidlen) = sizeof(ossl_der_oid_id_##oidname)
|
||||
|
||||
#define SET_DIGEST_OID(oidname, digestsz) \
|
||||
SET_OID(*oid, *oidlen, oidname); \
|
||||
*sz = digestsz
|
||||
|
||||
int ossl_DER_w_algorithmIdentifier_ML_DSA(WPACKET *pkt, int tag, ML_DSA_KEY *key)
|
||||
{
|
||||
const uint8_t *alg;
|
||||
size_t len;
|
||||
const uint8_t *oid;
|
||||
size_t oidlen;
|
||||
const char *name = ossl_ml_dsa_key_get_name(key);
|
||||
|
||||
if (OPENSSL_strcasecmp(name, "ML-DSA-44") == 0) {
|
||||
alg = ossl_der_oid_id_ml_dsa_44;
|
||||
len = sizeof(ossl_der_oid_id_ml_dsa_44);
|
||||
SET_OID(oid, oidlen, ml_dsa_44);
|
||||
} else if (OPENSSL_strcasecmp(name, "ML-DSA-65") == 0) {
|
||||
alg = ossl_der_oid_id_ml_dsa_65;
|
||||
len = sizeof(ossl_der_oid_id_ml_dsa_65);
|
||||
SET_OID(oid, oidlen, ml_dsa_65);
|
||||
} else if (OPENSSL_strcasecmp(name, "ML-DSA-87") == 0) {
|
||||
alg = ossl_der_oid_id_ml_dsa_87;
|
||||
len = sizeof(ossl_der_oid_id_ml_dsa_87);
|
||||
SET_OID(oid, oidlen, ml_dsa_87);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return ossl_DER_w_begin_sequence(pkt, tag)
|
||||
/* No parameters */
|
||||
&& ossl_DER_w_precompiled(pkt, -1, alg, len)
|
||||
&& ossl_DER_w_precompiled(pkt, -1, oid, oidlen)
|
||||
&& ossl_DER_w_end_sequence(pkt, tag);
|
||||
}
|
||||
|
||||
int ossl_der_oid_pq_dsa_prehash_digest(const char *oid_digest_name,
|
||||
const uint8_t **oid, size_t *oidlen, size_t *sz)
|
||||
{
|
||||
if (OPENSSL_strcasecmp(oid_digest_name, "SHAKE-256") == 0) {
|
||||
SET_DIGEST_OID(shake256, 64);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHAKE-128") == 0) {
|
||||
SET_DIGEST_OID(shake128, 32);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-224") == 0) {
|
||||
SET_DIGEST_OID(sha224, 28);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-256") == 0) {
|
||||
SET_DIGEST_OID(sha256, 32);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-384") == 0) {
|
||||
SET_DIGEST_OID(sha384, 48);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA-512") == 0) {
|
||||
SET_DIGEST_OID(sha512, 64);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-224") == 0) {
|
||||
SET_DIGEST_OID(sha3_224, 28);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-256") == 0) {
|
||||
SET_DIGEST_OID(sha3_256, 32);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-384") == 0) {
|
||||
SET_DIGEST_OID(sha3_384, 48);
|
||||
} else if (OPENSSL_strcasecmp(oid_digest_name, "SHA3-512") == 0) {
|
||||
SET_DIGEST_OID(sha3_512, 64);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
11
providers/common/include/prov/der_pq_dsa.h
Normal file
11
providers/common/include/prov/der_pq_dsa.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
int ossl_der_oid_pq_dsa_prehash_digest(const char *oid_digest_name,
|
||||
const uint8_t **oid, size_t *oidlen, size_t *sz);
|
||||
|
|
@ -159,6 +159,9 @@ static const OSSL_ALGORITHM deflt_digests[] = {
|
|||
#endif /* OPENSSL_NO_RMD160 */
|
||||
|
||||
{ PROV_NAMES_NULL, "provider=default", ossl_nullmd_functions },
|
||||
#ifndef OPENSSL_NO_ML_DSA
|
||||
{ PROV_NAMES_ML_DSA_MU, "provider=default", ossl_ml_dsa_mu_functions },
|
||||
#endif
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -49,8 +49,10 @@ static OSSL_FUNC_provider_query_operation_fn fips_query;
|
|||
static OSSL_FUNC_provider_query_operation_fn fips_query_internal;
|
||||
static OSSL_FUNC_provider_random_bytes_fn fips_random_bytes;
|
||||
|
||||
#define ALGC(NAMES, FUNC, CHECK) \
|
||||
{ { NAMES, FIPS_DEFAULT_PROPERTIES, FUNC }, CHECK }
|
||||
#define ALGC(NAMES, FUNC, CHECK) \
|
||||
{ \
|
||||
{ NAMES, FIPS_DEFAULT_PROPERTIES, FUNC }, CHECK \
|
||||
}
|
||||
#define ALG(NAMES, FUNC) ALGC(NAMES, FUNC, NULL)
|
||||
|
||||
extern OSSL_FUNC_core_thread_start_fn *c_thread_start;
|
||||
|
|
@ -300,6 +302,9 @@ static int fips_self_test(void *provctx)
|
|||
|
||||
static const OSSL_ALGORITHM fips_digests[] = {
|
||||
FIPS_DIGESTS_COMMON(),
|
||||
#ifndef OPENSSL_NO_ML_DSA
|
||||
{ PROV_NAMES_ML_DSA_MU, FIPS_DEFAULT_PROPERTIES, ossl_ml_dsa_mu_functions },
|
||||
#endif
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
static const OSSL_ALGORITHM fips_digests_internal[] = {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ $BLAKE2_GOAL=../../libdefault.a
|
|||
$SM3_GOAL=../../libdefault.a
|
||||
$MD5_GOAL=../../libdefault.a
|
||||
$NULL_GOAL=../../libdefault.a
|
||||
$ML_DSA_MU_GOAL=../../libdefault.a ../../libfips.a
|
||||
|
||||
$MD2_GOAL=../../liblegacy.a
|
||||
$MD4_GOAL=../../liblegacy.a
|
||||
|
|
@ -60,3 +61,7 @@ ENDIF
|
|||
IF[{- !$disabled{rmd160} -}]
|
||||
SOURCE[$RIPEMD_GOAL]=ripemd_prov.c
|
||||
ENDIF
|
||||
|
||||
IF[{- !$disabled{'ml-dsa'} -}]
|
||||
SOURCE[$ML_DSA_MU_GOAL]=ml_dsa_mu_prov.c
|
||||
ENDIF
|
||||
|
|
|
|||
345
providers/implementations/digests/ml_dsa_mu_prov.c
Normal file
345
providers/implementations/digests/ml_dsa_mu_prov.c
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
/*
|
||||
* mu is the value:
|
||||
* mu = SHAKE256(tr || M', 64)
|
||||
*
|
||||
* where tr is the hash of the public key
|
||||
* And M' is one of the following:
|
||||
* (1) Pure: M' = 00 || ctx_len || ctx || in (where in = message)
|
||||
* (2) PreHash: M' = 01 || ctx_len || ctx || OID || in (where in = hashed(msg))
|
||||
*/
|
||||
|
||||
#include "internal/deprecated.h" /* including crypto/sha.h requires this */
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/proverr.h>
|
||||
#include <openssl/core_names.h>
|
||||
#include "crypto/ml_dsa.h"
|
||||
#include "prov/provider_ctx.h"
|
||||
#include "prov/digestcommon.h"
|
||||
#include "prov/der_pq_dsa.h"
|
||||
#include "prov/implementations.h"
|
||||
#include "internal/common.h"
|
||||
#include "internal/sha3.h"
|
||||
#include "providers/implementations/digests/ml_dsa_mu_prov.inc"
|
||||
|
||||
#define SHAKE256_SIZE 64
|
||||
#define SHAKE_FLAGS (PROV_DIGEST_FLAG_ALGID_ABSENT)
|
||||
#define ML_DSA_MAX_CONTEXT_STRING_LEN 255
|
||||
|
||||
typedef struct mu_ctx_st {
|
||||
OSSL_LIB_CTX *libctx;
|
||||
char *propq;
|
||||
EVP_MD_CTX *mdctx;
|
||||
EVP_MD *md;
|
||||
uint8_t context[ML_DSA_MAX_CONTEXT_STRING_LEN];
|
||||
size_t context_len;
|
||||
uint8_t tr[SHAKE256_SIZE]; /* Pre-cached public key Hash */
|
||||
size_t keylen;
|
||||
const uint8_t *oid;
|
||||
size_t oid_len;
|
||||
size_t digest_len;
|
||||
size_t remaining;
|
||||
} MU_CTX;
|
||||
|
||||
static OSSL_FUNC_digest_newctx_fn mu_newctx;
|
||||
static OSSL_FUNC_digest_freectx_fn mu_freectx;
|
||||
static OSSL_FUNC_digest_get_params_fn mu_get_params;
|
||||
static OSSL_FUNC_digest_dupctx_fn mu_dupctx;
|
||||
static OSSL_FUNC_digest_init_fn mu_init;
|
||||
static OSSL_FUNC_digest_update_fn mu_update;
|
||||
static OSSL_FUNC_digest_final_fn mu_final;
|
||||
static OSSL_FUNC_digest_set_ctx_params_fn mu_set_ctx_params;
|
||||
static OSSL_FUNC_digest_settable_ctx_params_fn mu_settable_ctx_params;
|
||||
static OSSL_FUNC_digest_get_ctx_params_fn mu_get_ctx_params;
|
||||
static OSSL_FUNC_digest_gettable_ctx_params_fn mu_gettable_ctx_params;
|
||||
|
||||
static void *mu_newctx(void *provctx)
|
||||
{
|
||||
MU_CTX *ctx;
|
||||
|
||||
if (ossl_unlikely(!ossl_prov_is_running()))
|
||||
return NULL;
|
||||
ctx = OPENSSL_zalloc(sizeof(*ctx));
|
||||
if (ctx != NULL)
|
||||
ctx->libctx = PROV_LIBCTX_OF(provctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void mu_freectx(void *vctx)
|
||||
{
|
||||
MU_CTX *ctx = (MU_CTX *)vctx;
|
||||
|
||||
OPENSSL_free(ctx->propq);
|
||||
EVP_MD_free(ctx->md);
|
||||
EVP_MD_CTX_free(ctx->mdctx);
|
||||
OPENSSL_free(ctx);
|
||||
}
|
||||
|
||||
static void *mu_dupctx(void *ctx)
|
||||
{
|
||||
MU_CTX *src = (MU_CTX *)ctx;
|
||||
MU_CTX *dst = ossl_prov_is_running() ? OPENSSL_malloc(sizeof(*dst)) : NULL;
|
||||
|
||||
if (dst == NULL)
|
||||
return NULL;
|
||||
*dst = *src;
|
||||
dst->mdctx = NULL;
|
||||
dst->propq = NULL;
|
||||
dst->md = NULL;
|
||||
if (src->md != NULL) {
|
||||
if (!EVP_MD_up_ref(src->md))
|
||||
goto err;
|
||||
dst->md = src->md;
|
||||
}
|
||||
if (src->mdctx != NULL) {
|
||||
dst->mdctx = EVP_MD_CTX_new();
|
||||
if (dst->mdctx == NULL
|
||||
|| !EVP_MD_CTX_copy_ex(dst->mdctx, src->mdctx))
|
||||
goto err;
|
||||
}
|
||||
if (src->propq != NULL) {
|
||||
dst->propq = OPENSSL_strdup(src->propq);
|
||||
if (dst->propq == NULL)
|
||||
goto err;
|
||||
}
|
||||
return dst;
|
||||
err:
|
||||
mu_freectx(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mu_init(void *vctx, const OSSL_PARAM params[])
|
||||
{
|
||||
MU_CTX *ctx = (MU_CTX *)vctx;
|
||||
|
||||
if (ossl_unlikely(!ossl_prov_is_running()))
|
||||
return 0;
|
||||
|
||||
if (ctx->mdctx != NULL && !EVP_MD_CTX_reset(ctx->mdctx))
|
||||
return 0;
|
||||
ctx->remaining = ctx->digest_len;
|
||||
return mu_set_ctx_params(vctx, params);
|
||||
}
|
||||
|
||||
static int mu_get_params(OSSL_PARAM params[])
|
||||
{
|
||||
return ossl_digest_default_get_params(params, SHA3_BLOCKSIZE(256),
|
||||
SHAKE256_SIZE, SHAKE_FLAGS);
|
||||
}
|
||||
|
||||
static const OSSL_PARAM *mu_settable_ctx_params(ossl_unused void *ctx,
|
||||
ossl_unused void *provctx)
|
||||
{
|
||||
return ml_dsa_mu_set_ctx_params_list;
|
||||
}
|
||||
|
||||
static int set_property_query(MU_CTX *ctx, const char *propq)
|
||||
{
|
||||
OPENSSL_free(ctx->propq);
|
||||
ctx->propq = NULL;
|
||||
if (propq != NULL) {
|
||||
ctx->propq = OPENSSL_strdup(propq);
|
||||
if (ctx->propq == NULL)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static EVP_MD *shake_digest(MU_CTX *ctx)
|
||||
{
|
||||
if (ctx->md == NULL)
|
||||
ctx->md = EVP_MD_fetch(ctx->libctx, "SHAKE256", ctx->propq);
|
||||
return ctx->md;
|
||||
}
|
||||
|
||||
static int digest_public_key(MU_CTX *ctx, const uint8_t *pub, size_t publen)
|
||||
{
|
||||
int ret;
|
||||
EVP_MD *md;
|
||||
EVP_MD_CTX *mdctx;
|
||||
|
||||
if (publen != ML_DSA_44_PUB_LEN
|
||||
&& publen != ML_DSA_65_PUB_LEN
|
||||
&& publen != ML_DSA_87_PUB_LEN) {
|
||||
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
|
||||
return 0;
|
||||
}
|
||||
|
||||
md = shake_digest(ctx);
|
||||
if (md == NULL)
|
||||
return 0;
|
||||
mdctx = EVP_MD_CTX_new();
|
||||
if (mdctx == NULL)
|
||||
return 0;
|
||||
ret = EVP_DigestInit_ex(mdctx, md, NULL)
|
||||
&& EVP_DigestUpdate(mdctx, pub, publen)
|
||||
&& EVP_DigestFinalXOF(mdctx, ctx->tr, sizeof(ctx->tr));
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mu_set_ctx_params(void *vctx, const OSSL_PARAM params[])
|
||||
{
|
||||
MU_CTX *ctx = (MU_CTX *)vctx;
|
||||
struct ml_dsa_mu_set_ctx_params_st p;
|
||||
|
||||
if (ctx == NULL || !ml_dsa_mu_set_ctx_params_decoder(params, &p))
|
||||
return 0;
|
||||
|
||||
if (p.ctx != NULL) {
|
||||
void *vp = ctx->context;
|
||||
|
||||
if (!OSSL_PARAM_get_octet_string(p.ctx, &vp, sizeof(ctx->context),
|
||||
&(ctx->context_len))) {
|
||||
ctx->context_len = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (p.propq != NULL) {
|
||||
if (p.propq->data_type != OSSL_PARAM_UTF8_STRING
|
||||
|| !set_property_query(ctx, p.propq->data))
|
||||
return 0;
|
||||
}
|
||||
if (p.pubkey != NULL) {
|
||||
if (p.pubkey->data_type != OSSL_PARAM_OCTET_STRING)
|
||||
return 0;
|
||||
if (!digest_public_key(ctx, p.pubkey->data, p.pubkey->data_size))
|
||||
return 0;
|
||||
ctx->keylen = p.pubkey->data_size;
|
||||
}
|
||||
if (p.digestname != NULL) {
|
||||
int ret;
|
||||
|
||||
if (p.digestname->data_type != OSSL_PARAM_UTF8_STRING)
|
||||
return 0;
|
||||
ret = ossl_der_oid_pq_dsa_prehash_digest(p.digestname->data,
|
||||
&ctx->oid, &ctx->oid_len, &ctx->digest_len);
|
||||
if (ret)
|
||||
ctx->remaining = ctx->digest_len;
|
||||
else
|
||||
ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
|
||||
"%s is not supported", p.digestname->data);
|
||||
return ret;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const OSSL_PARAM *mu_gettable_ctx_params(ossl_unused void *ctx,
|
||||
ossl_unused void *provctx)
|
||||
{
|
||||
return ml_dsa_mu_get_ctx_params_list;
|
||||
}
|
||||
|
||||
static int mu_get_ctx_params(void *vctx, OSSL_PARAM params[])
|
||||
{
|
||||
MU_CTX *ctx = (MU_CTX *)vctx;
|
||||
struct ml_dsa_mu_get_ctx_params_st p;
|
||||
|
||||
if (ctx == NULL || !ml_dsa_mu_get_ctx_params_decoder(params, &p))
|
||||
return 0;
|
||||
|
||||
/* Size is an alias of xoflen */
|
||||
if (p.xoflen != NULL || p.size != NULL) {
|
||||
size_t xoflen = SHAKE256_SIZE;
|
||||
|
||||
if (p.size != NULL && !OSSL_PARAM_set_size_t(p.size, xoflen)) {
|
||||
ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
if (p.xoflen != NULL && !OSSL_PARAM_set_size_t(p.xoflen, xoflen)) {
|
||||
ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_init(MU_CTX *ctx)
|
||||
{
|
||||
if (ctx->mdctx == NULL) {
|
||||
EVP_MD *md = shake_digest(ctx);
|
||||
|
||||
if (md == NULL)
|
||||
return 0;
|
||||
if (ctx->keylen == 0) {
|
||||
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
|
||||
return 0;
|
||||
}
|
||||
ctx->mdctx = ossl_ml_dsa_mu_init_int(md, ctx->tr, sizeof(ctx->tr), 1,
|
||||
ctx->oid_len != 0, ctx->context, ctx->context_len);
|
||||
if (ctx->mdctx == NULL)
|
||||
return 0;
|
||||
if (!ossl_ml_dsa_mu_update(ctx->mdctx, ctx->oid, ctx->oid_len))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mu_update(void *vctx, const unsigned char *in, size_t inlen)
|
||||
{
|
||||
MU_CTX *ctx = (MU_CTX *)vctx;
|
||||
int ret;
|
||||
|
||||
if (ctx->oid_len > 0) {
|
||||
/* For the HASH-ML-DSA case we expect the input to be the size of the digest */
|
||||
if (inlen > ctx->remaining) {
|
||||
ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
|
||||
return 0;
|
||||
}
|
||||
ctx->remaining -= inlen;
|
||||
}
|
||||
ret = check_init(ctx)
|
||||
&& ossl_ml_dsa_mu_update(ctx->mdctx, in, inlen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mu_final(void *vctx, uint8_t *out, size_t *outl, size_t outsz)
|
||||
{
|
||||
MU_CTX *ctx = (MU_CTX *)vctx;
|
||||
size_t len = SHAKE256_SIZE;
|
||||
|
||||
if (ossl_unlikely(!ossl_prov_is_running()))
|
||||
return 0;
|
||||
if (out == NULL) {
|
||||
if (outl == NULL)
|
||||
return 0;
|
||||
} else if (outsz > 0) {
|
||||
if (outsz < len)
|
||||
return 0;
|
||||
|
||||
if (ctx->remaining != 0)
|
||||
return 0;
|
||||
if (!ossl_ml_dsa_mu_finalize(ctx->mdctx, out, len))
|
||||
return 0;
|
||||
}
|
||||
*outl = len;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const OSSL_DISPATCH ossl_ml_dsa_mu_functions[] = {
|
||||
{ OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))mu_newctx },
|
||||
{ OSSL_FUNC_DIGEST_INIT, (void (*)(void))mu_init },
|
||||
{ OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))mu_update },
|
||||
{ OSSL_FUNC_DIGEST_FINAL, (void (*)(void))mu_final },
|
||||
{ OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))mu_freectx },
|
||||
{ OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))mu_dupctx },
|
||||
{ OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))mu_set_ctx_params },
|
||||
{ OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS,
|
||||
(void (*)(void))mu_settable_ctx_params },
|
||||
{ OSSL_FUNC_DIGEST_GET_CTX_PARAMS, (void (*)(void))mu_get_ctx_params },
|
||||
{ OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS,
|
||||
(void (*)(void))mu_gettable_ctx_params },
|
||||
PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(mu),
|
||||
PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
|
||||
24
providers/implementations/digests/ml_dsa_mu_prov.inc.in
Normal file
24
providers/implementations/digests/ml_dsa_mu_prov.inc.in
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License 2.0 (the \"License\"). You may not use
|
||||
* this file except in compliance with the License. You can obtain a copy
|
||||
* in the file LICENSE in the source distribution or at
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
{-
|
||||
use OpenSSL::paramnames qw(produce_param_decoder);
|
||||
-}
|
||||
|
||||
{- produce_param_decoder('ml_dsa_mu_get_ctx_params',
|
||||
(['OSSL_DIGEST_PARAM_SIZE', 'size', 'uint'],
|
||||
['OSSL_DIGEST_PARAM_XOFLEN', 'xoflen', 'size_t'],
|
||||
)); -}
|
||||
|
||||
{- produce_param_decoder('ml_dsa_mu_set_ctx_params',
|
||||
(['OSSL_DIGEST_PARAM_MU_CONTEXT_STRING', 'ctx', 'octet_string'],
|
||||
['OSSL_DIGEST_PARAM_MU_PROPERTIES', 'propq', 'utf8_string'],
|
||||
['OSSL_DIGEST_PARAM_MU_PUB_KEY', 'pubkey', 'octet_string'],
|
||||
['OSSL_DIGEST_PARAM_MU_DIGEST', 'digestname', 'utf8_string']
|
||||
)); -}
|
||||
|
|
@ -42,6 +42,7 @@ extern const OSSL_DISPATCH ossl_mdc2_functions[];
|
|||
extern const OSSL_DISPATCH ossl_wp_functions[];
|
||||
extern const OSSL_DISPATCH ossl_ripemd160_functions[];
|
||||
extern const OSSL_DISPATCH ossl_nullmd_functions[];
|
||||
extern const OSSL_DISPATCH ossl_ml_dsa_mu_functions[];
|
||||
|
||||
/* Ciphers */
|
||||
extern const OSSL_DISPATCH ossl_null_functions[];
|
||||
|
|
|
|||
|
|
@ -271,6 +271,12 @@
|
|||
#define PROV_NAMES_MDC2 "MDC2:2.5.8.3.101"
|
||||
#define PROV_NAMES_WHIRLPOOL "WHIRLPOOL:1.0.10118.3.0.55"
|
||||
#define PROV_NAMES_RIPEMD_160 "RIPEMD-160:RIPEMD160:RIPEMD:RMD160:1.3.36.3.2.1"
|
||||
/*
|
||||
* Name taken from
|
||||
* https://csrc.nist.gov/csrc/media/Projects/post-quantum-cryptography/documents/faq/fips204-sec6-03192025.pdf
|
||||
* See ExternalMu-ML-DSA.Prehash
|
||||
*/
|
||||
#define PROV_NAMES_ML_DSA_MU "ML-DSA-MU"
|
||||
|
||||
/*-
|
||||
* KDFs / PRFs
|
||||
|
|
|
|||
166
test/evp_test.c
166
test/evp_test.c
|
|
@ -2577,6 +2577,7 @@ typedef struct pkey_data_st {
|
|||
size_t output_len;
|
||||
STACK_OF(OPENSSL_STRING) *init_controls; /* collection of controls */
|
||||
STACK_OF(OPENSSL_STRING) *controls; /* collection of controls */
|
||||
STACK_OF(OPENSSL_STRING) *mu_controls; /* collection of controls */
|
||||
EVP_PKEY *peer;
|
||||
int validate;
|
||||
} PKEY_DATA;
|
||||
|
|
@ -2635,6 +2636,7 @@ static int pkey_test_init(EVP_TEST *t, const char *name,
|
|||
kdata->keyop = keyop;
|
||||
kdata->init_controls = sk_OPENSSL_STRING_new_null();
|
||||
kdata->controls = sk_OPENSSL_STRING_new_null();
|
||||
kdata->mu_controls = sk_OPENSSL_STRING_new_null();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -2679,6 +2681,7 @@ static int pkey_test_init_ex2(EVP_TEST *t, const char *name,
|
|||
}
|
||||
kdata->init_controls = sk_OPENSSL_STRING_new_null();
|
||||
kdata->controls = sk_OPENSSL_STRING_new_null();
|
||||
kdata->mu_controls = sk_OPENSSL_STRING_new_null();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -2686,6 +2689,7 @@ static void pkey_test_cleanup(EVP_TEST *t)
|
|||
{
|
||||
PKEY_DATA *kdata = t->data;
|
||||
|
||||
ctrlfree(kdata->mu_controls);
|
||||
ctrlfree(kdata->init_controls);
|
||||
ctrlfree(kdata->controls);
|
||||
OPENSSL_free(kdata->input);
|
||||
|
|
@ -2757,6 +2761,8 @@ static int pkey_test_parse(EVP_TEST *t,
|
|||
return ctrladd(kdata->init_controls, value);
|
||||
if (strcmp(keyword, "Ctrl") == 0)
|
||||
return pkey_add_control(t, kdata->controls, value);
|
||||
if (strcmp(keyword, "CtrlMu") == 0)
|
||||
return ctrladd(kdata->mu_controls, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2805,16 +2811,160 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Calculate ML-DSA-MU.prehash() */
|
||||
static int calculate_mu(const uint8_t *pub, size_t publen,
|
||||
const uint8_t *ctx, size_t ctxlen, const uint8_t *msg, size_t msglen,
|
||||
const char *digestname, uint8_t *out, size_t outlen)
|
||||
{
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
EVP_MD *md = NULL;
|
||||
OSSL_PARAM params[4], *p = params;
|
||||
int ret = 0;
|
||||
size_t len;
|
||||
|
||||
if (pub == NULL || publen == 0)
|
||||
return 0;
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_PUB_KEY, (uint8_t *)pub, publen);
|
||||
if (ctx != NULL && ctxlen > 0)
|
||||
*p++ = OSSL_PARAM_construct_octet_string(OSSL_DIGEST_PARAM_MU_CONTEXT_STRING,
|
||||
(uint8_t *)ctx, ctxlen);
|
||||
if (digestname != NULL)
|
||||
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_DIGEST_PARAM_MU_DIGEST, (char *)digestname, 0);
|
||||
*p = OSSL_PARAM_construct_end();
|
||||
|
||||
if (!TEST_ptr(mdctx = EVP_MD_CTX_new())
|
||||
|| !TEST_ptr(md = EVP_MD_fetch(libctx, "ML-DSA-MU", NULL))
|
||||
|| !TEST_true(EVP_DigestInit_ex2(mdctx, md, params)))
|
||||
goto err;
|
||||
/* stream the message */
|
||||
while (msglen > 0) {
|
||||
len = (msglen >= 15 ? 15 : msglen);
|
||||
if (!TEST_true(EVP_DigestUpdate(mdctx, msg, len)))
|
||||
goto err;
|
||||
msg += len;
|
||||
msglen -= len;
|
||||
}
|
||||
if (!TEST_true(EVP_DigestFinalXOF(mdctx, out, outlen)))
|
||||
goto err;
|
||||
ret = 1;
|
||||
err:
|
||||
EVP_MD_free(md);
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pkey_calculate_mu(EVP_TEST *t, uint8_t *mu, size_t *mulen)
|
||||
{
|
||||
int ret = 0;
|
||||
OSSL_PARAM *p = NULL;
|
||||
static const OSSL_PARAM mu_digest_settable_ctx_params[] = {
|
||||
OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
|
||||
OSSL_PARAM_utf8_string(OSSL_ALG_PARAM_DIGEST, NULL, 0),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
OSSL_PARAM params[3] = {
|
||||
OSSL_PARAM_END,
|
||||
OSSL_PARAM_END,
|
||||
OSSL_PARAM_END,
|
||||
};
|
||||
size_t params_n = 0;
|
||||
uint8_t pub[3 * 1024];
|
||||
size_t publen = 0;
|
||||
uint8_t *ctx = NULL;
|
||||
size_t ctxlen = 0;
|
||||
const char *digestname = NULL;
|
||||
PKEY_DATA *kdata = t->data;
|
||||
EVP_PKEY *key = EVP_PKEY_CTX_get0_pkey(kdata->ctx);
|
||||
uint8_t *in = kdata->input;
|
||||
size_t inlen = kdata->input_len;
|
||||
uint8_t digest[64];
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
EVP_MD *md = NULL;
|
||||
|
||||
if (sk_OPENSSL_STRING_num(kdata->mu_controls) > 0) {
|
||||
if (!ctrl2params(t, kdata->mu_controls, mu_digest_settable_ctx_params,
|
||||
params, OSSL_NELEM(params), ¶ms_n))
|
||||
goto err;
|
||||
}
|
||||
p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_MU_CONTEXT_STRING);
|
||||
if (p != NULL) {
|
||||
ctx = p->data;
|
||||
ctxlen = p->data_size;
|
||||
}
|
||||
p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_MU_DIGEST);
|
||||
if (p != NULL && p->data != NULL) {
|
||||
/*
|
||||
* If we are prehashing then calculate the hash of the kdata->input and
|
||||
* set this as the new input
|
||||
*/
|
||||
size_t xoflen = 0;
|
||||
unsigned int len = 0;
|
||||
|
||||
digestname = p->data;
|
||||
mdctx = EVP_MD_CTX_new();
|
||||
if (mdctx == NULL)
|
||||
goto err;
|
||||
md = EVP_MD_fetch(libctx, digestname, NULL);
|
||||
if (md == NULL)
|
||||
goto err;
|
||||
if (!EVP_DigestInit(mdctx, md)
|
||||
|| !EVP_DigestUpdate(mdctx, in, inlen))
|
||||
goto err;
|
||||
/* Deal with the SHAKE algorithm not setting a default xoflen */
|
||||
if (EVP_MD_is_a(md, "SHAKE128"))
|
||||
xoflen = 32;
|
||||
else if (EVP_MD_is_a(md, "SHAKE256"))
|
||||
xoflen = 64;
|
||||
if (xoflen != 0) {
|
||||
len = (unsigned int)xoflen;
|
||||
if (!EVP_DigestFinalXOF(mdctx, digest, xoflen))
|
||||
goto err;
|
||||
} else {
|
||||
if (!EVP_DigestFinal(mdctx, digest, &len))
|
||||
goto err;
|
||||
}
|
||||
in = digest;
|
||||
inlen = len;
|
||||
}
|
||||
|
||||
if (!TEST_true(EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PUB_KEY,
|
||||
pub, sizeof(pub), &publen)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_true(calculate_mu(pub, publen, ctx, ctxlen, in, inlen,
|
||||
digestname, mu, *mulen)))
|
||||
goto err;
|
||||
ret = 1;
|
||||
err:
|
||||
EVP_MD_free(md);
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
ctrl2params_free(params, params_n, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pkey_test_run(EVP_TEST *t)
|
||||
{
|
||||
PKEY_DATA *expected = t->data;
|
||||
unsigned char *got = NULL;
|
||||
size_t got_len;
|
||||
EVP_PKEY_CTX *copy = NULL;
|
||||
uint8_t mu[64];
|
||||
size_t mulen = sizeof(mu);
|
||||
const uint8_t *in = expected->input;
|
||||
size_t inlen = expected->input_len;
|
||||
|
||||
if (!pkey_test_run_init(t))
|
||||
goto err;
|
||||
|
||||
if (sk_OPENSSL_STRING_num(expected->mu_controls) > 0) {
|
||||
if (!pkey_calculate_mu(t, mu, &mulen)) {
|
||||
t->err = "KEYOP_MU_ERROR";
|
||||
goto err;
|
||||
}
|
||||
in = mu;
|
||||
inlen = mulen;
|
||||
}
|
||||
|
||||
if (!pkey_check_security_category(t, EVP_PKEY_CTX_get0_pkey(expected->ctx)))
|
||||
goto err;
|
||||
|
||||
|
|
@ -2824,16 +2974,12 @@ static int pkey_test_run(EVP_TEST *t)
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (expected->keyop(expected->ctx, NULL, &got_len,
|
||||
expected->input, expected->input_len)
|
||||
<= 0
|
||||
if (expected->keyop(expected->ctx, NULL, &got_len, in, inlen) <= 0
|
||||
|| !TEST_ptr(got = OPENSSL_malloc(got_len))) {
|
||||
t->err = "KEYOP_LENGTH_ERROR";
|
||||
goto err;
|
||||
}
|
||||
if (expected->keyop(expected->ctx, got, &got_len,
|
||||
expected->input, expected->input_len)
|
||||
<= 0) {
|
||||
if (expected->keyop(expected->ctx, got, &got_len, in, inlen) <= 0) {
|
||||
t->err = "KEYOP_ERROR";
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -2848,16 +2994,12 @@ static int pkey_test_run(EVP_TEST *t)
|
|||
got = NULL;
|
||||
|
||||
/* Repeat the test on the EVP_PKEY context copy. */
|
||||
if (expected->keyop(copy, NULL, &got_len, expected->input,
|
||||
expected->input_len)
|
||||
<= 0
|
||||
if (expected->keyop(copy, NULL, &got_len, in, inlen) <= 0
|
||||
|| !TEST_ptr(got = OPENSSL_malloc(got_len))) {
|
||||
t->err = "KEYOP_LENGTH_ERROR";
|
||||
goto err;
|
||||
}
|
||||
if (expected->keyop(copy, got, &got_len, expected->input,
|
||||
expected->input_len)
|
||||
<= 0) {
|
||||
if (expected->keyop(copy, got, &got_len, in, inlen) <= 0) {
|
||||
t->err = "KEYOP_ERROR";
|
||||
goto err;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -172,6 +172,11 @@ my %params = (
|
|||
'OSSL_DIGEST_PARAM_SIZE' => "size", # size_t
|
||||
'OSSL_DIGEST_PARAM_XOF' => "xof", # int, 0 or 1
|
||||
'OSSL_DIGEST_PARAM_ALGID_ABSENT' => "algid-absent", # int, 0 or 1
|
||||
# external mu digest parameters
|
||||
'OSSL_DIGEST_PARAM_MU_PUB_KEY' => "pub", # octet string
|
||||
'OSSL_DIGEST_PARAM_MU_CONTEXT_STRING' => "context-string", # octet string
|
||||
'OSSL_DIGEST_PARAM_MU_DIGEST' => '*OSSL_ALG_PARAM_DIGEST', # utf8 string
|
||||
'OSSL_DIGEST_PARAM_MU_PROPERTIES' => '*OSSL_ALG_PARAM_PROPERTIES', # utf8 string
|
||||
|
||||
# MAC parameters
|
||||
'OSSL_MAC_PARAM_KEY' => "key", # octet string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue