mirror of
https://github.com/openssl/openssl.git
synced 2026-01-18 17:11:31 +01:00
Add LMS evp_test using NIST ACVP test data.
This covers all LMS algorithm parameter sets. The following changes were done to handle the tests: (1) Changed LMS to use OSSL_PKEY_PARAM_PUB_KEY instead of OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY for import/export. (There is no reason to have the encoded form for verify operations). (2) Fixed a bug for W=1 with truncated digests. The checksum was using a value of 8-w, which was off by 1 for this case. A value was added to the ots parameters that represents this value. (3) A check in evp_test for a NID was removed since LMS does not have OIDS (HSS does). (4) the unused PROPERTIES param was removed from the LMS keymanager. Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Paul Dale <ppzgs1@gmail.com> (Merged from https://github.com/openssl/openssl/pull/27885)
This commit is contained in:
@@ -13,24 +13,24 @@
|
||||
|
||||
/* Refer to SP800-208 Section 4 LM-OTS parameter sets */
|
||||
static const LM_OTS_PARAMS lm_ots_params[] = {
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W1, 32, 1, 265, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W2, 32, 2, 133, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W4, 32, 4, 67, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W8, 32, 8, 34, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W1, 24, 1, 200, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W2, 24, 2, 101, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W4, 24, 4, 51, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W8, 24, 8, 26, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W1, 32, 1, 265, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W2, 32, 2, 133, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W4, 32, 4, 67, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W8, 32, 8, 34, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W1, 32, 1, 265, 7, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W2, 32, 2, 133, 6, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W4, 32, 4, 67, 4, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N32_W8, 32, 8, 34, 0, "SHA256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W1, 24, 1, 200, 8, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W2, 24, 2, 101, 6, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W4, 24, 4, 51, 4, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHA256_N24_W8, 24, 8, 26, 0, "SHA256-192"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W1, 32, 1, 265, 7, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W2, 32, 2, 133, 6, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W4, 32, 4, 67, 4, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N32_W8, 32, 8, 34, 0, "SHAKE-256"},
|
||||
/* SHAKE-256/192 - OpenSSL does not support this as a name */
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W1, 24, 1, 200, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W2, 24, 2, 101, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W4, 24, 4, 51, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W8, 24, 8, 26, "SHAKE-256"},
|
||||
{ 0, 0, 0, 0, NULL },
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W1, 24, 1, 200, 8, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W2, 24, 2, 101, 6, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W4, 24, 4, 51, 4, "SHAKE-256"},
|
||||
{ OSSL_LM_OTS_TYPE_SHAKE_N24_W8, 24, 8, 26, 0, "SHAKE-256"},
|
||||
{ 0, 0, 0, 0, 0, NULL },
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,5 +62,5 @@ uint16_t ossl_lm_ots_params_checksum(const LM_OTS_PARAMS *params,
|
||||
|
||||
for (i = 0; i < bytes; ++i)
|
||||
sum += end - coef(S, i, params->w);
|
||||
return (sum << (8 - params->w));
|
||||
return (sum << params->ls);
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ int ossl_lms_pubkey_from_params(const OSSL_PARAM params[], LMS_KEY *lmskey)
|
||||
{
|
||||
const OSSL_PARAM *p = NULL;
|
||||
|
||||
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
|
||||
p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
|
||||
if (p != NULL) {
|
||||
if (p->data == NULL
|
||||
|| p->data_type != OSSL_PARAM_OCTET_STRING
|
||||
|
||||
@@ -20,7 +20,7 @@ LMS keymanager for import and export.
|
||||
|
||||
=over 4
|
||||
|
||||
=item "encoded-pub-key" (B<OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY>) <octet string>
|
||||
=item "pub" (B<OSSL_PKEY_PARAM_PUB_KEY>) <octet string>
|
||||
|
||||
Used for getting and setting the encoding of an LMS public key. The public key
|
||||
is expected to be in XDR format.
|
||||
|
||||
@@ -98,6 +98,12 @@ typedef struct lm_ots_params_st {
|
||||
* One of (200, 101, 51, 26) for n = 24, for w=1,2,4,8
|
||||
*/
|
||||
uint32_t p;
|
||||
/*
|
||||
* The size of the shift needed to move the checksum so
|
||||
* that it appears in the checksum digits.
|
||||
* See RFC 8554 Appendix B. LM-OTS Parameter Options
|
||||
*/
|
||||
uint32_t ls;
|
||||
const char *digestname; /* Hash Name */
|
||||
} LM_OTS_PARAMS;
|
||||
|
||||
|
||||
@@ -493,7 +493,7 @@ static int self_test_LMS(OSSL_SELF_TEST *st, OSSL_LIB_CTX *libctx)
|
||||
OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_KAT_SIGNATURE,
|
||||
OSSL_SELF_TEST_DESC_SIGN_LMS);
|
||||
|
||||
pm[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
|
||||
pm[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
|
||||
(unsigned char *)t->pub,
|
||||
t->publen);
|
||||
pm[1] = OSSL_PARAM_construct_end();
|
||||
|
||||
@@ -79,8 +79,7 @@ static int lms_import(void *keydata, int selection, const OSSL_PARAM params[])
|
||||
}
|
||||
|
||||
static const OSSL_PARAM lms_key_types[] = {
|
||||
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
|
||||
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
|
||||
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
|
||||
OSSL_PARAM_END
|
||||
};
|
||||
static const OSSL_PARAM *lms_imexport_types(int selection)
|
||||
@@ -109,7 +108,7 @@ static int lms_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
|
||||
return 0;
|
||||
|
||||
if (!ossl_param_build_set_octet_string(tmpl, params,
|
||||
OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
|
||||
OSSL_PKEY_PARAM_PUB_KEY,
|
||||
lmskey->pub.encoded,
|
||||
lmskey->pub.encodedlen))
|
||||
goto err;
|
||||
|
||||
@@ -5278,7 +5278,6 @@ start:
|
||||
char *strnid = NULL, *keydata = NULL;
|
||||
unsigned char *keybin;
|
||||
size_t keylen;
|
||||
int nid;
|
||||
|
||||
if (strcmp(pp->key, "PrivateKeyRaw") == 0)
|
||||
klist = &private_keys;
|
||||
@@ -5297,11 +5296,6 @@ start:
|
||||
return 0;
|
||||
}
|
||||
|
||||
nid = OBJ_txt2nid(strnid);
|
||||
if (nid == NID_undef) {
|
||||
TEST_info("Unrecognised algorithm NID");
|
||||
return 0;
|
||||
}
|
||||
if (!parse_bin(keydata, &keybin, &keylen)) {
|
||||
TEST_info("Failed to create binary key");
|
||||
return 0;
|
||||
|
||||
79
test/lms_parse.py
Normal file
79
test/lms_parse.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
# 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
|
||||
|
||||
# A python program written to parse (version 1.0) of the ACVP test vectors for
|
||||
# LMS. The file that can be processed by this utility can be downloaded from
|
||||
# https://raw.githubusercontent.com/usnistgov/ACVP-Server/refs/heads/master/gen-val/json-files/LMS-sigVer-1.0/internalProjection.json
|
||||
# and output from this utility to
|
||||
# test/recipes/30-test_evp_data/evppkey_lms.txt
|
||||
#
|
||||
# e.g. python3 mldsa_parse.py ~/Downloads/internalProjection.json > ./test/recipes/30-test_evp_data/evppkey_lms.txt
|
||||
#
|
||||
import json
|
||||
import argparse
|
||||
import datetime
|
||||
|
||||
def print_label(label, value):
|
||||
print(label + " = " + value)
|
||||
|
||||
def print_hexlabel(label, tag, value):
|
||||
print(label + " = hex" + tag + ":" + value)
|
||||
|
||||
def parse_lms_sig_ver(groups):
|
||||
for grp in groups:
|
||||
lmsmode = grp["lmsMode"]
|
||||
lmotsmode = grp["lmOtsMode"]
|
||||
name = lmsmode + "_" + str(grp["tgId"])
|
||||
pubkey = grp["publicKey"]
|
||||
|
||||
if grp["testType"] != "AFT":
|
||||
continue
|
||||
|
||||
print_label("Title", lmsmode + " " + lmotsmode)
|
||||
print("");
|
||||
print_label("PublicKeyRaw", name + ":" + "LMS" + ":" + pubkey)
|
||||
for tst in grp['tests']:
|
||||
testname = lmsmode + "_" + str(tst['tcId'])
|
||||
print("");
|
||||
if "reason" in tst:
|
||||
print("# " + tst['reason'])
|
||||
print_label("FIPSversion", ">=3.6.0")
|
||||
print_label("Verify-Message-Public", "LMS:" + name)
|
||||
print_label("Input", tst['message'])
|
||||
print_label("Output", tst['signature'])
|
||||
if not tst['testPassed']:
|
||||
print_label("Result", "VERIFY_ERROR")
|
||||
|
||||
parser = argparse.ArgumentParser(description="")
|
||||
parser.add_argument('filename', type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Open and read the JSON file
|
||||
with open(args.filename, 'r') as file:
|
||||
data = json.load(file)
|
||||
|
||||
year = datetime.date.today().year
|
||||
version = data['vsId']
|
||||
revision = data['revision']
|
||||
algorithm = data['algorithm']
|
||||
|
||||
print("# Copyright " + str(year) + " The OpenSSL Project Authors. All Rights Reserved.")
|
||||
print("#")
|
||||
print("# Licensed under the Apache License 2.0 (the \"License\"). You may not use")
|
||||
print("# this file except in compliance with the License. You can obtain a copy")
|
||||
print("# in the file LICENSE in the source distribution or at")
|
||||
print("# https://www.openssl.org/source/license.html\n")
|
||||
print("# ACVP test data for " + algorithm + " generated from")
|
||||
print("# https://raw.githubusercontent.com/usnistgov/ACVP-Server/refs/heads/master/gen-val/json-files/LMS-sigVer-1.0/internalProjection.json")
|
||||
print("# [version " + str(version) + " : revision " + str(revision) + "]")
|
||||
print("")
|
||||
|
||||
if algorithm == "LMS":
|
||||
parse_lms_sig_ver(data['testGroups'])
|
||||
else:
|
||||
print("Unsupported algorithm " + algorithm)
|
||||
@@ -34,7 +34,7 @@ static EVP_PKEY *lms_pubkey_from_data(const unsigned char *data, size_t datalen)
|
||||
EVP_PKEY *key = NULL;
|
||||
OSSL_PARAM params[2];
|
||||
|
||||
params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
|
||||
params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
|
||||
(unsigned char *)data, datalen);
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "LMS", propq))
|
||||
|
||||
@@ -34,6 +34,7 @@ my $no_siv = disabled("siv");
|
||||
my $no_argon2 = disabled("argon2");
|
||||
my $no_ml_dsa = disabled("ml-dsa");
|
||||
my $no_ml_kem = disabled("ml-kem");
|
||||
my $no_lms = disabled("lms");
|
||||
|
||||
# Default config depends on if the legacy module is built or not
|
||||
my $defaultcnf = $no_legacy ? 'default.cnf' : 'default-and-legacy.cnf';
|
||||
@@ -124,6 +125,9 @@ push @files, qw(
|
||||
evppkey_ml_kem_keygen.txt
|
||||
evppkey_ml_kem_encap_decap.txt
|
||||
) unless $no_ml_kem;
|
||||
push @files, qw(
|
||||
evppkey_lms_sigver.txt
|
||||
) unless $no_lms;
|
||||
|
||||
# A list of tests that only run with the default provider
|
||||
# (i.e. The algorithms are not present in the fips provider)
|
||||
|
||||
2411
test/recipes/30-test_evp_data/evppkey_lms_sigver.txt
Normal file
2411
test/recipes/30-test_evp_data/evppkey_lms_sigver.txt
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user