diff --git a/benchmark/bench_modules/wh_bench_mod_all.h b/benchmark/bench_modules/wh_bench_mod_all.h index 629be885a..6cf71d466 100644 --- a/benchmark/bench_modules/wh_bench_mod_all.h +++ b/benchmark/bench_modules/wh_bench_mod_all.h @@ -424,4 +424,30 @@ int wh_Bench_Mod_MlKem1024DecapsDma(whClientContext* client, whBenchOpContext* ctx, int id, void* params); +/* + * LMS benchmark module prototypes (wh_bench_mod_lms.c) + * + * LMS is stateful (hash-based) and DMA-only in wolfHSM, so there is a single + * variant per operation (no non-DMA counterpart). + */ +int wh_Bench_Mod_LmsKeyGen(whClientContext* client, whBenchOpContext* ctx, + int id, void* params); +int wh_Bench_Mod_LmsSign(whClientContext* client, whBenchOpContext* ctx, int id, + void* params); +int wh_Bench_Mod_LmsVerify(whClientContext* client, whBenchOpContext* ctx, + int id, void* params); + +/* + * XMSS benchmark module prototypes (wh_bench_mod_xmss.c) + * + * XMSS is stateful (hash-based) and DMA-only in wolfHSM, so there is a single + * variant per operation (no non-DMA counterpart). + */ +int wh_Bench_Mod_XmssKeyGen(whClientContext* client, whBenchOpContext* ctx, + int id, void* params); +int wh_Bench_Mod_XmssSign(whClientContext* client, whBenchOpContext* ctx, + int id, void* params); +int wh_Bench_Mod_XmssVerify(whClientContext* client, whBenchOpContext* ctx, + int id, void* params); + #endif /* WH_BENCH_MOD_ALL_H_ */ diff --git a/benchmark/bench_modules/wh_bench_mod_lms.c b/benchmark/bench_modules/wh_bench_mod_lms.c new file mode 100644 index 000000000..d4c0d1505 --- /dev/null +++ b/benchmark/bench_modules/wh_bench_mod_lms.c @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +#include "wh_bench_mod.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_crypto.h" + + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_BENCH_ENABLE) + +/* LMS is stateful (hash-based) and is only exposed by wolfHSM over DMA. KEY-GEN + * and SIGN need a full (signing) build. VERIFY additionally works in a + * verify-only build: it runs against a precomputed public key and signature + * (see the vectors below) instead of generating them. The verify path is + * therefore guarded only by WOLFSSL_HAVE_LMS, while keygen/sign additionally + * require !WOLFSSL_LMS_VERIFY_ONLY. */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) + +#include +#include "wolfssl/wolfcrypt/random.h" +#include "wolfssl/wolfcrypt/wc_lms.h" + +/* + * Timing characteristics of these benchmarks + * ------------------------------------------ + * LMS is a stateful hash-based signature scheme: each signature consumes one + * one-time key and advances the private key state. In wolfHSM the observed + * timing is shaped less by the LMS algorithm than by how the server persists + * the key: + * + * - VERIFY is state-independent. It recomputes the LM-OTS public key candidate + * and walks the H authentication-path nodes to the root, a fixed amount of + * work regardless of the signature index. Its per-op time is flat. It runs + * against a fixed precomputed (public key, signature) pair, so - unlike + * keygen/sign - it is also available in a verify-only build, the config most + * likely deployed on a verifying device in the field. + * + * - SIGN is dominated by key reconstruction, not by the signature itself. The + * server persists only a compact LMS private key to NVM; before each sign it + * calls wc_LmsKey_Reload (see _HandleLmsSignDma in wh_server_crypto.c), which + * rebuilds the Merkle tree state from the seed. That reload costs about as + * much as key generation, so SIGN throughput here tracks KEY-GEN throughput + * and stays essentially flat across indices. (Standalone, key-resident + * wolfCrypt would instead advance an in-memory auth-path traversal per sign, + * which is cheap but state-dependent - wolfHSM does not keep that state + * resident between requests.) + * + * - Because the key is reconstructed from scratch on every request, the + * wolfCrypt "sign smoothing" optimization (on by default for single-level LMS + * unless WOLFSSL_LMS_NO_SIGN_SMOOTHING is defined) has no observable effect in + * this benchmark: smoothing spreads the cost of an in-process incremental + * traversal across many resident signatures, and that traversal never + * survives between signs here. Building with WOLFSSL_LMS_NO_SIGN_SMOOTHING + * produces identical timings (verified). + * + * Benchmarking note: only the first WOLFHSM_CFG_BENCH_PK_ITERS indices are + * signed. Because each sign reloads the full tree, per-sign cost is uniform, so + * this small sample is representative for LMS. + */ + +/* LMS parameter set for the benchmark: L=1, H=5, W=8. This yields 2^5 = 32 + * one-time signatures, ~1.3 KB signatures, and a fast keygen. */ +#define WH_BENCH_LMS_LEVELS (1) +#define WH_BENCH_LMS_HEIGHT (5) +#define WH_BENCH_LMS_WINTERNITZ (8) + +/* Precomputed public key and signature for the VERIFY benchmark, generated once + * with wolfCrypt over WH_BENCH_LMS_MSG using the parameter set above. Embedding + * them lets verify run without keygen/sign, so it is available in a verify-only + * build. */ +#define WH_BENCH_LMS_MSG "Test message for LMS signing" +static const byte bench_lms_l1h5w8_pub[] = { + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x36, 0xa7, 0xe0, 0x89, 0x54, 0x18, 0xb6, 0x29, 0xd2, 0x22, 0x97, 0x29, + 0x3d, 0xb6, 0x5a, 0x2f, 0x1b, 0x7f, 0xf8, 0xe9, 0x63, 0x7b, 0xc1, 0xf6, + 0x46, 0x33, 0xd2, 0x70, 0x85, 0x5d, 0x5d, 0xc2, 0x27, 0xfc, 0x7e, 0x6b, + 0x1d, 0x26, 0xb8, 0xf5, 0x86, 0xb0, 0x54, 0xc0, 0xbb, 0xe8, 0xbb, 0xf6, +}; + +static const byte bench_lms_l1h5w8_sig[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x4a, 0xe9, 0x78, 0x79, 0xca, 0x8c, 0xfd, 0x1d, 0xee, 0x52, 0x0d, 0x71, + 0x46, 0xe3, 0x63, 0x22, 0x49, 0x7a, 0xa9, 0x12, 0xdb, 0x79, 0x49, 0x8d, + 0x22, 0x3c, 0xf6, 0x47, 0x7d, 0x68, 0xb2, 0x83, 0x24, 0x6e, 0x28, 0xd5, + 0x39, 0xfc, 0xcf, 0xee, 0xd5, 0x1e, 0xe0, 0x11, 0xfd, 0xb8, 0x4d, 0xf7, + 0xf6, 0xd7, 0xf6, 0x39, 0x0a, 0x98, 0x74, 0x70, 0x21, 0x4f, 0x27, 0x32, + 0xbd, 0x58, 0xb5, 0xea, 0x03, 0xce, 0x2c, 0x9e, 0x4c, 0x35, 0x4a, 0xca, + 0x1d, 0x3b, 0x5a, 0x5a, 0xe8, 0x4b, 0x6a, 0x31, 0x4c, 0xdb, 0xbe, 0x67, + 0x53, 0x0e, 0xc8, 0x40, 0xd4, 0xab, 0x03, 0xdb, 0xab, 0xd7, 0x77, 0x79, + 0xa7, 0x9a, 0x3e, 0x24, 0xe8, 0x55, 0x69, 0xe2, 0x02, 0x19, 0x56, 0x48, + 0xeb, 0x3d, 0x04, 0x47, 0x01, 0x22, 0x4d, 0x71, 0x6b, 0x2a, 0x42, 0xde, + 0x6b, 0x6e, 0xd4, 0x0e, 0x25, 0x5e, 0xb3, 0xab, 0x14, 0x96, 0x70, 0x3a, + 0xdc, 0x58, 0xb3, 0x6a, 0x84, 0x4a, 0xa2, 0x1c, 0x32, 0x37, 0xc4, 0x45, + 0x29, 0x16, 0xd4, 0x87, 0xfe, 0x01, 0x96, 0x5c, 0xd7, 0xad, 0xe7, 0xbb, + 0xf1, 0x0a, 0xbe, 0xc8, 0xaa, 0x7f, 0xba, 0x5e, 0xb6, 0xac, 0x43, 0x16, + 0x7c, 0x33, 0xdd, 0x0e, 0xfa, 0x30, 0xc8, 0xc1, 0xd2, 0x68, 0x1e, 0x48, + 0xe5, 0x53, 0x21, 0x63, 0x70, 0x07, 0xd3, 0x72, 0x11, 0x6f, 0xd2, 0x67, + 0x91, 0x81, 0x2d, 0xf8, 0xd7, 0x8f, 0x57, 0x74, 0x01, 0xa8, 0xb3, 0x7a, + 0x94, 0x11, 0x56, 0x9d, 0xe1, 0x2e, 0x52, 0xa7, 0x2a, 0x43, 0x52, 0xd5, + 0xb6, 0xf3, 0xc7, 0x44, 0xf1, 0xe3, 0xf4, 0x94, 0x5d, 0x21, 0x33, 0x70, + 0xc8, 0x0d, 0x31, 0xc5, 0xc1, 0x17, 0x5a, 0x1c, 0x3b, 0x47, 0x47, 0xce, + 0xe8, 0x04, 0x5e, 0x4d, 0xc1, 0x53, 0x13, 0xe7, 0xbb, 0xc4, 0x22, 0xc4, + 0xb6, 0x66, 0x2a, 0xe2, 0xf3, 0xf8, 0x12, 0x8a, 0x39, 0xd2, 0x5e, 0x74, + 0xc1, 0x00, 0x31, 0xf5, 0xc4, 0x3a, 0xe3, 0x92, 0xdd, 0x88, 0x35, 0x78, + 0x1c, 0x98, 0xc7, 0x4c, 0x0c, 0xdb, 0x3d, 0xb9, 0x99, 0x42, 0x4e, 0xc7, + 0x9a, 0x62, 0xe3, 0xa1, 0x83, 0xc3, 0x2a, 0xe4, 0x00, 0x35, 0x8a, 0x2a, + 0x2c, 0x91, 0x62, 0x89, 0xed, 0xb8, 0x45, 0x1d, 0x96, 0xd3, 0x9d, 0x56, + 0x63, 0xc1, 0xf9, 0xf9, 0x65, 0xb8, 0xf5, 0x2c, 0xbd, 0xb2, 0x21, 0xb0, + 0x22, 0x9f, 0xdf, 0x59, 0xdf, 0x97, 0x71, 0x11, 0xa8, 0xc9, 0x12, 0xfc, + 0xec, 0x66, 0xc7, 0xb8, 0x67, 0xc4, 0xc7, 0x42, 0x54, 0x7f, 0x4e, 0xe0, + 0x1e, 0xa3, 0xa1, 0x9d, 0xfb, 0x89, 0x72, 0x44, 0x55, 0x5a, 0x73, 0x91, + 0x6c, 0xd3, 0x0d, 0x16, 0x1a, 0x87, 0xe1, 0xac, 0xfe, 0xf8, 0xfd, 0x43, + 0x04, 0x8d, 0x91, 0x65, 0x8e, 0xda, 0x7e, 0xa5, 0xee, 0xbf, 0xfa, 0x1b, + 0x62, 0x4b, 0x09, 0x1c, 0x6c, 0xbf, 0x65, 0xaa, 0xc1, 0xad, 0x10, 0x46, + 0xf5, 0x67, 0xdf, 0x99, 0x53, 0x1a, 0xca, 0x22, 0x25, 0xd5, 0xb1, 0xda, + 0xec, 0x34, 0xf4, 0xea, 0x6d, 0x20, 0x04, 0xa6, 0xbb, 0xdb, 0xf4, 0x49, + 0xbe, 0x66, 0x74, 0x7f, 0x31, 0x7d, 0x94, 0xa1, 0x29, 0x59, 0x5d, 0x3b, + 0x0a, 0xc0, 0x56, 0xa6, 0x7a, 0xde, 0xd1, 0x07, 0x16, 0x1b, 0x06, 0x78, + 0xd4, 0xb6, 0xfb, 0x10, 0x36, 0xfd, 0x5e, 0x50, 0xae, 0xdb, 0x7f, 0xb6, + 0xa4, 0x92, 0xaa, 0x2d, 0x21, 0x61, 0x5f, 0x68, 0xd7, 0xe5, 0xa0, 0x09, + 0xa4, 0xeb, 0x55, 0xb7, 0x8d, 0xb5, 0xe1, 0x3d, 0xcd, 0x0c, 0x8c, 0x4c, + 0xea, 0xf8, 0xdf, 0x49, 0x29, 0xcb, 0x56, 0xa7, 0x83, 0xbb, 0x7c, 0x46, + 0x55, 0x70, 0x71, 0x07, 0x34, 0xde, 0x78, 0xdb, 0xa4, 0xc9, 0x66, 0x91, + 0x6f, 0xcc, 0x71, 0x50, 0x32, 0xd2, 0xb5, 0xa3, 0x11, 0x5f, 0x0f, 0x86, + 0xb5, 0x73, 0x9b, 0xd9, 0x2b, 0x00, 0xd9, 0x42, 0x64, 0x2f, 0xb9, 0x1a, + 0x85, 0x0c, 0xa8, 0xd8, 0xa3, 0x83, 0xfc, 0x23, 0x6b, 0xc2, 0xe8, 0x03, + 0xc1, 0x23, 0xd5, 0xdf, 0x0c, 0x1d, 0xcf, 0x39, 0x90, 0x06, 0x9f, 0x02, + 0x8d, 0x91, 0xe0, 0x1f, 0x4f, 0xf3, 0x37, 0x8c, 0x0c, 0x59, 0xe5, 0x98, + 0x66, 0x68, 0x49, 0xe8, 0x02, 0x2d, 0x73, 0x8c, 0xc5, 0xf7, 0xde, 0xb3, + 0xd7, 0x25, 0xd0, 0xc1, 0x92, 0x21, 0x33, 0x27, 0x26, 0xf1, 0xf0, 0xc1, + 0xe1, 0x0a, 0x9a, 0xff, 0x3d, 0x9f, 0x37, 0xdc, 0x68, 0x94, 0xe0, 0xc5, + 0x7c, 0x6a, 0xbf, 0xcd, 0xbb, 0x25, 0xd8, 0xf8, 0xeb, 0xf0, 0x15, 0x00, + 0x48, 0x52, 0xaa, 0xe4, 0xce, 0x6a, 0xd7, 0xf2, 0x94, 0x5b, 0x9b, 0xa4, + 0x45, 0x2f, 0x73, 0x50, 0x5c, 0x38, 0xea, 0x1d, 0x85, 0xb9, 0x51, 0x53, + 0x73, 0x59, 0x48, 0x93, 0x30, 0x9f, 0xd0, 0x98, 0x5b, 0xb7, 0x25, 0x27, + 0x54, 0x41, 0x1c, 0xa4, 0xfb, 0xbd, 0xfc, 0x42, 0x57, 0x41, 0x40, 0xff, + 0x2e, 0xa6, 0x06, 0x26, 0x0b, 0x95, 0x06, 0x4c, 0x60, 0xd4, 0x01, 0x3e, + 0xcd, 0x30, 0xac, 0x27, 0x4c, 0xd1, 0x72, 0x79, 0x02, 0x0e, 0x44, 0x82, + 0x81, 0x61, 0xc2, 0xed, 0xd7, 0xc1, 0x1e, 0x93, 0x7e, 0xc1, 0xb2, 0x07, + 0xad, 0x98, 0xd0, 0x12, 0x83, 0x88, 0x3e, 0x73, 0xd9, 0xf0, 0xd1, 0x4e, + 0x41, 0x6f, 0xd5, 0x38, 0x37, 0x56, 0xfa, 0x89, 0xdf, 0x7e, 0x91, 0xd6, + 0x19, 0x29, 0x12, 0xe5, 0x09, 0xa7, 0x6d, 0x37, 0x82, 0xa7, 0xe2, 0x25, + 0x2a, 0x1d, 0x13, 0xa2, 0xdf, 0x33, 0x12, 0xc2, 0x87, 0x59, 0x5d, 0xf7, + 0x9a, 0x22, 0x55, 0xb9, 0xdc, 0xc6, 0x53, 0x69, 0xb8, 0xe0, 0x9b, 0xaf, + 0xb4, 0x08, 0x85, 0x44, 0x66, 0xd6, 0x4d, 0x55, 0xaf, 0x89, 0x10, 0x95, + 0xf4, 0x6c, 0x07, 0x86, 0x6c, 0x10, 0x33, 0x31, 0xea, 0x96, 0x6c, 0x15, + 0x11, 0xdf, 0xdd, 0xc6, 0x5f, 0x3a, 0x28, 0x21, 0xb4, 0xf5, 0xfb, 0x38, + 0x0e, 0x79, 0x6f, 0x47, 0xfe, 0xaa, 0x6c, 0x91, 0x17, 0xdb, 0xbb, 0x6c, + 0x67, 0x20, 0x70, 0x79, 0x19, 0x20, 0x80, 0x53, 0x18, 0xef, 0xd7, 0x4f, + 0x02, 0x9b, 0x6a, 0x80, 0x20, 0xcb, 0xd9, 0x1f, 0x54, 0xd4, 0xbe, 0x8d, + 0xa7, 0x6c, 0x51, 0x8f, 0xcd, 0x82, 0xce, 0xe9, 0x40, 0x82, 0x62, 0x16, + 0xad, 0x55, 0x5f, 0x67, 0x96, 0xa9, 0xe9, 0x5c, 0xad, 0xe0, 0x26, 0x80, + 0xb7, 0x44, 0xcb, 0x09, 0x55, 0xc8, 0x1a, 0x17, 0x73, 0xab, 0xbf, 0x93, + 0x96, 0x92, 0x03, 0xed, 0x66, 0x2a, 0xfb, 0x96, 0x2f, 0x2e, 0x05, 0xbe, + 0xe7, 0xf2, 0xd7, 0xca, 0x3f, 0x5c, 0x77, 0x1f, 0xa0, 0xa8, 0x86, 0xed, + 0xd8, 0xa5, 0xb9, 0xe4, 0xdf, 0x65, 0xc8, 0x15, 0x01, 0x02, 0xac, 0xd9, + 0x57, 0x82, 0x7a, 0x62, 0x4d, 0x83, 0x72, 0x4e, 0xd7, 0x84, 0xbc, 0x5c, + 0x20, 0x9e, 0x74, 0x35, 0x6e, 0x97, 0x77, 0xf6, 0x61, 0x80, 0x87, 0x59, + 0x13, 0x04, 0x5f, 0x70, 0xe3, 0x76, 0xcb, 0xbb, 0xa4, 0xa3, 0x16, 0x0e, + 0xeb, 0x88, 0xc3, 0x93, 0xd1, 0x22, 0xde, 0xcf, 0xf6, 0xe2, 0xcb, 0xa5, + 0x93, 0xf4, 0x1f, 0x96, 0x91, 0x4a, 0x50, 0x71, 0x0f, 0x98, 0x33, 0xf6, + 0x28, 0xa5, 0x68, 0x7a, 0xac, 0x6e, 0x91, 0xfb, 0xf7, 0x7e, 0xe7, 0xdb, + 0xec, 0x0c, 0x4f, 0xba, 0x17, 0x35, 0x40, 0x77, 0xdc, 0x90, 0xf9, 0xd4, + 0x97, 0x44, 0x61, 0xd7, 0x4b, 0x5c, 0x28, 0xe8, 0x88, 0x2d, 0xca, 0x6d, + 0xbb, 0xf9, 0xc4, 0xce, 0x47, 0xdc, 0x6e, 0xad, 0x9d, 0xe2, 0x80, 0x3d, + 0x1f, 0x60, 0xd0, 0xf0, 0x5d, 0x45, 0x35, 0x94, 0x9a, 0xc2, 0x38, 0x3c, + 0x6a, 0x28, 0xf0, 0xff, 0xaf, 0x41, 0xcd, 0xf0, 0x17, 0x86, 0x96, 0x92, + 0x4e, 0x1e, 0xe9, 0x01, 0x4c, 0x6e, 0x56, 0xee, 0x30, 0xe3, 0x37, 0xc2, + 0x47, 0xfd, 0xb0, 0x7c, 0xf5, 0xbf, 0x3e, 0x31, 0xe2, 0xa1, 0x13, 0x72, + 0x66, 0x56, 0x3a, 0x37, 0xcf, 0x98, 0x2c, 0xe3, 0x87, 0x07, 0xe4, 0x55, + 0xb7, 0x36, 0xd3, 0xa6, 0x36, 0xc4, 0xd7, 0x27, 0x24, 0x1b, 0x3a, 0xf4, + 0x15, 0xc8, 0x2e, 0xd0, 0x06, 0x98, 0x60, 0xca, 0xc6, 0xc2, 0xe2, 0x92, + 0x16, 0xf0, 0xe1, 0x58, 0x36, 0x07, 0x03, 0xe4, 0x49, 0xbe, 0xf5, 0x48, + 0x76, 0x45, 0xce, 0x1f, 0xe5, 0xac, 0xdb, 0xf1, 0x10, 0x70, 0xf5, 0xca, + 0xc5, 0x3b, 0xe5, 0xd2, 0x00, 0x00, 0x00, 0x05, 0xc0, 0x93, 0xf0, 0xed, + 0xa3, 0x07, 0x74, 0x8a, 0xcf, 0x08, 0xed, 0x66, 0x76, 0x66, 0xab, 0x64, + 0xae, 0x8c, 0x7f, 0x9c, 0xe8, 0x0f, 0x47, 0x45, 0xd9, 0xb9, 0x36, 0xf1, + 0x46, 0x17, 0xb2, 0xc4, 0x0b, 0x70, 0x5e, 0x1a, 0x31, 0x93, 0x1c, 0x13, + 0x6e, 0x43, 0x9e, 0x65, 0x2c, 0xe9, 0x52, 0xe7, 0x35, 0x9d, 0x65, 0x54, + 0xdd, 0x3c, 0xcb, 0xb7, 0xbd, 0x3c, 0x70, 0xb0, 0x34, 0xa6, 0xd7, 0x7a, + 0xbb, 0x74, 0xfa, 0xf0, 0x86, 0x59, 0xd5, 0x8f, 0x61, 0x07, 0x46, 0xf8, + 0xa7, 0x94, 0xa5, 0x85, 0xe5, 0x0f, 0xd2, 0x81, 0x0a, 0xb7, 0x9a, 0x3a, + 0x7a, 0xb1, 0x72, 0x12, 0x7c, 0xdc, 0x36, 0x94, 0x85, 0xe9, 0x5c, 0x93, + 0x63, 0xec, 0xe5, 0xa2, 0x5f, 0x77, 0x0e, 0x1f, 0xaf, 0x8a, 0xa1, 0x62, + 0xf7, 0xb9, 0x6e, 0xea, 0xf5, 0x90, 0x59, 0x5a, 0x9f, 0x32, 0xa0, 0x7d, + 0xa5, 0x9a, 0x6a, 0x87, 0xdf, 0x48, 0x8c, 0xb0, 0x5f, 0xbb, 0x27, 0xc0, + 0x57, 0xd5, 0xa8, 0x17, 0xfa, 0x40, 0x69, 0x59, 0x70, 0x01, 0x77, 0x2f, + 0x98, 0xd2, 0x8b, 0xce, 0xc2, 0xb7, 0x74, 0x24, 0xd0, 0xe3, 0x04, 0xc6, +}; + + +/* Helper function for LMS verify benchmark. Loads the precomputed public key + * into the HSM cache (ephemeral, verify-only) and times wc_LmsKey_Verify over + * the fixed signature. No keygen/sign, so this runs in verify-only builds. */ +static int _benchLmsVerify(whClientContext* client, whBenchOpContext* ctx, + int id) +{ + int ret = 0; + LmsKey key; + const byte msg[] = WH_BENCH_LMS_MSG; + const word32 msgSz = (word32)(sizeof(msg) - 1); + int i; + int initialized_key = 0; + whKeyId keyId = WH_KEYID_ERASED; + char keyLabel[] = "bench-lms-verify-key"; + + (void)wh_Client_SetDmaMode(client, 1); + + /* Initialize the LMS key */ + ret = wc_LmsKey_Init(&key, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_Init %d\n", ret); + goto exit; + } + initialized_key = 1; + + /* Bind the parameter set (required before importing the public key) */ + ret = wc_LmsKey_SetParameters(&key, WH_BENCH_LMS_LEVELS, + WH_BENCH_LMS_HEIGHT, WH_BENCH_LMS_WINTERNITZ); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_SetParameters %d\n", ret); + goto exit; + } + + /* Load the precomputed public key into the wolfCrypt structure */ + ret = wc_LmsKey_ImportPubRaw(&key, bench_lms_l1h5w8_pub, + (word32)sizeof(bench_lms_l1h5w8_pub)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_ImportPubRaw %d\n", ret); + goto exit; + } + + /* Provision the verify-only public key into the HSM cache. EPHEMERAL keeps + * it out of NVM; it is evicted during cleanup. */ + ret = wh_Client_LmsImportPubKey(client, &key, &keyId, WH_NVM_FLAGS_EPHEMERAL, + (uint16_t)strlen(keyLabel), + (uint8_t*)keyLabel); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_LmsImportPubKey %d\n", ret); + goto exit; + } + + /* Set key ID */ + ret = wh_Client_LmsSetKeyId(&key, keyId); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_LmsSetKeyId %d\n", ret); + goto exit; + } + + /* Benchmark the verification operation over the fixed valid signature */ + for (i = 0; i < WOLFHSM_CFG_BENCH_PK_ITERS && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + + /* Time only the verify operation */ + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wc_LmsKey_Verify(&key, bench_lms_l1h5w8_sig, + (word32)sizeof(bench_lms_l1h5w8_sig), msg, + (int)msgSz); + benchStopRet = wh_Bench_StopOp(ctx, id); + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_Verify %d\n", ret); + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + } + +exit: + /* Clean up resources */ + if (initialized_key) { + wc_LmsKey_Free(&key); + } + + /* Evict the cached verify-only public key */ + if (!WH_KEYID_ISERASED(keyId)) { + int evictRet = wh_Client_KeyEvict(client, keyId); + if ((evictRet != 0) && (ret == 0)) { + ret = evictRet; + } + } + + return ret; +} + +#if !defined(WOLFSSL_LMS_VERIFY_ONLY) + +/* Signature buffer sized generously for L1_H5_W8 (~1328 bytes). File scope to + * keep it off the stack; benchmark modules run sequentially, so a single shared + * buffer is safe. */ +static byte g_lmsSigBuf[2048]; + +/* Helper function for LMS key generation benchmark */ +static int _benchLmsKeyGen(whClientContext* client, whBenchOpContext* ctx, + int id) +{ + int ret = 0; + LmsKey key; + WC_RNG rng[1] = {0}; + int i; + int initialized_rng = 0; + int initialized_key = 0; + whKeyId keyId = WH_KEYID_ERASED; + + (void)wh_Client_SetDmaMode(client, 1); + + /* Initialize the RNG */ + ret = wc_InitRng_ex(rng, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_InitRng_ex %d\n", ret); + return ret; + } + initialized_rng = 1; + + /* Benchmark the key generation */ + for (i = 0; i < WOLFHSM_CFG_BENCH_KG_ITERS && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + int keyIdRet = 0; + + /* Initialize the LMS key before each iteration */ + ret = wc_LmsKey_Init(&key, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_Init %d\n", ret); + break; + } + initialized_key = 1; + + /* Bind the LMS parameter set */ + ret = wc_LmsKey_SetParameters(&key, WH_BENCH_LMS_LEVELS, + WH_BENCH_LMS_HEIGHT, + WH_BENCH_LMS_WINTERNITZ); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_SetParameters %d\n", ret); + break; + } + + /* Time only the key generation. Keygen commits the private key to NVM + * on the server, so it is erased afterwards outside the timed region. */ + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wc_LmsKey_MakeKey(&key, rng); + benchStopRet = wh_Bench_StopOp(ctx, id); + + /* Capture the keyId as soon as the key exists so cleanup can erase the + * committed key even if a check below fails. The getter only reads a + * field, but its result gates the NVM cleanup, so check it too. */ + if (ret == 0) { + keyIdRet = wh_Client_LmsGetKeyId(&key, &keyId); + } + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_MakeKey %d\n", ret); + break; + } + if (keyIdRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_LmsGetKeyId %d\n", keyIdRet); + ret = keyIdRet; + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + + /* Erase the committed key from cache + NVM before the next iteration */ + if (!WH_KEYID_ISERASED(keyId)) { + int eraseRet = wh_Client_KeyErase(client, keyId); + if (eraseRet != 0) { + WH_BENCH_PRINTF("Failed to erase key %d\n", eraseRet); + ret = eraseRet; + break; + } + keyId = WH_KEYID_ERASED; + } + + /* Free the key after each iteration */ + wc_LmsKey_Free(&key); + initialized_key = 0; + } + + /* Clean up resources */ + if (initialized_key) { + wc_LmsKey_Free(&key); + } + if (initialized_rng) { + wc_FreeRng(rng); + } + + /* Erase a key left committed by a mid-iteration failure */ + if (!WH_KEYID_ISERASED(keyId)) { + int eraseRet = wh_Client_KeyErase(client, keyId); + if ((eraseRet != 0) && (ret == 0)) { + ret = eraseRet; + } + } + + return ret; +} + +/* Helper function for LMS sign benchmark */ +static int _benchLmsSign(whClientContext* client, whBenchOpContext* ctx, int id) +{ + int ret = 0; + LmsKey key; + WC_RNG rng[1] = {0}; + const byte msg[] = WH_BENCH_LMS_MSG; + const word32 msgSz = (word32)(sizeof(msg) - 1); + word32 sigSz = 0; + int i; + int iters = WOLFHSM_CFG_BENCH_PK_ITERS; + int initialized_rng = 0; + int initialized_key = 0; + whKeyId keyId = WH_KEYID_ERASED; + + (void)wh_Client_SetDmaMode(client, 1); + + /* Initialize the RNG */ + ret = wc_InitRng_ex(rng, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_InitRng_ex %d\n", ret); + goto exit; + } + initialized_rng = 1; + + /* Initialize the LMS key */ + ret = wc_LmsKey_Init(&key, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_Init %d\n", ret); + goto exit; + } + initialized_key = 1; + + /* Bind the LMS parameter set */ + ret = wc_LmsKey_SetParameters(&key, WH_BENCH_LMS_LEVELS, + WH_BENCH_LMS_HEIGHT, WH_BENCH_LMS_WINTERNITZ); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_SetParameters %d\n", ret); + goto exit; + } + + /* Determine the signature length and confirm it fits the buffer */ + ret = wc_LmsKey_GetSigLen(&key, &sigSz); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_GetSigLen %d\n", ret); + goto exit; + } + if (sigSz > sizeof(g_lmsSigBuf)) { + WH_BENCH_PRINTF("LMS sig buffer too small: need=%u have=%u\n", + (unsigned)sigSz, (unsigned)sizeof(g_lmsSigBuf)); + ret = BUFFER_E; + goto exit; + } + + /* Generate the signing key once (untimed). Keygen commits the key to NVM + * on the server, so it is erased during cleanup. */ + ret = wc_LmsKey_MakeKey(&key, rng); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_MakeKey %d\n", ret); + goto exit; + } + /* Capture the committed key's id so cleanup can erase it. Its result gates + * NVM cleanup, so surface a failure rather than leaking the key. */ + ret = wh_Client_LmsGetKeyId(&key, &keyId); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_LmsGetKeyId %d\n", ret); + goto exit; + } + + /* A fresh key must have signatures available */ + if (wc_LmsKey_SigsLeft(&key) == 0) { + WH_BENCH_PRINTF("LMS reported exhausted on fresh key\n"); + ret = WH_ERROR_ABORTED; + goto exit; + } + + /* LMS is stateful: the key holds 2^H one-time signatures. Cap the loop to + * that budget so raising the global WOLFHSM_CFG_BENCH_PK_ITERS (shared with + * the other PK benchmarks) does not exhaust the key mid-run. */ + if (iters > (1 << WH_BENCH_LMS_HEIGHT)) { + WH_BENCH_PRINTF("LMS sign capped to %d iterations (2^H OTS budget)\n", + (1 << WH_BENCH_LMS_HEIGHT)); + iters = (1 << WH_BENCH_LMS_HEIGHT); + } + + /* Benchmark the signing operation. Each sign advances the private state and + * commits it to NVM on the server before returning the signature. */ + for (i = 0; i < iters && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + word32 localSigSz = sigSz; + + /* Time only the sign operation */ + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wc_LmsKey_Sign(&key, g_lmsSigBuf, &localSigSz, msg, (int)msgSz); + benchStopRet = wh_Bench_StopOp(ctx, id); + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_LmsKey_Sign %d\n", ret); + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + } + +exit: + /* Clean up resources */ + if (initialized_key) { + wc_LmsKey_Free(&key); + } + if (initialized_rng) { + wc_FreeRng(rng); + } + + /* Erase the committed key from cache + NVM */ + if (!WH_KEYID_ISERASED(keyId)) { + int eraseRet = wh_Client_KeyErase(client, keyId); + if ((eraseRet != 0) && (ret == 0)) { + ret = eraseRet; + } + } + + return ret; +} + +#endif /* !WOLFSSL_LMS_VERIFY_ONLY */ + +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_LMS */ + +/* Public benchmark module functions for LMS */ +int wh_Bench_Mod_LmsKeyGen(whClientContext* client, whBenchOpContext* ctx, + int id, void* params) +{ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) && \ + !defined(WOLFSSL_LMS_VERIFY_ONLY) + (void)params; + return _benchLmsKeyGen(client, ctx, id); +#else + (void)client; + (void)ctx; + (void)id; + (void)params; + return WH_ERROR_NOTIMPL; +#endif +} + +int wh_Bench_Mod_LmsSign(whClientContext* client, whBenchOpContext* ctx, int id, + void* params) +{ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) && \ + !defined(WOLFSSL_LMS_VERIFY_ONLY) + (void)params; + return _benchLmsSign(client, ctx, id); +#else + (void)client; + (void)ctx; + (void)id; + (void)params; + return WH_ERROR_NOTIMPL; +#endif +} + +int wh_Bench_Mod_LmsVerify(whClientContext* client, whBenchOpContext* ctx, + int id, void* params) +{ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) + (void)params; + return _benchLmsVerify(client, ctx, id); +#else + (void)client; + (void)ctx; + (void)id; + (void)params; + return WH_ERROR_NOTIMPL; +#endif +} + +#endif /* !WOLFHSM_CFG_NO_CRYPTO && WOLFHSM_CFG_BENCH_ENABLE */ diff --git a/benchmark/bench_modules/wh_bench_mod_xmss.c b/benchmark/bench_modules/wh_bench_mod_xmss.c new file mode 100644 index 000000000..eb61b07d3 --- /dev/null +++ b/benchmark/bench_modules/wh_bench_mod_xmss.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +#include "wh_bench_mod.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_client_crypto.h" + + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLFHSM_CFG_BENCH_ENABLE) + +/* XMSS is stateful (hash-based) and is only exposed by wolfHSM over DMA. KEY-GEN + * and SIGN need a full (signing) build. VERIFY additionally works in a + * verify-only build: it runs against a precomputed public key and signature + * (see the vectors below) instead of generating them. The verify path is + * therefore guarded only by WOLFSSL_HAVE_XMSS, while keygen/sign additionally + * require !WOLFSSL_XMSS_VERIFY_ONLY. */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) + +#include +#include "wolfssl/wolfcrypt/random.h" +#include "wolfssl/wolfcrypt/wc_xmss.h" + +/* + * Timing characteristics of these benchmarks + * ------------------------------------------ + * XMSS is a stateful hash-based signature scheme: each signature consumes one + * one-time key and advances the private key state. The operations behave very + * differently under timing: + * + * - VERIFY is state-independent. It recomputes the WOTS+ public key candidate + * and walks the H authentication-path nodes to the root, a fixed amount of + * work regardless of the signature index. Its per-op time is flat. It runs + * against a fixed precomputed (public key, signature) pair, so - unlike + * keygen/sign - it is also available in a verify-only build, the config most + * likely deployed on a verifying device in the field. + * + * - SIGN is state-dependent, and - unlike LMS - fast relative to KEY-GEN. The + * server persists the full BDS traversal state, so wc_XmssKey_Reload (see + * _HandleXmssSignDma in wh_server_crypto.c) restores it cheaply rather than + * rebuilding the tree. Each sign then advances the BDS traversal to prepare + * the next auth path, and that cost is not uniform across indices: it spikes + * when treehash instances complete at subtree boundaries. Measured over + * consecutive indices the per-sign latency varies by several times, while + * KEY-GEN (which builds the whole tree once) is far slower than either. + * + * - XMSS has no "sign smoothing" option in wolfCrypt. The BDS worst-case- + * reduction parameter k (params->bds_k) is fixed to 0 for every parameter set + * (WC_XMSS_MAX_BDS_K == 0 in wc_xmss.h), so there is no build-time way to + * flatten the per-signature cost. Signing therefore stays inherently + * state-dependent. + * + * Benchmarking caveats: + * - SIGN only exercises the first WOLFHSM_CFG_BENCH_PK_ITERS signature indices, + * so the reported average undersamples the more expensive authentication-path + * refreshes that occur deeper in the key's lifetime. + * - KEY-GEN rebuilds the full 2^10 (1024-leaf) Merkle tree on every iteration, + * so it runs WOLFHSM_CFG_BENCH_KG_ITERS full tree builds (~seconds each) and + * dominates the wall-clock of a full benchmark run - by far the slowest module + * in the suite. Lower WOLFHSM_CFG_BENCH_KG_ITERS to shorten it. + */ + +/* "XMSS-SHA2_10_256" is the smallest standardized XMSS parameter set (height + * 10, 2^10 = 1024 signatures, pubLen 68, sigLen 2500). */ +#define WH_BENCH_XMSS_PARAM_STR "XMSS-SHA2_10_256" +#define WH_BENCH_XMSS_HEIGHT (10) + +/* Precomputed public key and signature for the VERIFY benchmark, generated once + * with wolfCrypt over WH_BENCH_XMSS_MSG using the parameter set above. Embedding + * them lets verify run without keygen/sign, so it is available in a verify-only + * build. */ +#define WH_BENCH_XMSS_MSG "Test message for XMSS signing" +static const byte bench_xmss_sha2_10_256_pub[] = { + 0x00, 0x00, 0x00, 0x01, 0xd9, 0xc1, 0xc8, 0xb6, 0xd1, 0x86, 0xc0, 0x05, + 0xf3, 0x4f, 0x74, 0x60, 0xe7, 0xfd, 0xe5, 0xf7, 0xb9, 0x34, 0x3f, 0x1d, + 0x9e, 0x8e, 0x07, 0xf6, 0x9b, 0xff, 0x44, 0xda, 0xf7, 0x2d, 0x2c, 0x4c, + 0xdd, 0x8d, 0xb5, 0x4c, 0xd8, 0x72, 0xe6, 0xa6, 0x8d, 0x54, 0x2f, 0xed, + 0x02, 0xb2, 0x0c, 0xcf, 0xa3, 0x4e, 0x3a, 0x8e, 0x96, 0xdd, 0x32, 0xf2, + 0x8d, 0x7c, 0x7e, 0x14, 0x87, 0x2a, 0x64, 0x46, +}; + +static const byte bench_xmss_sha2_10_256_sig[] = { + 0x00, 0x00, 0x00, 0x00, 0x09, 0x5b, 0x6d, 0x27, 0x4b, 0xd2, 0xe5, 0x24, + 0x5c, 0x5a, 0xf3, 0x50, 0x67, 0x97, 0xbc, 0x76, 0x0a, 0x3b, 0x3f, 0x61, + 0xf1, 0xd5, 0xac, 0x8c, 0x7e, 0x5e, 0x36, 0xad, 0x9c, 0x8d, 0xf6, 0x7d, + 0xb9, 0xa8, 0xeb, 0xd0, 0xf0, 0x38, 0x02, 0xc2, 0xe9, 0xd0, 0x5e, 0x05, + 0x35, 0xc2, 0xa5, 0x39, 0xa5, 0xb0, 0x22, 0x60, 0xba, 0x03, 0x39, 0x11, + 0x3a, 0xcd, 0x0a, 0x7a, 0xfb, 0x03, 0xcb, 0xe3, 0xf3, 0xdf, 0x3f, 0xae, + 0x68, 0x7c, 0xb1, 0xfa, 0xd9, 0x9b, 0xd8, 0x39, 0x34, 0x94, 0xa4, 0x3f, + 0x98, 0xf2, 0x27, 0xa7, 0xee, 0x72, 0x84, 0x00, 0x97, 0x52, 0x0a, 0xec, + 0x55, 0xf0, 0x25, 0x32, 0x0f, 0x0b, 0x7a, 0x8f, 0xef, 0x36, 0x6f, 0xbb, + 0xc6, 0x0f, 0xa9, 0x19, 0x2f, 0xfc, 0x8a, 0xd7, 0x5b, 0xaa, 0xbc, 0x01, + 0x1e, 0x41, 0x45, 0xe2, 0x9f, 0x79, 0xd2, 0x92, 0xbc, 0x86, 0x6f, 0x6a, + 0x68, 0x56, 0xa0, 0x9a, 0x4c, 0x4e, 0x65, 0x33, 0xe5, 0x61, 0x81, 0x91, + 0xfe, 0x50, 0x49, 0xea, 0xf1, 0x7f, 0x77, 0x8e, 0x25, 0x2c, 0x1c, 0x63, + 0x4c, 0xd7, 0xb4, 0xaf, 0x62, 0x80, 0xe3, 0x05, 0xb4, 0x88, 0xf5, 0x9e, + 0x2b, 0x4b, 0xea, 0xf5, 0xa9, 0xbe, 0x80, 0xf7, 0xa0, 0x46, 0xa2, 0xa4, + 0x39, 0xd6, 0xd5, 0x70, 0x1d, 0xb3, 0x55, 0x53, 0xba, 0x94, 0x8d, 0xa7, + 0x71, 0x3f, 0x41, 0x76, 0x67, 0x7f, 0x30, 0x8c, 0xd7, 0x76, 0x76, 0xba, + 0xcf, 0x60, 0xa0, 0x73, 0x25, 0xd5, 0x73, 0x65, 0xdc, 0x79, 0x67, 0xd8, + 0x53, 0x93, 0x55, 0x3f, 0xd2, 0x61, 0x6e, 0x00, 0x17, 0x5a, 0x8e, 0x91, + 0xc0, 0x76, 0xcc, 0xea, 0xfc, 0x4c, 0xd6, 0x67, 0xe4, 0x8d, 0x34, 0x35, + 0x89, 0x8a, 0xc9, 0x6c, 0x4b, 0x16, 0x7c, 0x04, 0xdf, 0x7e, 0x0e, 0x52, + 0x12, 0x50, 0xbd, 0x0d, 0x9b, 0x22, 0xbd, 0xe0, 0x53, 0xa6, 0xeb, 0x8e, + 0x2e, 0xbf, 0x1a, 0x69, 0x77, 0xe0, 0x2a, 0xd4, 0x0d, 0x51, 0x25, 0x6e, + 0xc6, 0x2d, 0x5a, 0xed, 0x21, 0x22, 0x81, 0x6c, 0x36, 0xe2, 0xbf, 0x89, + 0x9f, 0xce, 0xd8, 0x28, 0xcc, 0x1e, 0x9b, 0x68, 0x6a, 0x44, 0x08, 0xc4, + 0x8c, 0x0d, 0x48, 0x76, 0xa0, 0xca, 0xfa, 0x49, 0x0b, 0x77, 0x03, 0x35, + 0x9d, 0x25, 0xc4, 0x90, 0xc9, 0x7d, 0x7a, 0xff, 0xfa, 0x3b, 0xb6, 0xc8, + 0xc7, 0x01, 0xa3, 0x7a, 0x2d, 0xfb, 0x08, 0x0b, 0x59, 0xa7, 0xb3, 0xd8, + 0x7e, 0x21, 0x9d, 0xe8, 0xb1, 0xc3, 0x6d, 0x82, 0xba, 0xfe, 0xab, 0x72, + 0x3c, 0x5b, 0x6c, 0xe1, 0xa0, 0xf1, 0x32, 0xab, 0x61, 0x99, 0xb6, 0xcd, + 0x7a, 0x27, 0x7d, 0x99, 0xe4, 0x00, 0xbe, 0xdc, 0x7e, 0x51, 0x30, 0xa7, + 0xf7, 0xa1, 0xfa, 0xe3, 0x17, 0xc1, 0x4f, 0x6f, 0x7b, 0xce, 0x50, 0x5f, + 0xc2, 0xb9, 0x49, 0x66, 0xca, 0xe4, 0x91, 0x0f, 0xb1, 0xe1, 0x7c, 0x29, + 0x05, 0x17, 0xfe, 0xe3, 0x89, 0xfd, 0x00, 0xf4, 0x05, 0x08, 0xe9, 0xc0, + 0x30, 0x37, 0x1c, 0xa0, 0xcf, 0x1c, 0x2d, 0xe7, 0xb6, 0x24, 0x25, 0x39, + 0x14, 0xf3, 0xa9, 0xdf, 0xe4, 0x37, 0xb7, 0xd2, 0x6f, 0xd5, 0xee, 0x47, + 0x17, 0x6d, 0x1a, 0xb8, 0x98, 0xba, 0x8e, 0x3f, 0x45, 0xb0, 0x40, 0x35, + 0x94, 0x93, 0x08, 0x42, 0xb2, 0x4f, 0xff, 0x95, 0x6c, 0x1c, 0xba, 0xbe, + 0xa8, 0x24, 0x61, 0x51, 0x07, 0x07, 0xe4, 0x93, 0xd4, 0xce, 0x5a, 0x4a, + 0x3b, 0xa7, 0x1b, 0xe8, 0xb0, 0x07, 0x09, 0xc4, 0x9d, 0xd1, 0x8b, 0xcc, + 0xd8, 0x33, 0x00, 0x9c, 0x34, 0xbf, 0x71, 0x4f, 0x0f, 0xdc, 0x2d, 0xb3, + 0x45, 0xf2, 0x67, 0x70, 0x76, 0xe0, 0x07, 0xc1, 0xc1, 0x9f, 0xfd, 0x6e, + 0xba, 0xe2, 0xfe, 0x6f, 0x4f, 0x66, 0x93, 0x87, 0x84, 0x99, 0xd8, 0x08, + 0x62, 0xcc, 0x33, 0x75, 0x31, 0x56, 0xcc, 0x48, 0x7b, 0x42, 0xb4, 0xfb, + 0xcc, 0x48, 0xaf, 0x97, 0xe1, 0x58, 0x9e, 0x35, 0x65, 0xae, 0x47, 0xb1, + 0x6c, 0xcd, 0xb2, 0x9f, 0x94, 0xd3, 0x67, 0x14, 0xb6, 0x33, 0xe4, 0xc0, + 0x25, 0x62, 0x6f, 0x37, 0x0e, 0x57, 0x7b, 0x95, 0x34, 0xe0, 0x44, 0x4f, + 0x66, 0x25, 0x59, 0xc9, 0x62, 0xa1, 0x83, 0xcf, 0xf0, 0x4e, 0xbb, 0x1e, + 0x6c, 0x84, 0x7f, 0xa6, 0x57, 0x08, 0xa7, 0x98, 0x82, 0xb6, 0xb6, 0x19, + 0x3f, 0x1e, 0x5b, 0xfb, 0x70, 0x9a, 0x1f, 0x7a, 0xed, 0x83, 0xf9, 0x03, + 0x15, 0x2a, 0x15, 0x3e, 0x5c, 0xc1, 0x46, 0x1d, 0xcc, 0xa5, 0xf6, 0x7e, + 0x22, 0x24, 0xb3, 0x4e, 0xda, 0x18, 0x13, 0xe9, 0xf0, 0x97, 0x5d, 0x3c, + 0xda, 0x67, 0xec, 0x8d, 0x88, 0x01, 0xec, 0x76, 0x83, 0xc6, 0x1f, 0xe1, + 0x1e, 0x5a, 0x35, 0xf9, 0x39, 0x52, 0x8c, 0x0e, 0xc9, 0x19, 0x6c, 0x48, + 0x3d, 0x4e, 0x8b, 0xb5, 0xfa, 0x04, 0x0e, 0x3f, 0x26, 0x5e, 0x10, 0x04, + 0x62, 0xd1, 0x63, 0xd9, 0xf2, 0xa0, 0x32, 0x22, 0x17, 0x82, 0x28, 0x09, + 0xac, 0xe0, 0xd5, 0x07, 0xdb, 0x31, 0x22, 0xe4, 0x70, 0x8b, 0x7c, 0x62, + 0x05, 0x66, 0x75, 0x65, 0xfc, 0xa5, 0xcb, 0x21, 0xa6, 0x53, 0xca, 0x1a, + 0x16, 0x5f, 0x02, 0xd2, 0x72, 0xde, 0x10, 0x6d, 0xda, 0x16, 0x86, 0xfc, + 0x4e, 0x24, 0xa8, 0x03, 0x87, 0xd8, 0xf9, 0x65, 0xbf, 0xa8, 0x2a, 0x31, + 0xf1, 0xeb, 0xb4, 0xee, 0x4c, 0xb5, 0x00, 0xde, 0x60, 0x44, 0x2a, 0x58, + 0x92, 0x7b, 0x3c, 0x2c, 0xf1, 0x95, 0x37, 0xd6, 0x5e, 0x72, 0xd9, 0x6e, + 0xd8, 0x76, 0x18, 0x45, 0x3a, 0xbf, 0xde, 0xf2, 0x50, 0xaa, 0x94, 0x78, + 0xf8, 0xcb, 0x2c, 0xe3, 0xa3, 0x08, 0x08, 0xc4, 0x99, 0x6e, 0xaf, 0x7f, + 0x04, 0xa1, 0x0e, 0x31, 0xa1, 0x21, 0x12, 0x48, 0x82, 0xac, 0x5b, 0x10, + 0xc8, 0x31, 0x99, 0xb4, 0x65, 0xf8, 0x57, 0x55, 0x91, 0x79, 0x56, 0x12, + 0x96, 0xb0, 0xc5, 0x55, 0x55, 0xc5, 0xff, 0x90, 0xfd, 0x66, 0x25, 0x33, + 0x49, 0x15, 0xb2, 0x0d, 0xba, 0xc2, 0xf7, 0xc8, 0x57, 0x16, 0x86, 0xf7, + 0xa8, 0x7b, 0x43, 0x6c, 0xdc, 0x7e, 0xed, 0xf8, 0x7a, 0x7b, 0xda, 0x8d, + 0x70, 0xe7, 0x81, 0x7d, 0xec, 0x9d, 0xc5, 0x9c, 0xf9, 0x26, 0x84, 0x93, + 0xbc, 0x80, 0x06, 0x88, 0xe5, 0xc1, 0x4b, 0xc5, 0x54, 0x05, 0x9c, 0xc5, + 0xea, 0x5e, 0x1d, 0x24, 0x0d, 0x1d, 0xde, 0xc2, 0xdd, 0x5d, 0x8f, 0x61, + 0xe0, 0x39, 0x65, 0xd4, 0x13, 0x21, 0x21, 0x0e, 0x7e, 0x16, 0x54, 0xee, + 0x0c, 0x6b, 0xcb, 0xff, 0x28, 0x66, 0xa1, 0x63, 0xe5, 0x84, 0x1d, 0x0b, + 0xa6, 0x59, 0x94, 0x28, 0x50, 0x54, 0xc4, 0x92, 0xae, 0x18, 0xb0, 0x1c, + 0xf2, 0xc8, 0x12, 0xf5, 0x62, 0x46, 0x5e, 0xc8, 0xa6, 0x3f, 0x09, 0x89, + 0x02, 0x86, 0x1a, 0x3c, 0x84, 0x1b, 0x6e, 0xd3, 0x58, 0xd7, 0x41, 0xf8, + 0x3d, 0x75, 0x2a, 0x23, 0xcd, 0x33, 0xa0, 0x7d, 0xd9, 0x51, 0x5d, 0x5f, + 0xe0, 0xc2, 0x94, 0x4c, 0x23, 0x13, 0xc7, 0xb4, 0xc7, 0xad, 0x8c, 0x33, + 0x61, 0x0a, 0xbb, 0x99, 0xc0, 0x06, 0xe0, 0x13, 0x4e, 0x60, 0x65, 0xb5, + 0x2f, 0xdf, 0xbf, 0x03, 0x37, 0x65, 0xa8, 0x07, 0x0e, 0x62, 0x94, 0xdc, + 0x94, 0xfa, 0xe0, 0xe4, 0xe5, 0xf3, 0x2b, 0x92, 0x78, 0x84, 0x7f, 0x96, + 0x51, 0x6f, 0x65, 0xe9, 0x79, 0xc3, 0xbb, 0x81, 0xe3, 0xe6, 0x56, 0xeb, + 0x9d, 0x99, 0x1f, 0x29, 0x9c, 0x82, 0x37, 0x5a, 0x5a, 0xd6, 0x54, 0xce, + 0xca, 0x88, 0x6a, 0x46, 0x2d, 0x3d, 0x50, 0xbe, 0x99, 0x0e, 0x5d, 0x85, + 0xb1, 0x0b, 0xa5, 0x7b, 0x73, 0x05, 0xac, 0x0a, 0x51, 0x6a, 0xe9, 0x7b, + 0xb0, 0xed, 0x77, 0x14, 0xa6, 0xaa, 0xf4, 0xd0, 0xc9, 0x45, 0xb1, 0x9a, + 0x41, 0xf8, 0xb2, 0x0b, 0x58, 0xf1, 0xdc, 0x55, 0xa3, 0xa3, 0xeb, 0xd5, + 0x2d, 0x43, 0x1c, 0xae, 0xce, 0x78, 0xfe, 0x10, 0x81, 0xd2, 0xf0, 0x70, + 0x37, 0x94, 0xb5, 0x9d, 0x64, 0xad, 0xa0, 0x39, 0xf9, 0x45, 0xbc, 0xc4, + 0xc6, 0x8f, 0x79, 0x3b, 0x8c, 0xf9, 0x54, 0x3c, 0xbd, 0x5f, 0x3a, 0x88, + 0x74, 0x04, 0x10, 0x4c, 0x75, 0x42, 0x8e, 0x42, 0xf0, 0x85, 0x1c, 0xa9, + 0x45, 0xc6, 0x74, 0xd3, 0x25, 0x9c, 0x1d, 0xfa, 0x9a, 0x3d, 0x3c, 0xd8, + 0x82, 0x81, 0x01, 0xbf, 0xbc, 0x7e, 0xda, 0x08, 0x55, 0x21, 0xac, 0x6d, + 0xca, 0x1e, 0x2b, 0x98, 0xf9, 0xf7, 0xe3, 0xfc, 0x20, 0x8b, 0x61, 0xaf, + 0x2b, 0x6e, 0x4b, 0x98, 0xe5, 0x8d, 0x1d, 0x8e, 0x3e, 0xc3, 0xbb, 0x37, + 0xfe, 0x99, 0xcf, 0xdf, 0x3b, 0xc2, 0xb9, 0x44, 0x76, 0x26, 0x50, 0x63, + 0x17, 0x49, 0xbc, 0x48, 0xf0, 0x26, 0x09, 0x50, 0x33, 0x1a, 0x75, 0xb0, + 0xae, 0x2a, 0x19, 0x86, 0xcb, 0xf4, 0x69, 0x60, 0xb7, 0x01, 0xe5, 0x61, + 0x59, 0xa7, 0xa0, 0x3d, 0xfb, 0x4e, 0x28, 0x1f, 0x82, 0x31, 0x8a, 0xd5, + 0x4b, 0x2e, 0x78, 0x58, 0x0d, 0x7f, 0x72, 0xdd, 0x87, 0x7e, 0xfa, 0x07, + 0xcb, 0xac, 0x6e, 0xd4, 0x13, 0x13, 0x32, 0x92, 0x5c, 0x3b, 0xce, 0x25, + 0x2f, 0xed, 0x24, 0x92, 0x38, 0xc4, 0x86, 0x5e, 0xd8, 0x58, 0x3e, 0x29, + 0x34, 0x23, 0x8a, 0x9f, 0xe4, 0x33, 0x95, 0x36, 0x6d, 0xfe, 0x74, 0x81, + 0xd9, 0xa4, 0x88, 0x64, 0x50, 0x7d, 0x14, 0xb3, 0x4c, 0x61, 0x48, 0xac, + 0x2c, 0xd7, 0x81, 0x8d, 0x57, 0x16, 0xb8, 0xb0, 0x94, 0x84, 0xb2, 0x1f, + 0xe6, 0x38, 0x52, 0x89, 0xc1, 0xae, 0xf9, 0x7d, 0x28, 0x79, 0x2e, 0x05, + 0xa3, 0x52, 0xb5, 0x5b, 0xf8, 0xc9, 0xbe, 0xc2, 0xb9, 0xe1, 0x8b, 0xb2, + 0x77, 0xb2, 0xea, 0x20, 0xc7, 0x79, 0x28, 0x63, 0xea, 0x7f, 0x89, 0xcc, + 0x3c, 0xac, 0x6e, 0x05, 0x0c, 0xe1, 0xcf, 0x0c, 0xaf, 0x9a, 0xfc, 0x64, + 0x80, 0xff, 0x03, 0x8d, 0x02, 0xfe, 0x61, 0xa5, 0xaf, 0x17, 0xc7, 0x05, + 0x3c, 0x03, 0x4b, 0xa5, 0x0c, 0x5c, 0x9b, 0x20, 0x6a, 0x82, 0x5c, 0x91, + 0x09, 0xd0, 0x2a, 0xcc, 0x39, 0xca, 0x30, 0xef, 0x8c, 0x1b, 0x9b, 0xf3, + 0xc6, 0x29, 0xd5, 0xd9, 0xbf, 0xb7, 0xb7, 0x0c, 0x14, 0x80, 0x1b, 0xbd, + 0x3a, 0x0b, 0xb8, 0x33, 0x72, 0x41, 0x47, 0x51, 0x3a, 0xda, 0x00, 0xac, + 0xfe, 0x63, 0xad, 0xf1, 0x59, 0x50, 0xc5, 0xdf, 0x4e, 0x28, 0xed, 0x2c, + 0x11, 0x40, 0x39, 0xdd, 0x65, 0x70, 0x7f, 0x60, 0xf9, 0x6a, 0x4a, 0x3d, + 0xc0, 0xab, 0x28, 0xd4, 0xd8, 0x2f, 0xe9, 0x8b, 0x54, 0x8b, 0x4f, 0xef, + 0xc9, 0x01, 0x47, 0x63, 0x5e, 0x1b, 0x70, 0xcc, 0x64, 0x31, 0x77, 0x7d, + 0x3f, 0x4d, 0x83, 0xaf, 0x50, 0x37, 0x4d, 0xda, 0xc5, 0x6c, 0xe3, 0xdf, + 0xa5, 0xc6, 0xcc, 0x0b, 0x6c, 0x5c, 0xf9, 0x87, 0x1a, 0x18, 0xa9, 0xf3, + 0x15, 0x77, 0x0e, 0x95, 0x63, 0x77, 0x5b, 0x34, 0x0e, 0xee, 0x75, 0x99, + 0xd4, 0xc3, 0x3a, 0xe3, 0xf2, 0xaf, 0x28, 0x39, 0x40, 0x00, 0x0a, 0x9f, + 0x1e, 0x6f, 0xb0, 0x9a, 0x96, 0xeb, 0xf4, 0xf0, 0x5b, 0x75, 0x34, 0x3d, + 0xcf, 0xd6, 0x5b, 0xcb, 0x8e, 0x45, 0x46, 0x1e, 0x73, 0xb0, 0x5e, 0x7d, + 0xad, 0xf1, 0xff, 0x09, 0xb2, 0xc4, 0xb7, 0x8a, 0xd7, 0x8e, 0x87, 0xbf, + 0xc5, 0xcb, 0x56, 0xac, 0x70, 0x89, 0xe4, 0xe0, 0x90, 0x46, 0xd6, 0xbd, + 0x9b, 0x57, 0x7d, 0xeb, 0x05, 0x58, 0xf5, 0x02, 0x15, 0xc2, 0xa3, 0x4a, + 0x76, 0xec, 0x2c, 0x18, 0x88, 0x77, 0x1d, 0xb7, 0x2b, 0x6d, 0x51, 0xf0, + 0x62, 0x17, 0x29, 0x23, 0x87, 0xc5, 0xea, 0x96, 0xac, 0xa8, 0x84, 0x15, + 0x28, 0x0a, 0x14, 0x04, 0xc3, 0x69, 0x34, 0xd7, 0x9d, 0x8e, 0xe2, 0x62, + 0xf1, 0xe3, 0x74, 0x4e, 0x27, 0x87, 0xd5, 0x49, 0xd4, 0x63, 0x24, 0xd2, + 0xa6, 0xfe, 0xd5, 0xc5, 0x5b, 0x45, 0x80, 0xe7, 0xde, 0x63, 0xc7, 0x9f, + 0x81, 0x2b, 0xe9, 0xf3, 0xe0, 0x37, 0xa1, 0x1f, 0x10, 0x5c, 0xc3, 0x98, + 0xbf, 0x17, 0xcd, 0x04, 0x6b, 0x78, 0xd8, 0x02, 0x50, 0x5d, 0xe7, 0x17, + 0x63, 0x6a, 0xbf, 0x6b, 0xf8, 0x56, 0x56, 0xa9, 0x8e, 0xd8, 0x08, 0x00, + 0x73, 0x0d, 0xe3, 0xe5, 0x8d, 0x82, 0x22, 0xc7, 0xe6, 0xc9, 0x07, 0xff, + 0x21, 0x1d, 0x08, 0xc6, 0xc8, 0xa8, 0x6a, 0xb8, 0x71, 0x65, 0x37, 0x81, + 0xb1, 0x0b, 0x10, 0x9e, 0xc4, 0x81, 0xe0, 0x9a, 0x86, 0x2c, 0xcd, 0x59, + 0xff, 0xbf, 0x4c, 0xb7, 0x8f, 0x8e, 0x82, 0xd1, 0xf7, 0xc6, 0x87, 0x71, + 0x2e, 0x9d, 0xbf, 0x70, 0xf5, 0xa7, 0xa1, 0xd3, 0xd9, 0xd8, 0x23, 0x97, + 0xf8, 0xb1, 0xcc, 0x1d, 0xf8, 0xdc, 0x76, 0x86, 0xa5, 0xe5, 0x74, 0xa3, + 0x68, 0xc5, 0xd7, 0x61, 0x4d, 0x6f, 0xd9, 0x04, 0x5f, 0xbe, 0xfd, 0x90, + 0xa8, 0x83, 0x31, 0xc2, 0xf5, 0x40, 0xdf, 0xbe, 0x2f, 0x36, 0xa3, 0x1c, + 0xaf, 0xc3, 0xbf, 0x00, 0xd2, 0xe0, 0xa4, 0xda, 0xfa, 0x01, 0x11, 0x1b, + 0xc3, 0xc5, 0xd0, 0x5c, 0xba, 0x36, 0x64, 0xd6, 0x29, 0x60, 0x61, 0x8f, + 0x16, 0xc1, 0xc1, 0x0f, 0xc8, 0x4c, 0xdf, 0x39, 0x6c, 0x7a, 0xea, 0x5d, + 0x52, 0xd6, 0xb8, 0x50, 0x38, 0xd8, 0x9a, 0x95, 0x60, 0x9b, 0x78, 0x76, + 0xf0, 0xab, 0x44, 0x7b, 0x46, 0x0e, 0x74, 0x96, 0x2e, 0x2a, 0x11, 0xcd, + 0x03, 0x6f, 0x87, 0x81, 0x07, 0x74, 0xed, 0x36, 0x02, 0x28, 0x79, 0x99, + 0xfa, 0x49, 0xb1, 0x83, 0x29, 0xec, 0x1a, 0x38, 0xe3, 0xb4, 0x00, 0x58, + 0x79, 0xe0, 0x72, 0x39, 0x62, 0xe7, 0xce, 0x1e, 0xeb, 0x86, 0x28, 0xad, + 0x8f, 0x9a, 0xba, 0xee, 0x1d, 0xf0, 0xc0, 0x45, 0x75, 0x05, 0xba, 0xd4, + 0x81, 0xf5, 0x10, 0x2f, 0x77, 0x84, 0xb3, 0x67, 0x70, 0x7b, 0xc7, 0x84, + 0xff, 0xe3, 0xd5, 0xf8, 0x98, 0xe7, 0x6e, 0x21, 0x4e, 0x98, 0xd6, 0x4d, + 0xcf, 0xad, 0x3a, 0x4e, 0x7d, 0xa4, 0x0c, 0x5e, 0x38, 0xef, 0x41, 0xa2, + 0x05, 0xa8, 0xe6, 0xcf, 0xb5, 0xdc, 0x2b, 0xeb, 0xd6, 0x37, 0x05, 0x5a, + 0xf1, 0xdc, 0xc9, 0x14, 0x21, 0x33, 0x85, 0xd4, 0x2d, 0x50, 0x86, 0x3d, + 0x25, 0x88, 0x6e, 0x21, 0x67, 0xcf, 0x7b, 0xea, 0xd4, 0xa3, 0x37, 0xd1, + 0x77, 0xc2, 0x4b, 0xe9, 0x0f, 0xc3, 0x78, 0xff, 0x83, 0x9b, 0xbe, 0x42, + 0x82, 0xef, 0x7c, 0x79, 0xcd, 0x1d, 0x68, 0x95, 0xc9, 0xbc, 0x19, 0x22, + 0x2f, 0x9f, 0x6f, 0x29, 0xc6, 0xca, 0x85, 0x98, 0x8e, 0xe4, 0xa8, 0x11, + 0xc2, 0x8b, 0x73, 0x63, 0x8f, 0xae, 0xe2, 0x97, 0xf6, 0x38, 0xe1, 0x95, + 0xc5, 0xb8, 0xef, 0xee, 0x56, 0xf5, 0xd7, 0xe4, 0x82, 0x91, 0x0b, 0xc4, + 0x48, 0x8c, 0x74, 0xe2, 0x34, 0x6b, 0xc1, 0x6b, 0x70, 0xa6, 0x98, 0xcb, + 0x21, 0xc4, 0x45, 0xbf, 0x9a, 0x4f, 0xe5, 0x0f, 0x5a, 0x7a, 0x17, 0x91, + 0xee, 0xf9, 0x6d, 0xb1, 0x35, 0xc3, 0x4e, 0x82, 0xf5, 0xf4, 0x8c, 0xca, + 0x21, 0x4a, 0x37, 0x35, 0xae, 0xf2, 0x0f, 0x6d, 0x5d, 0xe5, 0x63, 0xe6, + 0x9a, 0x61, 0x80, 0x36, 0x54, 0x3f, 0x28, 0x97, 0x20, 0x72, 0x31, 0x14, + 0xc7, 0x47, 0x46, 0x49, 0x29, 0xb9, 0x80, 0x38, 0x72, 0xc3, 0x08, 0x0a, + 0x87, 0xc6, 0x64, 0x4b, 0xd6, 0x1a, 0xd0, 0xcb, 0xb6, 0x2c, 0x9b, 0xc5, + 0x4a, 0x4e, 0x1d, 0xc6, 0x71, 0x6e, 0xc2, 0x31, 0x8c, 0xa4, 0xdb, 0xbd, + 0x98, 0xe6, 0xb1, 0xfa, 0x8d, 0x64, 0x4e, 0xb9, 0x2d, 0x99, 0xff, 0x00, + 0xc5, 0xd0, 0x8d, 0x81, 0x45, 0xd7, 0x01, 0x24, 0x4a, 0xc2, 0x88, 0x4f, + 0x7a, 0xff, 0xef, 0x7a, 0x12, 0xe1, 0xfa, 0x43, 0x78, 0x0c, 0x92, 0x2e, + 0xba, 0xf8, 0xce, 0x32, 0x9c, 0xde, 0xb3, 0x08, 0xa6, 0xed, 0x6c, 0x51, + 0xa6, 0x32, 0x05, 0x0a, 0x6a, 0x1b, 0xd6, 0xc8, 0x16, 0x5e, 0x2b, 0x75, + 0xf4, 0x67, 0xd9, 0x9c, 0x0b, 0x7e, 0xb5, 0x90, 0x5d, 0xd8, 0xe8, 0x38, + 0xbf, 0x7c, 0xff, 0xd7, 0xab, 0xba, 0x66, 0x06, 0x9a, 0x87, 0xd6, 0xe6, + 0xa7, 0x78, 0x54, 0x93, 0xfe, 0xd6, 0x8f, 0x47, 0x0c, 0x82, 0x13, 0x1c, + 0x26, 0x49, 0x69, 0x70, 0x5d, 0x0f, 0xdd, 0x84, 0x6d, 0x55, 0x88, 0x2d, + 0x67, 0xbe, 0x2a, 0x11, 0x13, 0xb3, 0xdc, 0xa0, 0xa7, 0xce, 0x49, 0x4a, + 0x72, 0x58, 0xa3, 0xec, 0x7c, 0xa8, 0xa5, 0x98, 0xeb, 0x30, 0x0b, 0xd9, + 0x6a, 0xb0, 0xdc, 0x64, 0x44, 0xe5, 0x21, 0x96, 0xa4, 0xbb, 0xc4, 0x66, + 0x31, 0xba, 0xce, 0x2b, 0xa4, 0x76, 0x76, 0x5e, 0x84, 0x08, 0xc9, 0x2c, + 0xdc, 0x55, 0x8f, 0x1d, 0xb9, 0xdc, 0xa3, 0x4e, 0xb1, 0x1c, 0xba, 0x7d, + 0xa1, 0x0a, 0x40, 0xae, 0x29, 0xb0, 0x6b, 0xe6, 0xa8, 0xa7, 0x93, 0xc9, + 0x15, 0xb8, 0x53, 0x2f, 0x5f, 0xd7, 0xc7, 0x0b, 0x2d, 0x25, 0xdd, 0xd0, + 0x0c, 0x2d, 0xc0, 0x12, 0xfa, 0x5a, 0x05, 0xe0, 0xda, 0xfc, 0x42, 0x78, + 0x1d, 0xc7, 0xe5, 0x08, 0x10, 0xae, 0x51, 0xf0, 0xd3, 0x7f, 0xac, 0x90, + 0xa7, 0xc1, 0xdb, 0x3a, 0xd0, 0x7f, 0x0a, 0xd7, 0x89, 0x74, 0x9e, 0x0a, + 0x0d, 0xc0, 0x98, 0x08, 0x7c, 0x5e, 0x06, 0xf8, 0x5b, 0x3f, 0xb1, 0xf8, + 0xdb, 0xf3, 0xab, 0x91, 0x50, 0x33, 0xdd, 0x50, 0xc8, 0x3a, 0xd8, 0xb4, + 0x93, 0x44, 0x29, 0x5d, 0xf2, 0xd3, 0x97, 0x6e, 0xd5, 0xb5, 0x93, 0x2f, + 0xde, 0xa9, 0xcb, 0x8e, 0x0f, 0xe3, 0xd5, 0x5a, 0x00, 0x3a, 0x7b, 0xdb, + 0x79, 0xb2, 0x16, 0x48, 0x5c, 0x7f, 0xcf, 0x08, 0xac, 0xa8, 0x6c, 0x41, + 0x6e, 0x43, 0x85, 0x4c, 0x92, 0xb2, 0x93, 0x18, 0xd4, 0xda, 0xf2, 0x5e, + 0x73, 0xf8, 0xaf, 0x11, 0xe0, 0xf2, 0xf6, 0x98, 0x30, 0xea, 0x04, 0xc7, + 0x7c, 0x9f, 0xe5, 0x66, 0x18, 0x55, 0x41, 0x47, 0x0b, 0x90, 0xeb, 0xc7, + 0x59, 0xa0, 0xfe, 0x48, 0x1d, 0xf2, 0x90, 0x37, 0xac, 0xff, 0x42, 0xb3, + 0xab, 0xde, 0x11, 0x69, 0xa8, 0xaa, 0xeb, 0xc4, 0x4b, 0x80, 0x75, 0xd6, + 0x59, 0xad, 0x86, 0x4e, 0x44, 0xae, 0xda, 0xab, 0x2f, 0xc2, 0x36, 0x60, + 0x7d, 0xf1, 0x33, 0x27, 0xdc, 0x39, 0x9b, 0x97, 0x19, 0x1b, 0x71, 0x0e, + 0xca, 0x50, 0x2c, 0xd0, 0xa0, 0x4d, 0xe7, 0xbd, 0x02, 0xb7, 0x5a, 0x17, + 0x40, 0x22, 0x77, 0xaa, 0xc1, 0x8c, 0x2b, 0x0e, 0xf2, 0x6f, 0xcf, 0x95, + 0xf3, 0x21, 0xdd, 0x0f, 0x7e, 0x0f, 0x62, 0x9f, 0x9b, 0xb2, 0xb8, 0xad, + 0x82, 0x18, 0x42, 0x47, 0x73, 0x7b, 0x16, 0xda, 0xbb, 0x86, 0xd5, 0x1c, + 0xd7, 0xcf, 0x74, 0x43, 0xed, 0x87, 0xa1, 0xf0, 0x3a, 0xe7, 0x24, 0x16, + 0xfa, 0x53, 0xf6, 0xc7, +}; + + +/* Helper function for XMSS verify benchmark. Loads the precomputed public key + * into the HSM cache (ephemeral, verify-only) and times wc_XmssKey_Verify over + * the fixed signature. No keygen/sign, so this runs in verify-only builds. */ +static int _benchXmssVerify(whClientContext* client, whBenchOpContext* ctx, + int id) +{ + int ret = 0; + XmssKey key; + const byte msg[] = WH_BENCH_XMSS_MSG; + const word32 msgSz = (word32)(sizeof(msg) - 1); + int i; + int initialized_key = 0; + whKeyId keyId = WH_KEYID_ERASED; + char keyLabel[] = "bench-xmss-verify-key"; + + (void)wh_Client_SetDmaMode(client, 1); + + /* Initialize the XMSS key */ + ret = wc_XmssKey_Init(&key, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_Init %d\n", ret); + goto exit; + } + initialized_key = 1; + + /* Bind the parameter string (required before importing the public key) */ + ret = wc_XmssKey_SetParamStr(&key, WH_BENCH_XMSS_PARAM_STR); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_SetParamStr %d\n", ret); + goto exit; + } + + /* Load the precomputed public key into the wolfCrypt structure */ + ret = wc_XmssKey_ImportPubRaw(&key, bench_xmss_sha2_10_256_pub, + (word32)sizeof(bench_xmss_sha2_10_256_pub)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_ImportPubRaw %d\n", ret); + goto exit; + } + + /* Provision the verify-only public key into the HSM cache. EPHEMERAL keeps + * it out of NVM; it is evicted during cleanup. */ + ret = wh_Client_XmssImportPubKey(client, &key, &keyId, + WH_NVM_FLAGS_EPHEMERAL, + (uint16_t)strlen(keyLabel), + (uint8_t*)keyLabel); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_XmssImportPubKey %d\n", ret); + goto exit; + } + + /* Set key ID */ + ret = wh_Client_XmssSetKeyId(&key, keyId); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_XmssSetKeyId %d\n", ret); + goto exit; + } + + /* Benchmark the verification operation over the fixed valid signature */ + for (i = 0; i < WOLFHSM_CFG_BENCH_PK_ITERS && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + + /* Time only the verify operation */ + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wc_XmssKey_Verify(&key, bench_xmss_sha2_10_256_sig, + (word32)sizeof(bench_xmss_sha2_10_256_sig), msg, + (int)msgSz); + benchStopRet = wh_Bench_StopOp(ctx, id); + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_Verify %d\n", ret); + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + } + +exit: + /* Clean up resources */ + if (initialized_key) { + wc_XmssKey_Free(&key); + } + + /* Evict the cached verify-only public key */ + if (!WH_KEYID_ISERASED(keyId)) { + int evictRet = wh_Client_KeyEvict(client, keyId); + if ((evictRet != 0) && (ret == 0)) { + ret = evictRet; + } + } + + return ret; +} + +#if !defined(WOLFSSL_XMSS_VERIFY_ONLY) + +/* Signature buffer sized for XMSS-SHA2_10_256 (2500 bytes). File scope to keep + * it off the stack; benchmark modules run sequentially, so a single shared + * buffer is safe. */ +static byte g_xmssSigBuf[2500]; + +/* Helper function for XMSS key generation benchmark */ +static int _benchXmssKeyGen(whClientContext* client, whBenchOpContext* ctx, + int id) +{ + int ret = 0; + XmssKey key; + WC_RNG rng[1] = {0}; + int i; + int initialized_rng = 0; + int initialized_key = 0; + whKeyId keyId = WH_KEYID_ERASED; + + (void)wh_Client_SetDmaMode(client, 1); + + /* Initialize the RNG */ + ret = wc_InitRng_ex(rng, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_InitRng_ex %d\n", ret); + return ret; + } + initialized_rng = 1; + + /* Benchmark the key generation */ + for (i = 0; i < WOLFHSM_CFG_BENCH_KG_ITERS && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + int keyIdRet = 0; + + /* Initialize the XMSS key before each iteration */ + ret = wc_XmssKey_Init(&key, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_Init %d\n", ret); + break; + } + initialized_key = 1; + + /* Bind the XMSS parameter string */ + ret = wc_XmssKey_SetParamStr(&key, WH_BENCH_XMSS_PARAM_STR); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_SetParamStr %d\n", ret); + break; + } + + /* Time only the key generation. Keygen commits the private key to NVM + * on the server, so it is erased afterwards outside the timed region. */ + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wc_XmssKey_MakeKey(&key, rng); + benchStopRet = wh_Bench_StopOp(ctx, id); + + /* Capture the keyId as soon as the key exists so cleanup can erase the + * committed key even if a check below fails. The getter only reads a + * field, but its result gates the NVM cleanup, so check it too. */ + if (ret == 0) { + keyIdRet = wh_Client_XmssGetKeyId(&key, &keyId); + } + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_MakeKey %d\n", ret); + break; + } + if (keyIdRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_XmssGetKeyId %d\n", keyIdRet); + ret = keyIdRet; + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + + /* Erase the committed key from cache + NVM before the next iteration */ + if (!WH_KEYID_ISERASED(keyId)) { + int eraseRet = wh_Client_KeyErase(client, keyId); + if (eraseRet != 0) { + WH_BENCH_PRINTF("Failed to erase key %d\n", eraseRet); + ret = eraseRet; + break; + } + keyId = WH_KEYID_ERASED; + } + + /* Free the key after each iteration */ + wc_XmssKey_Free(&key); + initialized_key = 0; + } + + /* Clean up resources */ + if (initialized_key) { + wc_XmssKey_Free(&key); + } + if (initialized_rng) { + wc_FreeRng(rng); + } + + /* Erase a key left committed by a mid-iteration failure */ + if (!WH_KEYID_ISERASED(keyId)) { + int eraseRet = wh_Client_KeyErase(client, keyId); + if ((eraseRet != 0) && (ret == 0)) { + ret = eraseRet; + } + } + + return ret; +} + +/* Helper function for XMSS sign benchmark */ +static int _benchXmssSign(whClientContext* client, whBenchOpContext* ctx, + int id) +{ + int ret = 0; + XmssKey key; + WC_RNG rng[1] = {0}; + const byte msg[] = WH_BENCH_XMSS_MSG; + const word32 msgSz = (word32)(sizeof(msg) - 1); + word32 sigSz = 0; + int i; + int iters = WOLFHSM_CFG_BENCH_PK_ITERS; + int initialized_rng = 0; + int initialized_key = 0; + whKeyId keyId = WH_KEYID_ERASED; + + (void)wh_Client_SetDmaMode(client, 1); + + /* Initialize the RNG */ + ret = wc_InitRng_ex(rng, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_InitRng_ex %d\n", ret); + goto exit; + } + initialized_rng = 1; + + /* Initialize the XMSS key */ + ret = wc_XmssKey_Init(&key, NULL, WH_CLIENT_DEVID(client)); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_Init %d\n", ret); + goto exit; + } + initialized_key = 1; + + /* Bind the XMSS parameter string */ + ret = wc_XmssKey_SetParamStr(&key, WH_BENCH_XMSS_PARAM_STR); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_SetParamStr %d\n", ret); + goto exit; + } + + /* Determine the signature length and confirm it fits the buffer */ + ret = wc_XmssKey_GetSigLen(&key, &sigSz); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_GetSigLen %d\n", ret); + goto exit; + } + if (sigSz > sizeof(g_xmssSigBuf)) { + WH_BENCH_PRINTF("XMSS sig buffer too small: need=%u have=%u\n", + (unsigned)sigSz, (unsigned)sizeof(g_xmssSigBuf)); + ret = BUFFER_E; + goto exit; + } + + /* Generate the signing key once (untimed). Keygen commits the key to NVM + * on the server, so it is erased during cleanup. */ + ret = wc_XmssKey_MakeKey(&key, rng); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_MakeKey %d\n", ret); + goto exit; + } + /* Capture the committed key's id so cleanup can erase it. Its result gates + * NVM cleanup, so surface a failure rather than leaking the key. */ + ret = wh_Client_XmssGetKeyId(&key, &keyId); + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wh_Client_XmssGetKeyId %d\n", ret); + goto exit; + } + + /* A fresh key must have signatures available */ + if (wc_XmssKey_SigsLeft(&key) == 0) { + WH_BENCH_PRINTF("XMSS reported exhausted on fresh key\n"); + ret = WH_ERROR_ABORTED; + goto exit; + } + + /* XMSS is stateful: the key holds 2^H one-time signatures. Cap the loop to + * that budget so raising the global WOLFHSM_CFG_BENCH_PK_ITERS (shared with + * the other PK benchmarks) does not exhaust the key mid-run. */ + if (iters > (1 << WH_BENCH_XMSS_HEIGHT)) { + WH_BENCH_PRINTF("XMSS sign capped to %d iterations (2^H OTS budget)\n", + (1 << WH_BENCH_XMSS_HEIGHT)); + iters = (1 << WH_BENCH_XMSS_HEIGHT); + } + + /* Benchmark the signing operation. Each sign advances the private state and + * commits it to NVM on the server before returning the signature. */ + for (i = 0; i < iters && ret == 0; i++) { + int benchStartRet; + int benchStopRet; + word32 localSigSz = sigSz; + + /* Time only the sign operation */ + benchStartRet = wh_Bench_StartOp(ctx, id); + ret = wc_XmssKey_Sign(&key, g_xmssSigBuf, &localSigSz, msg, (int)msgSz); + benchStopRet = wh_Bench_StopOp(ctx, id); + + if (benchStartRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StartOp %d\n", benchStartRet); + ret = benchStartRet; + break; + } + if (ret != 0) { + WH_BENCH_PRINTF("Failed to wc_XmssKey_Sign %d\n", ret); + break; + } + if (benchStopRet != 0) { + WH_BENCH_PRINTF("Failed to wh_Bench_StopOp %d\n", benchStopRet); + ret = benchStopRet; + break; + } + } + +exit: + /* Clean up resources */ + if (initialized_key) { + wc_XmssKey_Free(&key); + } + if (initialized_rng) { + wc_FreeRng(rng); + } + + /* Erase the committed key from cache + NVM */ + if (!WH_KEYID_ISERASED(keyId)) { + int eraseRet = wh_Client_KeyErase(client, keyId); + if ((eraseRet != 0) && (ret == 0)) { + ret = eraseRet; + } + } + + return ret; +} + +#endif /* !WOLFSSL_XMSS_VERIFY_ONLY */ + +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_XMSS */ + +/* Public benchmark module functions for XMSS */ +int wh_Bench_Mod_XmssKeyGen(whClientContext* client, whBenchOpContext* ctx, + int id, void* params) +{ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) && \ + !defined(WOLFSSL_XMSS_VERIFY_ONLY) + (void)params; + return _benchXmssKeyGen(client, ctx, id); +#else + (void)client; + (void)ctx; + (void)id; + (void)params; + return WH_ERROR_NOTIMPL; +#endif +} + +int wh_Bench_Mod_XmssSign(whClientContext* client, whBenchOpContext* ctx, + int id, void* params) +{ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) && \ + !defined(WOLFSSL_XMSS_VERIFY_ONLY) + (void)params; + return _benchXmssSign(client, ctx, id); +#else + (void)client; + (void)ctx; + (void)id; + (void)params; + return WH_ERROR_NOTIMPL; +#endif +} + +int wh_Bench_Mod_XmssVerify(whClientContext* client, whBenchOpContext* ctx, + int id, void* params) +{ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) + (void)params; + return _benchXmssVerify(client, ctx, id); +#else + (void)client; + (void)ctx; + (void)id; + (void)params; + return WH_ERROR_NOTIMPL; +#endif +} + +#endif /* !WOLFHSM_CFG_NO_CRYPTO && WOLFHSM_CFG_BENCH_ENABLE */ diff --git a/benchmark/config/user_settings.h b/benchmark/config/user_settings.h index 6caa31755..c13fcaa81 100644 --- a/benchmark/config/user_settings.h +++ b/benchmark/config/user_settings.h @@ -157,6 +157,12 @@ extern "C" { /* ML-KEM Options */ #define WOLFSSL_HAVE_MLKEM +/* LMS / HSS Options (RFC 8554, NIST SP 800-208) */ +#define WOLFSSL_HAVE_LMS + +/* XMSS / XMSS^MT Options (RFC 8391, NIST SP 800-208) */ +#define WOLFSSL_HAVE_XMSS + /** Composite features */ #define HAVE_HKDF #define HAVE_CMAC_KDF diff --git a/benchmark/wh_bench.c b/benchmark/wh_bench.c index fa595e847..7378af3cc 100644 --- a/benchmark/wh_bench.c +++ b/benchmark/wh_bench.c @@ -282,6 +282,26 @@ typedef enum BenchModuleIdx { BENCH_MODULE_IDX_ML_KEM_1024_DECAPS_DMA, #endif /* !WOLFSSL_NO_ML_KEM_1024 */ #endif /* WOLFSSL_HAVE_MLKEM */ + +/* LMS (stateful, DMA-only) */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) && \ + !defined(WOLFSSL_LMS_VERIFY_ONLY) + BENCH_MODULE_IDX_LMS_KEY_GEN, + BENCH_MODULE_IDX_LMS_SIGN, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) + BENCH_MODULE_IDX_LMS_VERIFY, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_LMS */ + +/* XMSS (stateful, DMA-only) */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) && \ + !defined(WOLFSSL_XMSS_VERIFY_ONLY) + BENCH_MODULE_IDX_XMSS_KEY_GEN, + BENCH_MODULE_IDX_XMSS_SIGN, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) + BENCH_MODULE_IDX_XMSS_VERIFY, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_XMSS */ #endif /* !(WOLFHSM_CFG_NO_CRYPTO) */ /* number of modules. This must be the last entry and will be used as the * size of the global modules array */ @@ -496,6 +516,26 @@ static BenchModule g_benchModules[] = { [BENCH_MODULE_IDX_ML_KEM_1024_DECAPS_DMA] = {"ML-KEM-1024-DECAPS-DMA", wh_Bench_Mod_MlKem1024DecapsDma, BENCH_THROUGHPUT_OPS, 0, NULL}, #endif /* !WOLFSSL_NO_ML_KEM_1024 */ #endif /* WOLFSSL_HAVE_MLKEM */ + + /* LMS (stateful, DMA-only) */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) && \ + !defined(WOLFSSL_LMS_VERIFY_ONLY) + [BENCH_MODULE_IDX_LMS_KEY_GEN] = {"LMS-L1H5W8-KEY-GEN", wh_Bench_Mod_LmsKeyGen, BENCH_THROUGHPUT_OPS, 0, NULL}, + [BENCH_MODULE_IDX_LMS_SIGN] = {"LMS-L1H5W8-SIGN", wh_Bench_Mod_LmsSign, BENCH_THROUGHPUT_OPS, 0, NULL}, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_LMS && !WOLFSSL_LMS_VERIFY_ONLY */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_LMS) + [BENCH_MODULE_IDX_LMS_VERIFY] = {"LMS-L1H5W8-VERIFY", wh_Bench_Mod_LmsVerify, BENCH_THROUGHPUT_OPS, 0, NULL}, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_LMS */ + + /* XMSS (stateful, DMA-only) */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) && \ + !defined(WOLFSSL_XMSS_VERIFY_ONLY) + [BENCH_MODULE_IDX_XMSS_KEY_GEN] = {"XMSS-SHA2_10_256-KEY-GEN", wh_Bench_Mod_XmssKeyGen, BENCH_THROUGHPUT_OPS, 0, NULL}, + [BENCH_MODULE_IDX_XMSS_SIGN] = {"XMSS-SHA2_10_256-SIGN", wh_Bench_Mod_XmssSign, BENCH_THROUGHPUT_OPS, 0, NULL}, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_XMSS && !WOLFSSL_XMSS_VERIFY_ONLY */ +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_HAVE_XMSS) + [BENCH_MODULE_IDX_XMSS_VERIFY] = {"XMSS-SHA2_10_256-VERIFY", wh_Bench_Mod_XmssVerify, BENCH_THROUGHPUT_OPS, 0, NULL}, +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_HAVE_XMSS */ #endif /* !(WOLFHSM_CFG_NO_CRYPTO) */ }; /* clang-format on */ diff --git a/benchmark/wh_bench_ops.h b/benchmark/wh_bench_ops.h index d2d0d3aad..c24fc02c7 100644 --- a/benchmark/wh_bench_ops.h +++ b/benchmark/wh_bench_ops.h @@ -26,7 +26,7 @@ #include /* Maximum number of operations that can be registered */ -#define MAX_BENCH_OPS 119 +#define MAX_BENCH_OPS 128 /* Maximum length of operation name */ #define MAX_OP_NAME 64