| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <openssl/core_names.h>
 | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  | #include <openssl/decoder.h>
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | #include <openssl/evp.h>
 | 
					
						
							|  |  |  | #include "crypto/lms.h"
 | 
					
						
							|  |  |  | #include "internal/nelem.h"
 | 
					
						
							|  |  |  | #include "testutil.h"
 | 
					
						
							|  |  |  | #include "lms.inc"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  | typedef enum OPTION_choice { | 
					
						
							|  |  |  |     OPT_ERR = -1, | 
					
						
							|  |  |  |     OPT_EOF = 0, | 
					
						
							|  |  |  |     OPT_CONFIG_FILE, | 
					
						
							|  |  |  |     OPT_TEST_ENUM | 
					
						
							|  |  |  | } OPTION_CHOICE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | static OSSL_LIB_CTX *libctx = NULL; | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  | static char *propq = NULL; | 
					
						
							|  |  |  | static OSSL_PROVIDER *nullprov = NULL; | 
					
						
							|  |  |  | static OSSL_PROVIDER *libprov = NULL; | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | static EVP_PKEY *lms_pubkey_from_data(const unsigned char *data, size_t datalen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     EVP_PKEY *key = NULL; | 
					
						
							|  |  |  |     OSSL_PARAM params[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-02 16:21:39 +08:00
										 |  |  |     params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |                                                   (unsigned char *)data, datalen); | 
					
						
							|  |  |  |     params[1] = OSSL_PARAM_construct_end(); | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  |     ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "LMS", propq)) | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |         && TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1) | 
					
						
							|  |  |  |         && (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) == 1); | 
					
						
							|  |  |  |     if (ret == 0) { | 
					
						
							|  |  |  |         EVP_PKEY_free(key); | 
					
						
							|  |  |  |         key = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     return key; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_bad_pub_len_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[1]; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     size_t publen = 0; | 
					
						
							|  |  |  |     unsigned char pubdata[128]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_size_t_le(td->publen + 16, sizeof(pubdata))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OPENSSL_cleanse(pubdata, sizeof(pubdata)); | 
					
						
							|  |  |  |     memcpy(pubdata, td->pub, td->publen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (publen = 0; publen <= td->publen + 16; publen += 3) { | 
					
						
							|  |  |  |         if (publen == td->publen) | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         if (!TEST_ptr_null(pkey = lms_pubkey_from_data(pubdata, publen))) | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     if (ret == 0) | 
					
						
							|  |  |  |         TEST_note("Incorrectly accepted public key of length %u (expected %u)", | 
					
						
							|  |  |  |                   (unsigned)publen, (unsigned)td->publen); | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret == 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  | static int lms_pubkey_decoder_fail_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     OSSL_DECODER_CTX *dctx = NULL; | 
					
						
							|  |  |  |     int selection = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     const unsigned char *pdata; | 
					
						
							|  |  |  |     size_t pdatalen; | 
					
						
							|  |  |  |     static const unsigned char pub_bad_LMSType[] = { | 
					
						
							|  |  |  |         0x00, 0x00, 0x00, 0xAA | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     static const unsigned char pub_bad_OTSType[] = { | 
					
						
							|  |  |  |         0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAA | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, NULL, NULL, "LMS", | 
					
						
							|  |  |  |                                                        selection, | 
					
						
							|  |  |  |                                                        libctx, NULL))) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pdata = td->pub; | 
					
						
							|  |  |  |     pdatalen = 3; | 
					
						
							|  |  |  |     if (!TEST_false(OSSL_DECODER_from_data(dctx, &pdata, &pdatalen))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pdatalen = SIZE_MAX; | 
					
						
							|  |  |  |     if (!TEST_false(OSSL_DECODER_from_data(dctx, &pdata, &pdatalen))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pdata = pub_bad_LMSType; | 
					
						
							|  |  |  |     pdatalen = sizeof(pub_bad_LMSType); | 
					
						
							|  |  |  |     if (!TEST_false(OSSL_DECODER_from_data(dctx, &pdata, &pdatalen))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pdata = pub_bad_OTSType; | 
					
						
							|  |  |  |     pdatalen = sizeof(pub_bad_OTSType); | 
					
						
							|  |  |  |     if (!TEST_false(OSSL_DECODER_from_data(dctx, &pdata, &pdatalen))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |     OSSL_DECODER_CTX_free(dctx); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static EVP_PKEY *key_decode_from_bio(BIO *bio, const char *keytype) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     OSSL_DECODER_CTX *dctx = NULL; | 
					
						
							|  |  |  |     int selection = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, NULL, NULL, | 
					
						
							|  |  |  |                                                        keytype, | 
					
						
							|  |  |  |                                                        selection, | 
					
						
							|  |  |  |                                                        libctx, NULL))) | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_true(OSSL_DECODER_from_bio(dctx, bio))) { | 
					
						
							|  |  |  |         EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |         pkey = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     OSSL_DECODER_CTX_free(dctx); | 
					
						
							|  |  |  |     return pkey; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static EVP_PKEY *key_decode_from_data(const unsigned char *data, size_t datalen, | 
					
						
							|  |  |  |                                       const char *keytype) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     BIO *bio; | 
					
						
							|  |  |  |     EVP_PKEY *key = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-07 10:11:52 +08:00
										 |  |  |     if (!TEST_ptr(bio = BIO_new_mem_buf(data, (int)datalen))) | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  |         return NULL; | 
					
						
							|  |  |  |     key = key_decode_from_bio(bio, keytype); | 
					
						
							|  |  |  |     BIO_free(bio); | 
					
						
							|  |  |  |     return key; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_key_decode_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td1 = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY *key = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = TEST_ptr(key = key_decode_from_data(td1->pub, td1->publen, NULL)); | 
					
						
							|  |  |  |     EVP_PKEY_free(key); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_pubkey_decoder_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY *pub = NULL; | 
					
						
							|  |  |  |     OSSL_DECODER_CTX *dctx = NULL; | 
					
						
							|  |  |  |     const unsigned char *data; | 
					
						
							|  |  |  |     size_t data_len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(dctx = OSSL_DECODER_CTX_new_for_pkey(&pub, "xdr", NULL, | 
					
						
							|  |  |  |                                                        "LMS", | 
					
						
							|  |  |  |                                                        OSSL_KEYMGMT_SELECT_PUBLIC_KEY, | 
					
						
							|  |  |  |                                                        libctx, NULL))) | 
					
						
							|  |  |  |         goto err; | 
					
						
							|  |  |  |     data = td->pub; | 
					
						
							|  |  |  |     data_len = td->publen; | 
					
						
							|  |  |  |     if (!TEST_true(OSSL_DECODER_from_data(dctx, &data, &data_len))) | 
					
						
							|  |  |  |         goto err; | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  |     EVP_PKEY_free(pub); | 
					
						
							|  |  |  |     OSSL_DECODER_CTX_free(dctx); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | static int lms_key_eq_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  |     EVP_PKEY *key[4]; | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |     LMS_ACVP_TEST_DATA *td1 = &lms_testdata[0]; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td2 = &lms_testdata[1]; | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  |     size_t i; | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | #ifndef OPENSSL_NO_EC
 | 
					
						
							|  |  |  |     EVP_PKEY *eckey = NULL; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  |     for (i = 0; i < OSSL_NELEM(key); i++) | 
					
						
							|  |  |  |         key[i] = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |     if (!TEST_ptr(key[0] = lms_pubkey_from_data(td1->pub, td1->publen)) | 
					
						
							|  |  |  |             || !TEST_ptr(key[1] = lms_pubkey_from_data(td1->pub, td1->publen)) | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  |             || !TEST_ptr(key[2] = lms_pubkey_from_data(td2->pub, td2->publen)) | 
					
						
							|  |  |  |             || !TEST_ptr(key[3] = key_decode_from_data(td1->pub, td1->publen, | 
					
						
							|  |  |  |                                                        NULL))) | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = TEST_int_eq(EVP_PKEY_eq(key[0], key[1]), 1) | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  |         && TEST_int_ne(EVP_PKEY_eq(key[0], key[2]), 1) | 
					
						
							|  |  |  |         && TEST_int_eq(EVP_PKEY_eq(key[0], key[3]), 1); | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |     if (ret == 0) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef OPENSSL_NO_EC
 | 
					
						
							|  |  |  |     if (!TEST_ptr(eckey = EVP_PKEY_Q_keygen(libctx, NULL, "EC", "P-256"))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     ret = TEST_int_ne(EVP_PKEY_eq(key[0], eckey), 1); | 
					
						
							|  |  |  |     EVP_PKEY_free(eckey); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | end: | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  |     EVP_PKEY_free(key[3]); | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |     EVP_PKEY_free(key[2]); | 
					
						
							|  |  |  |     EVP_PKEY_free(key[1]); | 
					
						
							|  |  |  |     EVP_PKEY_free(key[0]); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_key_validate_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *vctx = NULL; | 
					
						
							|  |  |  |     EVP_PKEY *key = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(key = lms_pubkey_from_data(td->pub, td->publen))) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     if (!TEST_ptr(vctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     ret = TEST_int_eq(EVP_PKEY_check(vctx), 1); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(vctx); | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     EVP_PKEY_free(key); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  | static int lms_verify_test(int tst) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[tst]; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     EVP_SIGNATURE *sig = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = TEST_ptr(pkey = lms_pubkey_from_data(td->pub, td->publen)) | 
					
						
							|  |  |  |         && TEST_ptr(sig = EVP_SIGNATURE_fetch(libctx, "LMS", NULL)) | 
					
						
							|  |  |  |         && TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, NULL)) | 
					
						
							|  |  |  |         && TEST_int_eq(EVP_PKEY_verify_message_init(ctx, sig, NULL), 1) | 
					
						
							|  |  |  |         && TEST_int_eq(EVP_PKEY_verify(ctx, td->sig, td->siglen, | 
					
						
							|  |  |  |                                        td->msg, td->msglen), 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     EVP_SIGNATURE_free(sig); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_digest_verify_fail_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY *pub = NULL; | 
					
						
							|  |  |  |     EVP_MD_CTX *vctx = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(pub = lms_pubkey_from_data(td->pub, td->publen))) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     if (!TEST_ptr(vctx = EVP_MD_CTX_new())) | 
					
						
							|  |  |  |         goto err; | 
					
						
							|  |  |  |     /* Only one shot mode is supported, streaming fails to initialise */ | 
					
						
							|  |  |  |     if (!TEST_int_eq(EVP_DigestVerifyInit_ex(vctx, NULL, NULL, libctx, NULL, | 
					
						
							|  |  |  |                                              pub, NULL), 0)) | 
					
						
							|  |  |  |         goto err; | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  |  err: | 
					
						
							|  |  |  |     EVP_PKEY_free(pub); | 
					
						
							|  |  |  |     EVP_MD_CTX_free(vctx); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:44:56 +08:00
										 |  |  | static int lms_digest_signing_fail_test(void) | 
					
						
							| 
									
										
										
										
											2025-06-26 16:36:37 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY *pub = NULL; | 
					
						
							|  |  |  |     EVP_MD_CTX *vctx = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(pub = lms_pubkey_from_data(td->pub, td->publen))) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     if (!TEST_ptr(vctx = EVP_MD_CTX_new())) | 
					
						
							|  |  |  |         goto err; | 
					
						
							|  |  |  |     if (!TEST_int_eq(EVP_DigestSignInit_ex(vctx, NULL, NULL, libctx, NULL, | 
					
						
							|  |  |  |                                            pub, NULL), 0)) | 
					
						
							|  |  |  |         goto err; | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  |  err: | 
					
						
							|  |  |  |     EVP_PKEY_free(pub); | 
					
						
							|  |  |  |     EVP_MD_CTX_free(vctx); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:44:56 +08:00
										 |  |  | static int lms_message_signing_fail_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     EVP_SIGNATURE *sig = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = TEST_ptr(pkey = lms_pubkey_from_data(td->pub, td->publen)) | 
					
						
							|  |  |  |         && TEST_ptr(sig = EVP_SIGNATURE_fetch(libctx, "LMS", NULL)) | 
					
						
							|  |  |  |         && TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, NULL)) | 
					
						
							|  |  |  |         && TEST_int_eq(EVP_PKEY_sign_message_init(ctx, sig, NULL), -2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     EVP_SIGNATURE_free(sig); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-27 05:17:12 +08:00
										 |  |  | static int lms_paramgen_fail_test(void) | 
					
						
							| 
									
										
										
										
											2025-06-26 16:44:56 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     int ret; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "LMS", NULL)) | 
					
						
							|  |  |  |         && TEST_int_eq(EVP_PKEY_paramgen_init(ctx), -2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-27 05:17:12 +08:00
										 |  |  | static int lms_keygen_fail_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "LMS", NULL)) | 
					
						
							|  |  |  |         && TEST_int_eq(EVP_PKEY_keygen_init(ctx), -2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  | static int lms_verify_fail_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[0]; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(pkey = lms_pubkey_from_data(td->pub, td->publen)) | 
					
						
							|  |  |  |             || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, NULL))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     /* Only one shot mode is supported, streaming fails to initialise */ | 
					
						
							|  |  |  |     if (!TEST_int_eq(EVP_PKEY_verify_init(ctx), -2)) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_verify_bad_sig_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0, i = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[1]; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     EVP_SIGNATURE *sig = NULL; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     unsigned char *sig_data = NULL, corrupt_mask = 0x01; | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * Corrupt every 3rd byte to run less tests. The smallest element of an XDR | 
					
						
							|  |  |  |      * encoding is 4 bytes, so this will corrupt every element. | 
					
						
							|  |  |  |      * Memory sanitisation is slow, so a larger step size is used for this. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  | #if defined(OSSL_SANITIZE_MEMORY)
 | 
					
						
							|  |  |  |     const int step = (int)(td->siglen >> 1); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     const int step = 3; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Copy the signature so that we can corrupt it */ | 
					
						
							|  |  |  |     sig_data = OPENSSL_memdup(td->sig, td->siglen); | 
					
						
							|  |  |  |     if (sig_data == NULL) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(pkey = lms_pubkey_from_data(td->pub, td->publen)) | 
					
						
							|  |  |  |             || !TEST_ptr(sig = EVP_SIGNATURE_fetch(libctx, "LMS", NULL)) | 
					
						
							|  |  |  |             || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, NULL))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_int_eq(EVP_PKEY_verify_message_init(ctx, sig, NULL), 1)) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < (int)td->siglen; i += step) { | 
					
						
							|  |  |  |         sig_data[i] ^= corrupt_mask; /* corrupt a byte */ | 
					
						
							|  |  |  |         if (i > 0) | 
					
						
							|  |  |  |             sig_data[i - step] ^= corrupt_mask; /* Reset the previously corrupt byte */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!TEST_int_eq(EVP_PKEY_verify(ctx, sig_data, td->siglen, | 
					
						
							|  |  |  |                                          td->msg, td->msglen), 0)) { | 
					
						
							|  |  |  |             TEST_note("Incorrectly passed when %dth byte of signature" | 
					
						
							|  |  |  |                       " was corrupted", i); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     EVP_SIGNATURE_free(sig); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |     OPENSSL_free(sig_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Test that using the incorrect signature lengths (both shorter and longer) | 
					
						
							|  |  |  |  * fail. | 
					
						
							|  |  |  |  * NOTE: It does not get an out of bounds read due to the signature | 
					
						
							|  |  |  |  * knowing how large it should be | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int lms_verify_bad_sig_len_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[1]; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     EVP_SIGNATURE *sig = NULL; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     size_t siglen = 0; | 
					
						
							|  |  |  |     const int step = 3; | 
					
						
							|  |  |  |     unsigned char sigdata[4096]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(pkey = lms_pubkey_from_data(td->pub, td->publen)) | 
					
						
							|  |  |  |             || !TEST_ptr(sig = EVP_SIGNATURE_fetch(libctx, "LMS", NULL)) | 
					
						
							|  |  |  |             || !TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, NULL))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_size_t_le(td->siglen + 16, sizeof(sigdata))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OPENSSL_cleanse(sigdata, sizeof(sigdata)); | 
					
						
							|  |  |  |     memcpy(sigdata, td->sig, td->siglen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = 0; | 
					
						
							|  |  |  |     for (siglen = 0; siglen < td->siglen + 16; siglen += step) { | 
					
						
							|  |  |  |         if (siglen == td->siglen)   /* ignore the size that should pass */ | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         if (!TEST_int_eq(EVP_PKEY_verify_message_init(ctx, sig, NULL), 1)) | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         if (!TEST_int_eq(EVP_PKEY_verify(ctx, sigdata, siglen, | 
					
						
							|  |  |  |                                          td->msg, td->msglen), 0)) { | 
					
						
							|  |  |  |             TEST_note("Incorrectly accepted signature key of length" | 
					
						
							|  |  |  |                       " %u (expected %u)", (unsigned)siglen, (unsigned)td->siglen); | 
					
						
							|  |  |  |             goto end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     EVP_SIGNATURE_free(sig); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int lms_verify_bad_pub_sig_test(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     LMS_ACVP_TEST_DATA *td = &lms_testdata[1]; | 
					
						
							|  |  |  |     int ret = 0, i = 0; | 
					
						
							|  |  |  |     EVP_PKEY *pkey = NULL; | 
					
						
							|  |  |  |     EVP_SIGNATURE *sig = NULL; | 
					
						
							|  |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							|  |  |  |     unsigned char *pub = NULL; | 
					
						
							|  |  |  |     const int step = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Copy the public key data so that we can corrupt it */ | 
					
						
							|  |  |  |     if (!TEST_ptr(pub = OPENSSL_memdup(td->pub, td->publen))) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!TEST_ptr(sig = EVP_SIGNATURE_fetch(libctx, "LMS", NULL))) | 
					
						
							|  |  |  |         goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < (int)td->publen; i += step) { | 
					
						
							| 
									
										
										
										
											2025-07-14 09:06:10 +08:00
										 |  |  |         pub[i] ^= 1; /* corrupt a byte */ | 
					
						
							|  |  |  |         /* Corrupting the public key may cause the key load to fail */ | 
					
						
							|  |  |  |         pkey = lms_pubkey_from_data(pub, td->publen); | 
					
						
							|  |  |  |         if (pkey != NULL) { | 
					
						
							|  |  |  |             if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, NULL))) | 
					
						
							|  |  |  |                 goto end; | 
					
						
							|  |  |  |             /* We expect the verify to fail */ | 
					
						
							|  |  |  |             if ((EVP_PKEY_verify_message_init(ctx, sig, NULL) == 1) | 
					
						
							|  |  |  |                     && !TEST_int_eq(EVP_PKEY_verify(ctx, td->sig, td->siglen, | 
					
						
							|  |  |  |                                                     td->msg, td->msglen), 0)) { | 
					
						
							|  |  |  |                 TEST_note("Incorrectly passed when byte %d of the public key" | 
					
						
							|  |  |  |                           " was corrupted", i); | 
					
						
							|  |  |  |                 goto end; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  |             EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |             pkey = NULL; | 
					
						
							| 
									
										
										
										
											2025-07-14 09:06:10 +08:00
										 |  |  |             EVP_PKEY_CTX_free(ctx); | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  |             ctx = NULL; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-07-14 09:06:10 +08:00
										 |  |  |         pub[i] ^= 1; /* restore the corrupted byte */ | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = 1; | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  |     EVP_SIGNATURE_free(sig); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  |     EVP_PKEY_free(pkey); | 
					
						
							|  |  |  |     OPENSSL_free(pub); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  | const OPTIONS *test_get_options(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static const OPTIONS options[] = { | 
					
						
							|  |  |  |         OPT_TEST_OPTIONS_DEFAULT_USAGE, | 
					
						
							|  |  |  |         { "config", OPT_CONFIG_FILE, '<', | 
					
						
							|  |  |  |           "The configuration file to use for the libctx" }, | 
					
						
							|  |  |  |         { NULL } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return options; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  | int setup_tests(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  |     OPTION_CHOICE o; | 
					
						
							|  |  |  |     char *config_file = NULL; | 
					
						
							| 
									
										
										
										
											2025-09-17 09:49:59 +08:00
										 |  |  |     EVP_PKEY_CTX *ctx = NULL; | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Swap the libctx to test non-default context only */ | 
					
						
							|  |  |  |     propq = "provider=default"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while ((o = opt_next()) != OPT_EOF) { | 
					
						
							|  |  |  |         switch (o) { | 
					
						
							|  |  |  |         case OPT_CONFIG_FILE: | 
					
						
							|  |  |  |             config_file = opt_arg(); | 
					
						
							|  |  |  |             propq = ""; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case OPT_TEST_CASES: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |         case OPT_ERR: | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!test_get_libctx(&libctx, &nullprov, config_file, &libprov, NULL)) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-17 09:49:59 +08:00
										 |  |  |     ctx = EVP_PKEY_CTX_new_from_name(libctx, "LMS", propq); | 
					
						
							|  |  |  |     if (ctx == NULL && ERR_get_error() == EVP_R_UNSUPPORTED_ALGORITHM) | 
					
						
							|  |  |  |         return TEST_skip("LMS algorithm is not available in provider"); | 
					
						
							|  |  |  |     EVP_PKEY_CTX_free(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |     ADD_TEST(lms_bad_pub_len_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_key_validate_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_key_eq_test); | 
					
						
							| 
									
										
										
										
											2024-10-01 00:38:58 +08:00
										 |  |  |     ADD_TEST(lms_key_decode_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_pubkey_decoder_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_pubkey_decoder_fail_test); | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  |     ADD_ALL_TESTS(lms_verify_test, OSSL_NELEM(lms_testdata)); | 
					
						
							|  |  |  |     ADD_TEST(lms_verify_fail_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_digest_verify_fail_test); | 
					
						
							| 
									
										
										
										
											2025-06-26 16:44:56 +08:00
										 |  |  |     ADD_TEST(lms_digest_signing_fail_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_message_signing_fail_test); | 
					
						
							| 
									
										
										
										
											2025-06-27 05:17:12 +08:00
										 |  |  |     ADD_TEST(lms_paramgen_fail_test); | 
					
						
							| 
									
										
										
										
											2025-06-26 16:44:56 +08:00
										 |  |  |     ADD_TEST(lms_keygen_fail_test); | 
					
						
							| 
									
										
										
										
											2024-10-01 10:35:43 +08:00
										 |  |  |     ADD_TEST(lms_verify_bad_sig_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_verify_bad_sig_len_test); | 
					
						
							|  |  |  |     ADD_TEST(lms_verify_bad_pub_sig_test); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-03 09:16:15 +08:00
										 |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-10-01 14:17:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | void cleanup_tests(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     OSSL_PROVIDER_unload(nullprov); | 
					
						
							|  |  |  |     OSSL_PROVIDER_unload(libprov); | 
					
						
							|  |  |  |     OSSL_LIB_CTX_free(libctx); | 
					
						
							|  |  |  | } |