Skip to content

Commit 5a8d047

Browse files
authored
Add NIST LMS Signature verify KATs (#2435)
* Add utilities to ease processing public key buffer. Signed-off-by: ashman-p <nashley@cisco.com> * Process public key to process LMS public key buffers without explicit levels. Signed-off-by: ashman-p <nashley@cisco.com> * Update hash results for NIST based KATs. Signed-off-by: ashman-p <nashley@cisco.com> * Use NIST based KATs. Signed-off-by: ashman-p <nashley@cisco.com> * Update test to read NIST HSS and LMS public keys. Signed-off-by: ashman-p <nashley@cisco.com> * Fix formatting errors. Signed-off-by: Norman Ashley <nashley@cisco.com> * Correct hash results. Signed-off-by: Norman Ashley <nashley@cisco.com> * Update HSS/LMS Public key detection type. Signed-off-by: ashman-p <nashley@cisco.com> * Update public key and sig OTS field matching. Signed-off-by: ashman-p <nashley@cisco.com> --------- Signed-off-by: ashman-p <nashley@cisco.com> Signed-off-by: Norman Ashley <nashley@cisco.com>
1 parent 0096577 commit 5a8d047

6 files changed

Lines changed: 207 additions & 49 deletions

File tree

src/sig_stfl/lms/external/common_defs.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <stdint.h>
1111
#include <stdbool.h>
12+
#include <stddef.h>
1213
#include <oqs/oqs.h>
1314
#define MAX_HASH 32 /* Length of the largest hash we support */
1415

@@ -68,6 +69,45 @@ typedef uint_fast64_t sequence_t;
6869
#define LMOTS_SHA256_N32_W4 0x00000003
6970
#define LMOTS_SHA256_N32_W8 0x00000004
7071

72+
/* Supported LMS / LM-OTS parameter-set identifiers (must stay in sync with #defines above) */
73+
#define OQS_LMS_TYPE_LIST_LEN 5u
74+
#define OQS_LMOTS_TYPE_LIST_LEN 4u
75+
76+
static const param_set_t OQS_LMS_TYPE_LIST[OQS_LMS_TYPE_LIST_LEN] = {
77+
LMS_SHA256_N32_H5,
78+
LMS_SHA256_N32_H10,
79+
LMS_SHA256_N32_H15,
80+
LMS_SHA256_N32_H20,
81+
LMS_SHA256_N32_H25,
82+
};
83+
84+
static const param_set_t OQS_LMOTS_TYPE_LIST[OQS_LMOTS_TYPE_LIST_LEN] = {
85+
LMOTS_SHA256_N32_W1,
86+
LMOTS_SHA256_N32_W2,
87+
LMOTS_SHA256_N32_W4,
88+
LMOTS_SHA256_N32_W8,
89+
};
90+
91+
static inline bool oqs_lms_type_list_contains(param_set_t v) {
92+
size_t i;
93+
for (i = 0; i < OQS_LMS_TYPE_LIST_LEN; i++) {
94+
if (OQS_LMS_TYPE_LIST[i] == v) {
95+
return true;
96+
}
97+
}
98+
return false;
99+
}
100+
101+
static inline bool oqs_lmots_type_list_contains(param_set_t v) {
102+
size_t i;
103+
for (i = 0; i < OQS_LMOTS_TYPE_LIST_LEN; i++) {
104+
if (OQS_LMOTS_TYPE_LIST[i] == v) {
105+
return true;
106+
}
107+
}
108+
return false;
109+
}
110+
71111
/*
72112
* Internal formats of various hashes
73113
*

src/sig_stfl/lms/external/hss_verify_inc.c

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,75 @@
1616
#include "lm_ots_common.h"
1717
#include "hss.h"
1818

19+
/*
20+
* Classify a serialized public key buffer as HSS or LMS by correlating its
21+
* leading fields with the incremental-signature header.
22+
*
23+
* Public-key layouts (RFC 8554, sec. 5.3 and 6.1):
24+
* - HSS pk: u32(L) || u32(lm_type) || u32(lm_ots) || I || T[1]
25+
* - LMS pk: u32(lm_type) || u32(lm_ots) || I || T[1]
26+
*
27+
* signature header :
28+
* sig[] = u32(L-1) || u32(q) || u32(lm_ots) ...
29+
* L = (the HSS level count minus one)
30+
* Therefore, for an HSS public key, sig[0..3] + 1 must equal pk[0..3] and the
31+
* resulting level count must be a valid HSS level (1..MAX_HSS_LEVELS). As a
32+
* cross-check, pk[4..7] is required to be a valid LMS type for HSS
33+
* and pk[8..11] is required to be a valid LM-OTS type for LMS;
34+
* Further, the signature LM-OTS types must those present in the public key.
35+
*/
36+
static bool is_hss_public_key(const unsigned char *public_key,
37+
const unsigned char *signature) {
38+
uint_fast32_t sig_levels =
39+
(uint_fast32_t)get_bigendian(signature, 4) + 1;
40+
uint_fast32_t sig_lm_ots = (uint_fast32_t)get_bigendian(signature + 8, 4);
41+
uint_fast32_t pk_levels = (uint_fast32_t)get_bigendian(public_key, 4);
42+
uint_fast32_t pk_lm_type = (uint_fast32_t)get_bigendian(public_key + 4, 4);
43+
uint_fast32_t pk_lm_ots = (uint_fast32_t)get_bigendian(public_key + 8, 4);
44+
45+
if (sig_levels < MIN_HSS_LEVELS || sig_levels > MAX_HSS_LEVELS) {
46+
return false;
47+
}
48+
if (pk_levels != sig_levels) {
49+
return false;
50+
}
51+
if (pk_lm_ots != sig_lm_ots) {
52+
return false;
53+
}
54+
return oqs_lms_type_list_contains(pk_lm_type) && oqs_lmots_type_list_contains(pk_lm_ots);
55+
}
56+
57+
/*
58+
* Parse a public key buffer as a serialized LMS or HSS public key.
59+
* Returns 0 on failure; 1 for a single-tree LMS public key; or the HSS level
60+
* count (from the first four bytes) when the buffer matches the HSS layout.
61+
* Validate by matching the fields in the signature buffer.
62+
*/
63+
static uint_fast32_t public_key_levels(const unsigned char *public_key,
64+
const unsigned char *signature) {
65+
param_set_t w0 = (param_set_t)get_bigendian(public_key, 4);
66+
param_set_t w1 = (param_set_t)get_bigendian(public_key + 4, 4);
67+
param_set_t w2 = (param_set_t)get_bigendian(public_key + 8, 4);
68+
69+
if (is_hss_public_key(public_key, signature)) {
70+
uint_fast32_t levels = (uint_fast32_t)w0;
71+
if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) {
72+
return 0;
73+
}
74+
if (!oqs_lms_type_list_contains(w1) || !oqs_lmots_type_list_contains(w2)) {
75+
return 0;
76+
}
77+
78+
return levels;
79+
}
80+
81+
if (oqs_lms_type_list_contains(w0) && oqs_lmots_type_list_contains(w1)) {
82+
return 1;
83+
}
84+
85+
return 0;
86+
}
87+
1988
/*
2089
* Start the process of validating an HSS signature incrementally. Parameters:
2190
* ctx - The state we'll use to track the incremental validation
@@ -39,31 +108,25 @@ bool hss_validate_signature_init(
39108
/* we got a failure */
40109

41110
const unsigned char *orig_signature = signature;
42-
;
111+
43112
/* Get the number of levels the signature claims */
44113
if (signature_len < 4) {
45114
ctx->status = info->error_code = hss_error_bad_signature;
46115
return false;
47116
}
48117
uint_fast32_t levels = (uint_fast32_t)get_bigendian( signature, 4 ) + 1;
49118
/* +1 because what's in the signature is levels-1 */
119+
uint_fast32_t pub_levels = public_key_levels(public_key, orig_signature);
120+
if (is_hss_public_key(public_key, orig_signature)) {
121+
public_key += 4;
122+
}
50123
signature += 4; signature_len -= 4;
51-
uint_fast32_t pub_levels = (uint_fast32_t)get_bigendian( public_key, 4 );
52-
public_key += 4;
53124

54125
if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS ) {
55126
ctx->status = info->error_code = hss_error_bad_signature;
56127
return false;
57128
}
58129

59-
/*
60-
* The level for public keys for single level trees sometimes have a value of zero.
61-
* in this case, bump public key level to 1.
62-
*/
63-
if ((levels == 1) && (pub_levels == 0)) {
64-
pub_levels = 1;
65-
}
66-
67130
if (levels != pub_levels) {
68131
/* Signature and public key don't agree */
69132
ctx->status = info->error_code = hss_error_bad_signature;

tests/KATs/sig_stfl/kats.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@
3636
"XMSSMT-SHAKE_60/3_256": "7ca90f7c64b21d844975ef39c48405dc61922f6fd0be8cbb88b2a18a54bc754d",
3737
"XMSSMT-SHAKE_60/6_256": "c11ca5be510f88c9c8188cb98da65e7d4b2be1cd7efc5a9769348c4fa2b33b24",
3838
"XMSSMT-SHAKE_60/12_256": "79b6690809f1317fbc2466590e4fccc8a7f706b05abcb277ad1018565096ad88",
39-
"LMS_SHA256_H5_W1": "26273b16351d40b7a7bf73db200c4494cba890624235d214bca9368e60cd1c02",
39+
"LMS_SHA256_H5_W1": "071e5fedf41bfcf43c2f10ed238fbe18da0a5773095064bb0a62ce79f2ba58c9",
4040
"LMS_SHA256_H5_W2": "a4877dbf9f06a08469afaee13cf25ef98e20064d2be0009888c68698995aca7d",
4141
"LMS_SHA256_H5_W4": "e13ceda1f66c90cad1a15087f26bb025378f7fbb69ecfc138ac365a9bf3fb6a5",
4242
"LMS_SHA256_H5_W8": "175f2b5b8a6e8a5faa82bdeb2779a88cc977ad7cca46d815b0d02c6dd672396d",
43-
"LMS_SHA256_H10_W1": "b52bb3ff8fab21d69eb0933f5eeffac1380f87c1c8154983cfe4f3f27fcfb1e9",
43+
"LMS_SHA256_H10_W1": "86d6c14478ff270b89e8cb158dacfe7c667466e93e9f226ca1bfe38f5b3a2aaa",
4444
"LMS_SHA256_H10_W2": "a1a2709362ac8aeb956d0d88cd4a42ce2fc8df9a69979270299b9471f61c3dbd",
4545
"LMS_SHA256_H10_W4": "707e4ff1adb835f6e79453caca0c787c156a2ee270b1657a42ebbe6eb7424494",
4646
"LMS_SHA256_H10_W8": "799e7bdf00fc0839519e6847b7df40763b89949e1d1b99bd5b9f669387bf0fbc",

0 commit comments

Comments
 (0)