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:
slontis 2025-11-26 17:42:43 +11:00 committed by Pauli
parent faf48d097b
commit 175cda569d
23 changed files with 878 additions and 54 deletions

1
.gitignore vendored
View file

@ -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

View file

@ -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.

View file

@ -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]=\

View file

@ -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)
{

View file

@ -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 \

View 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

View file

@ -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)>,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View 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);

View file

@ -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 }
};

View file

@ -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[] = {

View file

@ -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

View 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

View 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']
)); -}

View file

@ -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[];

View file

@ -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

View file

@ -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), &params_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

View file

@ -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