From e0fde6bfae67b885bff091b46cca39f8c80ab501 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Thu, 6 Apr 2023 00:15:33 -0400 Subject: [PATCH 01/13] Initial commit of Cisco's open source LMS --- src/CMakeLists.txt | 4 + src/oqsconfig.h.cmake | 2 + src/sig_stateful/lms/CMakeLists.txt | 42 + .../lms/external/ACVP Definition.txt | 23 + src/sig_stateful/lms/external/README | 7 + src/sig_stateful/lms/external/common_defs.h | 178 ++++ src/sig_stateful/lms/external/config.h | 36 + src/sig_stateful/lms/external/endian.c | 23 + src/sig_stateful/lms/external/endian.h | 9 + src/sig_stateful/lms/external/hash.c | 119 +++ src/sig_stateful/lms/external/hash.h | 57 ++ src/sig_stateful/lms/external/hss.c | 169 ++++ src/sig_stateful/lms/external/hss.h | 417 ++++++++ src/sig_stateful/lms/external/hss_alloc.c | 555 +++++++++++ src/sig_stateful/lms/external/hss_aux.c | 355 +++++++ src/sig_stateful/lms/external/hss_aux.h | 59 ++ src/sig_stateful/lms/external/hss_common.c | 48 + src/sig_stateful/lms/external/hss_common.h | 22 + src/sig_stateful/lms/external/hss_compute.c | 174 ++++ src/sig_stateful/lms/external/hss_derive.c | 325 ++++++ src/sig_stateful/lms/external/hss_derive.h | 74 ++ src/sig_stateful/lms/external/hss_generate.c | 930 ++++++++++++++++++ src/sig_stateful/lms/external/hss_internal.h | 243 +++++ src/sig_stateful/lms/external/hss_keygen.c | 368 +++++++ src/sig_stateful/lms/external/hss_param.c | 153 +++ src/sig_stateful/lms/external/hss_reserve.c | 194 ++++ src/sig_stateful/lms/external/hss_reserve.h | 21 + src/sig_stateful/lms/external/hss_sign.c | 736 ++++++++++++++ src/sig_stateful/lms/external/hss_sign_inc.c | 218 ++++ src/sig_stateful/lms/external/hss_sign_inc.h | 81 ++ src/sig_stateful/lms/external/hss_thread.h | 135 +++ .../lms/external/hss_thread_pthread.c | 298 ++++++ .../lms/external/hss_thread_single.c | 63 ++ src/sig_stateful/lms/external/hss_verify.c | 196 ++++ src/sig_stateful/lms/external/hss_verify.h | 23 + .../lms/external/hss_verify_inc.c | 203 ++++ .../lms/external/hss_verify_inc.h | 82 ++ src/sig_stateful/lms/external/hss_zeroize.c | 49 + src/sig_stateful/lms/external/hss_zeroize.h | 10 + src/sig_stateful/lms/external/license.txt | 29 + src/sig_stateful/lms/external/lm_common.c | 79 ++ src/sig_stateful/lms/external/lm_common.h | 20 + src/sig_stateful/lms/external/lm_ots.h | 64 ++ src/sig_stateful/lms/external/lm_ots_common.c | 99 ++ src/sig_stateful/lms/external/lm_ots_common.h | 16 + src/sig_stateful/lms/external/lm_ots_sign.c | 168 ++++ src/sig_stateful/lms/external/lm_ots_verify.c | 122 +++ src/sig_stateful/lms/external/lm_ots_verify.h | 23 + src/sig_stateful/lms/external/lm_verify.c | 107 ++ src/sig_stateful/lms/external/lm_verify.h | 12 + .../lms/external/programming.notes | 674 +++++++++++++ src/sig_stateful/lms/external/read.me | 597 +++++++++++ src/sig_stateful/lms/external/sha256.c | 183 ++++ src/sig_stateful/lms/external/sha256.h | 43 + src/sig_stateful/sig_stfl.c | 155 ++- src/sig_stateful/sig_stfl.h | 76 +- 56 files changed, 9166 insertions(+), 2 deletions(-) create mode 100644 src/sig_stateful/lms/CMakeLists.txt create mode 100644 src/sig_stateful/lms/external/ACVP Definition.txt create mode 100644 src/sig_stateful/lms/external/README create mode 100644 src/sig_stateful/lms/external/common_defs.h create mode 100644 src/sig_stateful/lms/external/config.h create mode 100644 src/sig_stateful/lms/external/endian.c create mode 100644 src/sig_stateful/lms/external/endian.h create mode 100644 src/sig_stateful/lms/external/hash.c create mode 100644 src/sig_stateful/lms/external/hash.h create mode 100644 src/sig_stateful/lms/external/hss.c create mode 100644 src/sig_stateful/lms/external/hss.h create mode 100644 src/sig_stateful/lms/external/hss_alloc.c create mode 100644 src/sig_stateful/lms/external/hss_aux.c create mode 100644 src/sig_stateful/lms/external/hss_aux.h create mode 100644 src/sig_stateful/lms/external/hss_common.c create mode 100644 src/sig_stateful/lms/external/hss_common.h create mode 100644 src/sig_stateful/lms/external/hss_compute.c create mode 100644 src/sig_stateful/lms/external/hss_derive.c create mode 100644 src/sig_stateful/lms/external/hss_derive.h create mode 100644 src/sig_stateful/lms/external/hss_generate.c create mode 100644 src/sig_stateful/lms/external/hss_internal.h create mode 100644 src/sig_stateful/lms/external/hss_keygen.c create mode 100644 src/sig_stateful/lms/external/hss_param.c create mode 100644 src/sig_stateful/lms/external/hss_reserve.c create mode 100644 src/sig_stateful/lms/external/hss_reserve.h create mode 100644 src/sig_stateful/lms/external/hss_sign.c create mode 100644 src/sig_stateful/lms/external/hss_sign_inc.c create mode 100644 src/sig_stateful/lms/external/hss_sign_inc.h create mode 100644 src/sig_stateful/lms/external/hss_thread.h create mode 100644 src/sig_stateful/lms/external/hss_thread_pthread.c create mode 100644 src/sig_stateful/lms/external/hss_thread_single.c create mode 100644 src/sig_stateful/lms/external/hss_verify.c create mode 100644 src/sig_stateful/lms/external/hss_verify.h create mode 100644 src/sig_stateful/lms/external/hss_verify_inc.c create mode 100644 src/sig_stateful/lms/external/hss_verify_inc.h create mode 100644 src/sig_stateful/lms/external/hss_zeroize.c create mode 100644 src/sig_stateful/lms/external/hss_zeroize.h create mode 100644 src/sig_stateful/lms/external/license.txt create mode 100644 src/sig_stateful/lms/external/lm_common.c create mode 100644 src/sig_stateful/lms/external/lm_common.h create mode 100644 src/sig_stateful/lms/external/lm_ots.h create mode 100644 src/sig_stateful/lms/external/lm_ots_common.c create mode 100644 src/sig_stateful/lms/external/lm_ots_common.h create mode 100644 src/sig_stateful/lms/external/lm_ots_sign.c create mode 100644 src/sig_stateful/lms/external/lm_ots_verify.c create mode 100644 src/sig_stateful/lms/external/lm_ots_verify.h create mode 100644 src/sig_stateful/lms/external/lm_verify.c create mode 100644 src/sig_stateful/lms/external/lm_verify.h create mode 100644 src/sig_stateful/lms/external/programming.notes create mode 100644 src/sig_stateful/lms/external/read.me create mode 100644 src/sig_stateful/lms/external/sha256.c create mode 100644 src/sig_stateful/lms/external/sha256.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a682fc6c55..2eef22dae9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,6 +67,10 @@ if(OQS_ENABLE_SIG_STFL_XMSS) add_subdirectory(sig_stateful/xmss) set(SIG_STFL_OBJS ${SIG_STFL_OBJS} $) endif() +if(OQS_ENABLE_SIG_STFL_LMS) + add_subdirectory(sig_stateful/lms) + set(SIG_STFL_OBJS ${SIG_STFL_OBJS} $) +endif() ##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_ADD_ALG_OBJECTS_END add_library(oqs kem/kem.c diff --git a/src/oqsconfig.h.cmake b/src/oqsconfig.h.cmake index 9b6354bd1c..3becf823c0 100644 --- a/src/oqsconfig.h.cmake +++ b/src/oqsconfig.h.cmake @@ -295,4 +295,6 @@ #cmakedefine OQS_ENABLE_SIG_STFL_XMSSMT_SHA256_6_H60 1 #cmakedefine OQS_ENABLE_SIG_STFL_XMSSMT_SHA256_12_H60 1 +#cmakedefine OQS_ENABLE_SIG_STFL_LMS 1 +#cmakedefine OQS_ENABLE_SIG_STFL_HSS 1 diff --git a/src/sig_stateful/lms/CMakeLists.txt b/src/sig_stateful/lms/CMakeLists.txt new file mode 100644 index 0000000000..4f41445339 --- /dev/null +++ b/src/sig_stateful/lms/CMakeLists.txt @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: +set(SRCS + external/endian.c + external/hash.c + external/hss.c + external/hss_alloc.c + external/hss_aux.c + external/hss_common.c + external/hss_compute.c + external/hss_derive.c + external/hss_generate.c + external/hss_keygen.c + external/hss_param.c + external/hss_reserve.c + external/hss_sign.c + external/hss_sign_inc.c + external/hss_thread_single.c + external/hss_verify.c + external/hss_verify_inc.c + external/hss_zeroize.c + external/lm_common.c + external/lm_ots_common.c + external/lm_ots_sign.c + external/lm_ots_verify.c + external/lm_verify.c + external/sha256.c) + +add_library(lms OBJECT ${SRCS}) +target_include_directories(lms PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/external) +target_compile_definitions(lms PRIVATE OQS + LMS_EXPORT= + WITH_SHA256_N32_W1 + WITH_SHA256_N32_W2 + WITH_SHA256_N32_W4 + WITH_SHA256_N32_W8 + WITH_SHA256_N32_H5 + WITH_SHA256_N32_H10 + WITH_SHA256_N32_H15 + WITH_SHA256_N32_H20 + WITH_SHA256_N32_H25 + ) + diff --git a/src/sig_stateful/lms/external/ACVP Definition.txt b/src/sig_stateful/lms/external/ACVP Definition.txt new file mode 100644 index 0000000000..d32fa4905d --- /dev/null +++ b/src/sig_stateful/lms/external/ACVP Definition.txt @@ -0,0 +1,23 @@ +This is the current definition for the ACVP-compatible method for deriving secrets. +The only question with ACVP (or any test vector) is how to make the crypto deterministic. Now, the RFC leaves a number of areas open; we need to fill in the gaps. +For any LMS tree, we have these potentially nondeterministic values: +- The I value +- The private values x[i] that are the start of each Winternitz chain; see Algorithm 0 +- The randomizer value C that is in the header that is inserted in the initial message hash for each signature; see Algorithm 3 +The RFC lists in Appendix A a method for generating the private values s[i] based on a SEED value (and the I value, the i value that indicates the WOTS digit the private value is for, and the j value that indicates the leaf in the LMS tree); we propose to extend that method: +- Every LMS tree will have a SEED and I value associated with it; this SEED will be m bits long (where m is the value specified by the LMS parameter set), and the I value will be 128 bits long. +- To generate the private x[i] values, we will use the algorithm in Appendix A +- To generate the randomized value C, we will adapt algorithm A, by using the SEED value of the LMS tree, the I value of the LMS tree, the q value to be the LMS index, and the i value to be 65533 +- To generate the SEED value for a child LMS tree, we will adapt algorithm A by using the SEED value of the parent LMS tree, the I value of the parent LMS tree, the q value to be the LMS index of the child, and the i value to be 65534 (and the SEED being the SEED of the parent LMS tree). +- To generate the I value for a child LMS tree, we will adapt algorithm A by using the SEED value of the parent LMS tree, the I value of the parent LMS tree, the q value to be the LMS index of the child, and the i value to be 65535 (and the SEED being the SEED of the parent LMS tree); the I value will be the first 128 bits of the hash. +The ACVP server will specify the SEED and I for the root (top-most) LMS tree; the SEED and I values for all child LMS trees are derived as above. +Rules about handling varying length SEEDs (based on differing hash functions): +- A SEED for an LMS tree will always be m bits long; where m is specified by the LMS parameter set. +- The ACVP server MUST specify the correct length for the root SEED (and the client will error out if incorrect) +- If the SEED generated by a parent LMS tree isn’t the correct length for the child then: +o If it is too long, then it will be truncated to m bits +o If it is too short, then it will have 0 bits appended to make it m bits +(Hmmmm, would it more sense to just ignore the ‘all seeds must be precisely m bits long’ rule (as stated in Appendix A), and just allow variable length seeds??? The only issue I can think of is that the current code can’t handle a variable-length top-level seed (but that could be fixed). It might make sense to keep the ‘the ACVP seed must be m bits long’ rule (if someone was paranoid enough to use SHA-512, then they might not be happy with 256 bits root seeds), but be flexible elsewhere… + + +The ACVP server will specify a SEED and I which will be used to seed the entire process. Note that, if the SEED value isn’t the same length as the hash size indicated by the root LMS hash parameter set, the request will be rejected. diff --git a/src/sig_stateful/lms/external/README b/src/sig_stateful/lms/external/README new file mode 100644 index 0000000000..36b26924d4 --- /dev/null +++ b/src/sig_stateful/lms/external/README @@ -0,0 +1,7 @@ +This code attempts to be a usable implementation of the LMS Hash Based +Signature Scheme from RFC 8554. + +See read.me for documentation how to use it. + +This is the ACVP branch - designed to be (optionally) compatible with the +public ACVP server diff --git a/src/sig_stateful/lms/external/common_defs.h b/src/sig_stateful/lms/external/common_defs.h new file mode 100644 index 0000000000..83739949ee --- /dev/null +++ b/src/sig_stateful/lms/external/common_defs.h @@ -0,0 +1,178 @@ +#if !defined( COMMON_DEFS_H_ ) +#define COMMON_DEFS_H_ + +/* + * These are defintions for the LMS implementation that are common throughout + * the system (and so are collected in one place) + */ + +#include +#include + +#define MAX_HASH 32 /* Length of the largest hash we support */ + +/* The I (Merkle tree identifier) value is 16 bytes long */ +#define I_LEN 16 + +/* The maximum height of a Merkle tree */ +#define MAX_MERKLE_HEIGHT 25 + +/* The mininum height of a Merkle tree. Some of our update logic assumes */ +/* this isn't too small */ +#define MIN_MERKLE_HEIGHT 5 + +/* The minimum/maximum number of levels of Merkle trees within an HSS trees */ +#define MIN_HSS_LEVELS 1 /* Minumum levels we allow */ +#define MAX_HSS_LEVELS 8 /* Maximum levels we allow */ + +/* This is the length of our internal seed values */ +#define SEED_LEN 32 /* Enough to make Grover's infeasible */ + +/* Here are some internal types used within the code. They are listed more */ +/* for documentation ("this is what this variable is expected to be") rather */ +/* than to let the compiler do any sort of type checking */ + + /* This is an index into a Merkle tree */ + /* Used for both the leaf index (0..N-1) and the node number (1..2*N-1), */ + /* where N is the size 2**h of the tre */ +#if MAX_MERKLE_HEIGHT > 31 + /* We need to express more than 32 bits in this type */ +typedef uint_fast64_t merkle_index_t; +#error We need to extend the id we place within a hash to more than 4 bytes +#else +typedef uint_fast32_t merkle_index_t; +#endif + + /* This is the name of a parameter set */ + /* Used for both an OTS parameter set or an LM parameter set */ + /* Both are 32 bits */ +typedef uint_fast32_t param_set_t; + + /* This is a sequence number over an HSS tree */ + /* This means we can never generate more than 2**64 signatures from a */ + /* private key (even if the parameter set would, in theory, allow us */ + /* to do more) */ +typedef uint_fast64_t sequence_t; + +/* Defined LM parameter sets */ +#define LMS_SHA256_N32_H5 0x00000005 +#define LMS_SHA256_N32_H10 0x00000006 +#define LMS_SHA256_N32_H15 0x00000007 +#define LMS_SHA256_N32_H20 0x00000008 +#define LMS_SHA256_N32_H25 0x00000009 + +/* LM-OTS registry */ +#define LMOTS_SHA256_N32_W1 0x00000001 +#define LMOTS_SHA256_N32_W2 0x00000002 +#define LMOTS_SHA256_N32_W4 0x00000003 +#define LMOTS_SHA256_N32_W8 0x00000004 + +/* + * Internal formats of various hashes + * + * We do a number of different hashes as a part of this package; some + * specified by the draft, some specific to us. + * For each such hash, we list the values being hashed, and the offset + * from the start where they go. We treat them as indicies into unsigned char + * arrays, and not structs, to avoid any potential padding issues with structs + * + * For a hash of type XXXX, XXXX_Z is the offset where component Z goes, + * XXXX_LEN(hash_len) is the length being hashed (assuming that hash length), + * XXXX_MAXLEN is the maximum length it can be (for allocation), and D_XXXX + * is the hash distinguisher (the value that makes it different from any other + * hash) + */ + +/* The initial message hashing */ +#define MESG_I 0 +#define MESG_Q 16 +#define MESG_D 20 /* The fixed D_MESG value */ +#define MESG_C 22 +#define MESG_PREFIX_LEN(n) (MESG_C + (n)) /* Length not counting the actual */ + /* message being signed */ +#define MESG_PREFIX_MAXLEN MESG_PREFIX_LEN(MAX_HASH) +#define D_MESG 0x8181 + +/* The Winternitz iteration hashes */ +#define ITER_I 0 +#define ITER_Q 16 +#define ITER_K 20 /* The RFC uses i here */ +#define ITER_J 22 +#define ITER_PREV 23 /* Hash from previous iteration; RFC uses tmp */ +#define ITER_LEN(hash_len) (ITER_PREV + (hash_len)) +#define ITER_MAX_LEN ITER_LEN(MAX_HASH) + +/* Hashing the OTS public key */ +#define PBLC_I 0 +#define PBLC_Q 16 +#define PBLC_D 20 /* The fixed D_PBLC value */ +#define PBLC_PREFIX_LEN 22 /* Not counting the OTS public keys */ +#define D_PBLC 0x8080 + +/* Hashing Merkle tree leaf nodes */ +#define LEAF_I 0 +#define LEAF_R 16 +#define LEAF_D 20 +#define LEAF_PK 22 +#define LEAF_LEN(root_len) (LEAF_PK + (root_len)) +#define LEAF_MAX_LEN LEAF_LEN(MAX_HASH) +#define D_LEAF 0x8282 + +/* Hashing Merkle tree internal nodes */ +#define INTR_I 0 +#define INTR_R 16 +#define INTR_D 20 +#define INTR_PK 22 +#define INTR_LEN(root_len) (INTR_PK + 2 * (root_len)) +#define INTR_MAX_LEN INTR_LEN(MAX_HASH) +#define D_INTR 0x8383 + +/* The determanistic key generation */ +/* Also used to generate subkeys in the j-tree hierarchy */ +/* As we'll always do either one or the other, we can reuse the structure */ +/* for both purposes */ +#define PRG_I 0 +#define PRG_Q 16 +#define PRG_J 20 +#define PRG_FF 22 /* A fixed 0xff goes here */ +#define PRG_SEED 23 +#define PRG_LEN(seed_len) (23 + (seed_len)) +#define PRG_MAX_LEN PRG_LEN(MAX_HASH) + +/* The below are hash formats that the draft does not list, but we */ +/* implement ourselves (largely because we need to be determanistic */ +/* based on the seed) */ + +/* Hash used to generate subkeys in the q tree hierarchy */ +#define QTREE_I 0 +#define QTREE_Q 16 +#define QTREE_D 20 /* D_QTREE goes here */ +#define QTREE_SEED 22 +#define QTREE_LEN (22 + 32) /* We assume a fixed length seed */ +#define QTREE_MAX_LEN QTREE_LEN +#define D_QTREE 0xffff + +/* Hash used to generate the master seed for the top level Merkle tree */ +#define TOPSEED_I 0 /* 16 0's here (we don't have an I value) */ +#define TOPSEED_Q 16 /* 0's here (as we don't have a Q value) */ +#define TOPSEED_D 20 /* D_TOPSEED */ +#define TOPSEED_WHICH 22 /* 0 -> Gen Master seed (used as seed for */ + /* the next two) */ + /* 1 -> Create top level seed */ + /* 2 -> Create top level I */ +#define TOPSEED_SEED 23 /* 32 bytes long */ +#define TOPSEED_LEN (TOPSEED_SEED + 32) +#define D_TOPSEED 0xfefe + +/* Hash used to generate the key used for the authenticating the aux values */ +#define DAUX_I 0 /* 16 0's here (no I value) */ +#define DAUX_Q 16 /* 4 more 0's here (no Q value) */ +#define DAUX_D 20 /* D_AUX_SEED_DERIVE */ +#define DAUX_PREFIX_LEN 22 /* Not counting the seed value */ +#define D_DAUX 0xfdfd + +/* Macro to set the D_XXXX value to the XXXX_D offset */ +#define SET_D(p, value) (void)(((p)[0] = (value) >> 8), \ + ((p)[1] = (value) & 0xff)) + +#endif /* COMMON_DEFS_H_ */ diff --git a/src/sig_stateful/lms/external/config.h b/src/sig_stateful/lms/external/config.h new file mode 100644 index 0000000000..e23d19fa9a --- /dev/null +++ b/src/sig_stateful/lms/external/config.h @@ -0,0 +1,36 @@ +#if !defined( CONFIG_H_ ) +#define CONFIG_H_ + +#define LMS_UNUSED(x) (void)(x) + +/* + * This file has #define's that specify how this package operates, and + * are designed to be tweaked by the user. + * + * These can be adjusted to be appropriate for what the application and + * the operating environment needs + */ + +/* + * This modifies which seed generation logic we use + * Note that changing these parameters will change the mapping + * between private keys. + * + * 0 -> We generate seeds using the process defined in Appendix A of the draft + * This is slightly faster + * 1 -> We use a side channel resistant process, never using any single secret + * seed in more than a defined number of distinct hashes + * 2 -> We generate seeds and secrets in a way which is compatible with ACVP + */ +#define SECRET_METHOD 2 + +/* + * If we're using the side channel resistant method, this defines the max + * number of times we'll use a single secret. Note that this is the log2 + * of the max number of times, and so 3 means 'no more than 8 times' + * Reducing SECRET_MAX is a bit more costly; however I don't know that if + * it is significant + */ +#define SECRET_MAX 4 /* Never use a seed more than 16 times */ + +#endif /* CONFIG_H_ */ diff --git a/src/sig_stateful/lms/external/endian.c b/src/sig_stateful/lms/external/endian.c new file mode 100644 index 0000000000..709dc7bf98 --- /dev/null +++ b/src/sig_stateful/lms/external/endian.c @@ -0,0 +1,23 @@ +#include "endian.h" + +void put_bigendian( void *target, unsigned long long value, size_t bytes ) { + unsigned char *b = target; + int i; + + for (i = bytes-1; i >= 0; i--) { + b[i] = value & 0xff; + value >>= 8; + } +} + +unsigned long long get_bigendian( const void *target, size_t bytes ) { + const unsigned char *b = target; + unsigned long long result = 0; + size_t i; + + for (i=0; i + +void put_bigendian( void *target, unsigned long long value, size_t bytes ); +unsigned long long get_bigendian( const void *target, size_t bytes ); + +#endif /* ENDIAN_H_ */ diff --git a/src/sig_stateful/lms/external/hash.c b/src/sig_stateful/lms/external/hash.c new file mode 100644 index 0000000000..dffcdaf6a6 --- /dev/null +++ b/src/sig_stateful/lms/external/hash.c @@ -0,0 +1,119 @@ +#include +#include "hash.h" +#include "sha256.h" +#include "hss_zeroize.h" + +#define ALLOW_VERBOSE 0 /* 1 -> we allow the dumping of intermediate */ + /* states. Useful for debugging; horrid */ + /* for security */ + +/* + * This is the file that implements the hashing APIs we use internally. + * At the present, our parameter sets support only one hash function + * (SHA-256, using full 256 bit output), however, that is likely to change + * in the future + */ + +#if ALLOW_VERBOSE +#include +#include +/* + * Debugging flag; if this is set, we chat about what we're hashing, and what + * the result is it's useful when debugging; however we probably don't want to + * do this if we're multithreaded... + */ +bool hss_verbose = false; +#endif + +/* + * This will hash the message, given the hash type. It assumes that the result + * buffer is large enough for the hash + */ +void hss_hash_ctx(void *result, int hash_type, union hash_context *ctx, + const void *message, size_t message_len) { +#if ALLOW_VERBOSE + if (hss_verbose) { + int i; for (i=0; i< message_len; i++) printf( " %02x%s", ((unsigned char*)message)[i], (i%16 == 15) ? "\n" : "" ); + } +#endif + + switch (hash_type) { + case HASH_SHA256: { + SHA256_Init(&ctx->sha256); + SHA256_Update(&ctx->sha256, message, message_len); + SHA256_Final(result, &ctx->sha256); +#if ALLOW_VERBOSE + if (hss_verbose) { + printf( " ->" ); + int i; for (i=0; i<32; i++) printf( " %02x", ((unsigned char *)result)[i] ); printf( "\n" ); + } +#endif + break; + } + } +} + +void hss_hash(void *result, int hash_type, + const void *message, size_t message_len) { + union hash_context ctx; + hss_hash_ctx(result, hash_type, &ctx, message, message_len); + hss_zeroize(&ctx, sizeof ctx); +} + + +/* + * This provides an API to do incremental hashing. We use it when hashing the + * message; since we don't know how long it could be, we don't want to + * allocate a buffer that's long enough for that, plus the decoration we add + */ +void hss_init_hash_context(int h, union hash_context *ctx) { + switch (h) { + case HASH_SHA256: + SHA256_Init( &ctx->sha256 ); + break; + } +} + +void hss_update_hash_context(int h, union hash_context *ctx, + const void *msg, size_t len_msg) { +#if ALLOW_VERBOSE + if (hss_verbose) { + int i; for (i=0; isha256, msg, len_msg); + break; + } +} + +void hss_finalize_hash_context(int h, union hash_context *ctx, void *buffer) { + switch (h) { + case HASH_SHA256: + SHA256_Final(buffer, &ctx->sha256); +#if ALLOW_VERBOSE + if (hss_verbose) { + printf( " -->" ); + int i; for (i=0; i<32; i++) printf( " %02x", ((unsigned char*)buffer)[i] ); + printf( "\n" ); + } +#endif + break; + } +} + + +unsigned hss_hash_length(int hash_type) { + switch (hash_type) { + case HASH_SHA256: return 32; + } + return 0; +} + +unsigned hss_hash_blocksize(int hash_type) { + switch (hash_type) { + case HASH_SHA256: return 64; + } + return 0; +} diff --git a/src/sig_stateful/lms/external/hash.h b/src/sig_stateful/lms/external/hash.h new file mode 100644 index 0000000000..a61f9f5039 --- /dev/null +++ b/src/sig_stateful/lms/external/hash.h @@ -0,0 +1,57 @@ +#if !defined( HASH_H__ ) +#define HASH_H__ +#include "sha256.h" +#include +#include + +/* + * This defines the hash interface used within HSS. + * All globals are prefixed with hss_ to avoid name conflicts + * Gee, C++ namespaces would be nice... + */ + +/* + * Hash types + */ +enum { + HASH_SHA256 = 1, /* SHA256 */ +}; + +union hash_context { + SHA256_CTX sha256; + /* Any other hash contexts would go here */ +}; + +/* Hash the message */ +void hss_hash(void *result, int hash_type, + const void *message, size_t message_len); + +/* Does the same, but with the passed hash context (which isn't zeroized) */ +/* This is here to save time; let the caller use the same ctx for multiple */ +/* hashes, and then finally zeroize it if necessary */ +void hss_hash_ctx(void *result, int hash_type, union hash_context *ctx, + const void *message, size_t message_len); + +/* + * This is a debugging flag; turning this on will cause the system to dump + * the inputs and the outputs of all hash functions. It only works if + * debugging is allowed in hash.c (it's off by default), and it is *real* + * chatty; however sometimes you really need it for debugging + */ +extern bool hss_verbose; + +/* + * This constant has migrated to common_defs.h + */ +/* #define MAX_HASH 32 */ /* Length of the largest hash we support */ + +unsigned hss_hash_length(int hash_type); +unsigned hss_hash_blocksize(int hash_type); + +void hss_init_hash_context( int h, union hash_context *ctx ); +void hss_update_hash_context( int h, union hash_context *ctx, + const void *msg, size_t len_msg ); +void hss_finalize_hash_context( int h, union hash_context *ctx, + void *buffer); + +#endif /* HASH_H__ */ diff --git a/src/sig_stateful/lms/external/hss.c b/src/sig_stateful/lms/external/hss.c new file mode 100644 index 0000000000..c38455daed --- /dev/null +++ b/src/sig_stateful/lms/external/hss.c @@ -0,0 +1,169 @@ +/* + * This is an implementation of the HSS signature scheme from LMS + * This is designed to be full-featured + * + * Currently, this file consists of functions that don't have a better home + */ +#include +#include +#include "common_defs.h" +#include "hss.h" +#include "hash.h" +#include "endian.h" +#include "hss_internal.h" +#include "hss_aux.h" +#include "hss_derive.h" +#include "config.h" +#include "lm_ots_common.h" + +/* + * Allocate and load an ephemeral key + */ +struct hss_working_key *hss_load_private_key( + bool (*read_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + size_t memory_target, + const unsigned char *aux_data, size_t len_aux_data, + struct hss_extra_info *info ) { + + /* Step 1: determine the parameter set */ + unsigned levels; + param_set_t lm[ MAX_HSS_LEVELS ]; + param_set_t ots[ MAX_HSS_LEVELS ]; + if (!hss_get_parameter_set( &levels, lm, ots, read_private_key, context)) { + /* Can't read private key, or private key invalid */ + return 0; + } + + /* Step 2: allocate the ephemeral key */ + struct hss_working_key *w = allocate_working_key(levels, lm, ots, + memory_target, info); + if (!w) { + /* Memory allocation failure, most likely (we've already vetted */ + /* the parameter sets) */ + return 0; + } + + /* Step 3: load the ephemeral key */ + if (! hss_generate_working_key( read_private_key, context, + aux_data, len_aux_data, w, info )) { + /* About the only thing I can see failing here is perhaps */ + /* attempting to reread the private key failed the second time; */ + /* seems unlikely, but not impossible */ + hss_free_working_key( w ); + return 0; + } + + /* Success! */ + return w; +} + +/* + * Internal function to generate the root seed and I value (based on the + * private seed). We do this (rather than select seed, I at random) so that + * we don't need to store it in our private key; we can recompute them + */ +bool hss_generate_root_seed_I_value(unsigned char *seed, unsigned char *I, + const unsigned char *master_seed) { +#if SECRET_METHOD == 2 + /* In ACVP mode, we use the master seed as the source for both the */ + /* root seed, and the root I value */ + memcpy( seed, master_seed, SEED_LEN ); + memcpy( I, master_seed + SEED_LEN, I_LEN ); +#else + /* + * We use a two-level hashing scheme so that we end up using the master + * seed only twice throughout the system (once here, once to generate the + * aux hmac key) + */ + unsigned char hash_preimage[ TOPSEED_LEN ]; + unsigned char hash_postimage[ MAX_HASH ]; + + memset( hash_preimage + TOPSEED_I, 0, I_LEN ); + memset( hash_preimage + TOPSEED_Q, 0, 4 ); + SET_D( hash_preimage + TOPSEED_D, D_TOPSEED ); + hash_preimage[TOPSEED_WHICH] = 0x00; + memcpy( hash_preimage + TOPSEED_SEED, master_seed, SEED_LEN ); + + /* We use a fixed SHA256 hash; we don't care about interoperability */ + /* so we don't need to worry about what parameter set the */ + /* user specified */ +#if I_LEN > 32 || SEED_LEN != 32 +#error This logic needs to be reworked +#endif + union hash_context ctx; + + hss_hash_ctx(hash_postimage, HASH_SHA256, &ctx, hash_preimage, + TOPSEED_LEN ); + memcpy( hash_preimage + TOPSEED_SEED, hash_postimage, SEED_LEN ); + + /* Now compute the top level seed */ + hash_preimage[TOPSEED_WHICH] = 0x01; + hss_hash_ctx(seed, HASH_SHA256, &ctx, hash_preimage, TOPSEED_LEN ); + + /* Now compute the top level I value */ + hash_preimage[TOPSEED_WHICH] = 0x02; + hss_hash_ctx(hash_postimage, HASH_SHA256, &ctx, hash_preimage, + TOPSEED_LEN ); + memcpy( I, hash_postimage, I_LEN ); + + hss_zeroize( hash_preimage, sizeof hash_preimage ); /* There's keying */ + /* data here */ + hss_zeroize( &ctx, sizeof ctx ); +#endif + return true; +} + +/* + * Internal function to generate the child I value (based on the parent's + * I value). While this needs to be determanistic (so that we can create the + * same I values between reboots), there's no requirement for interoperability. + * So we use a fixed SHA256; when we support a hash function other than SHA256, + * we needn't update this. + */ +bool hss_generate_child_seed_I_value( unsigned char *seed, unsigned char *I, + const unsigned char *parent_seed, + const unsigned char *parent_I, + merkle_index_t index, + param_set_t lm, param_set_t ots) { + struct seed_derive derive; + if (!hss_seed_derive_init( &derive, lm, ots, parent_I, parent_seed )) { + return false; + } + + hss_seed_derive_set_q( &derive, index ); + + /* Compute the child seed value */ + hss_seed_derive_set_j( &derive, SEED_CHILD_SEED ); + hss_seed_derive( seed, &derive, true ); + /* True sets the j value to SEED_CHILD_I */ + + /* Compute the child I value; with increment_j set to true in the */ + /* above call, derive has been set to the SEED_CHILD_I position */ + unsigned char postimage[ SEED_LEN ]; + hss_seed_derive( postimage, &derive, false ); + memcpy( I, postimage, I_LEN ); + + hss_seed_derive_done( &derive ); + + return true; +} + +void hss_init_extra_info( struct hss_extra_info *p ) { + if (p) memset( p, 0, sizeof *p ); +} + +void hss_extra_info_set_threads( struct hss_extra_info *p, int num_threads ) { + if (p) p->num_threads = num_threads; +} + +bool hss_extra_info_test_last_signature( struct hss_extra_info *p ) { + if (!p) return false; + return p->last_signature; +} + +enum hss_error_code hss_extra_info_test_error_code( struct hss_extra_info *p ) { + if (!p) return hss_error_got_null; + return p->error_code; +} diff --git a/src/sig_stateful/lms/external/hss.h b/src/sig_stateful/lms/external/hss.h new file mode 100644 index 0000000000..b4e5e1698d --- /dev/null +++ b/src/sig_stateful/lms/external/hss.h @@ -0,0 +1,417 @@ +#if !defined(HSS_H_) +#define HSS_H_ + +#include +#include +#include "common_defs.h" + +/* + * This is intended to be a usable (nontoy) implementation of the LMS + * signature scheme. The public data (public keys, signatures) are + * precisely the same as the standard LMS implmentation; however it + * strives to be more usable, in the following ways: + * + * - During signature generation time, it incrementally computes the next + * trees; that means that it doesn't need to generate the next Merkle tree + * from scratch on the 1025th signature. + * - It doesn't try to hold the entire Merkle tree in memory; hence a level + * 25 Merkle tree doesn't need to save 2**25 internal node values. This + * does increase the time to generate the next siganture (as we will need + * to recompute some internal nodes); however by only a small constant factor + * - It divides the private key into three parts, only one of which needs to + * be kept secret, and updated dynamically; the other parts are a working + * copy (that can be kept in RAM, and can be dynamically regenerated as + * needed), and some optional static (nonprivate) data (which can speed up + * the regeneration process) + * - API to explicitly reserve the next N signatures (so that we don't need + * to update the secure storage copy quite as often) + * + * + * We use a nonflat memory structure for the working_key. Part of the reason + * we use a flat representation elsewhere is so that they can be written (and + * later read) to/from disk as required; we specifically assume that the + * working_key is never written to disk. And, being able to use C structures + * makes this rather nontrivial structure a bit more transparent + * + * Here is the intended order of usage: + * Step 1: generate the private/public keypair: + * The API to do this is hss_generate_private_key; this is done once per + * private key; and you should write the private key to secure storage + * (which the passed update_private_key function could do) + * + * Step 2: (which you can do per restart): + * Load the private keypair into memory: hss_load_private_key + * + * Step 3: generate signatures (which you can do lots of time after you've + * loaded the key into memory): + * The API to do this is hss_generate_signature. Note that this needs + * to update the private key state; the update_private_key function pointer + * can be useful here + * + * Step 4: (when you're done with the loaded private key; optional) + * Free the ephemeral copy (hss_free_working_key). Note that this is not + * required for correctness; this just does a free() + * + * + * One can also verify signatures at any time; all that needs is a public + * key, a signature and a message; it's not a part of the intended order + * of usage + */ + +struct hss_extra_info; + +/* + * This will generate a fresh (unadorned) private key, with the selected + * parameter set, the corresponding public key, and (optionally) the aux_data + * that is associated with the private key. + * + * The generate_random function will be called when this function needs + * random values; it is assumed to generate cryptographically secure ones. + * We ask you to pass a function, rather than an array of random values, + * to emphasize that we really do need fresh random data here; the security + * of this entire system depends on it. + * + * levels, lm_type, lm_ots_type is the parameter set for the new key. + * levels is the number of levels in the HSS hierarchy (1-8), while + * lm_type[], lm_ots_type[] are arrays giving the parameter set of each + * individual level; level i of the hierarchy will have LMS parameter set + * lm_type[i] and OTS parameter set lm_ots_type[i] (where i=0 is the topmost + * Merkle tree. + * + * The update_private_key function will be called when the private key is + * generated; it is expected to write the private key to secure storage (and + * the context pointer is a value that is passed to the update_private_key + * function; it can be used to tell the update_private_key function where + * in the secure storage to place the key). If the passed update_private_key + * function pointer is NULL, the private will will be written to the context + * pointer (which is expected to hold 48 bytes of data) + * + * public_key is where the freshly generated public key will be placed, and + * len_public_key is the size of the array (and this will generate an error + * if the public key is larger than the array). See the hss_get_public_key_len + * function for the expected length of the public key + * + * aux_data is where to place internal nodes of the Merkle tree, and + * len_aux_data is the length of the provided buffer. This aux_data + * is optional (pass in a NULL if it's not being used), but does significantly + * speed the generate_working_key process. It's envisioned use is to write + * this aux_data to disk, and reread it when it's time to regenerate the + * ephemeral key; it need not be kept in secure storage; revealing it doesn't + * help an attacker to generate forgeries, and if an attacker does manage to + * corrupt it, the regeneration process will detect the corruption and ignore + * it. Also, even if writing it to disk is not possible, passing in a + * small array here and passing that to the initial regeneration call will + * speed that up (and later ones can omit it; those will go slow, but at + * least you got the speed up benefit the first time). + * + * One slightly tricky thing about aux data is that the required length of the + * aux data; there are several different possible time/memory trade-offs. + * Depending on the length, we'll automatically pick the fastest option that + * fits. If we have N bytes available total, see hss_get_aux_data_len for + * the amount of data we'll actually use (and so the amount you need to write + * to disk) + */ +bool hss_generate_private_key( + bool (*generate_random)(void *output, size_t length), + unsigned levels, + const param_set_t *lm_type, const param_set_t *lm_ots_type, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + unsigned char *public_key, size_t len_public_key, + unsigned char *aux_data, size_t len_aux_data, + struct hss_extra_info *info); + +/* + * This is the routine to load a private key into memory, and + * initialize the working data structures; these data structures + * allow us to generate signtures quickly + * + * The read_private_key is a function to read the private key from secure + * storage, with context being a value passed to that function. + * If the read_private_key pointer is NULL, we assume that the context + * pointer points to the private key. + * This assumes that the key has already been generated by + * hss_generate_private_key + * + * memory_target is a value which gives a goal for the amount of memory (in + * bytes) that this structure should take up. There are a number of + * time/memory trade-offs possible; the function uses this parameter as a + * guide as to what trade-offs it should take. This structure tries to + * allocate no more than memory_target bytes; however it is considered + * advisatory; this function will never fail beccause memory_target was too + * small (so passing 0 will work, and will minimize the memory used) + * + * aux_data points to a buffer containing the auxiliary data generated + * during the key generation process, with len_aux_data being the length + * of the buffer. Passing it a NULL means that we're not providing that + * data (which is fine; it just means this will take longer) + * + * On success, this malloc's the ephemeral key (struct hss_working_key*) and + * retursn it. Because it mallocs it, it asssumes that the caller will + * eventually free it (via the hss_free_working_key function, don't try + * calling free() yourself) + */ +struct hss_working_key; +struct hss_working_key *hss_load_private_key( + bool (*read_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + size_t memory_target, + const unsigned char *aux_data, size_t len_aux_data, /* Optional */ + struct hss_extra_info *info); + +/* + * Corresponding function to free the working key + */ +void hss_free_working_key( struct hss_working_key * ); + +/* + * This will actually generate a signature + * + * working_key is the key that has been allocated by allocate_working_key and + * initialied by hss_generate_working_key + * + * The update_private_key function will be called when the private key is + * updated; it is expected to write the private key to secure storage (and the + * context pointer is a value that is passed to the update_private_key + * function; it can be used to tell the update_private_key function where + * in the secure storage to place the key). And, if it is NULL, the context + * is expected to point to a copy of the private_key in RAM. + * One distinction is that, on an update, len_private_key will be 8; + * the update_private_key can choose to update only the first 8 bytes + * of the private key (the rest will be unchanged), or write all + * 48 bytes (private_key will point to the full 48 byte value) + * + * message, message_len are the message being signed + * + * signature is where the signature will be written, with signature_len being + * the length of the buffer. See the hss_get_signature_len function for the + * expected signature length for this parameter set; if signature_len is too + * short for the signature to fit, this will fail. + */ +bool hss_generate_signature( + struct hss_working_key *working_key, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + const void *message, size_t message_len, + unsigned char *signature, size_t signature_len, + struct hss_extra_info *info); + +/* + * See hss_verify.h for the signature verfication routine; it's in a + * separate file for those programs that only need to verify a signature + */ +#include "hss_verify.h" + +/* + * Lower level routines to allocate and initialize a working key. + * + * hss_load_working_key will do the work of the below routines; these are + * provided separately in case you need more control (e.g. reuse an already + * allocated working key) + * + * First, the routine to allocate (but not initialize) a working key. + * + * The levels/lm_type/lm_ots_type are the same parameter sets as in the + * generate public/private keypair call; the parameter set must match the + * values for the private key. + * + * memory_target is a value which gives a goal for the amount of memory that + * this structure should take up. There are a number of time/memory trade-offs + * possible; the function uses this parameter as a guide as to what trade-offs + * it should take. This structure tries to allocate no more than memory_target + * bytes; however it is considered advisatory; this function will never fail + * beccause memory_target was too small (so passing 0 will work, and will + * minimize the memory used) + */ +struct hss_working_key *allocate_working_key( + unsigned levels, + const param_set_t *lm_type, const param_set_t *lm_ots_type, + size_t memory_target, + struct hss_extra_info *info); + +/* + * This is called on reload (or initial key generation), it'll take the + * working key that's been allocated by allocate_working_key, and initialize + * it based on the private key; this working key is what we need to actually + * generate signatures. + * + * The read_private_key is a function to read the private key from secure + * storage, with context being a value passed to that function. + * If NULL, we assume that the context pointer points to the private key + * + * aux_data points to a buffer containing the auxiliary data generated + * during the key generation process, with len_aux_data being the length + * of the buffer. Passing it a NULL means that we're not providing that + * data (which is fine; it just means this will take longer) + * + * working_key is a pointer to the allocated working key + */ +bool hss_generate_working_key( + bool (*read_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + const unsigned char *aux_data, size_t len_aux_data, /* Optional */ + struct hss_working_key *working_key, + struct hss_extra_info *info); + +/* + * This will make sure that (at least) N signatures are reserved; that is, we + * won't need to actually call the update function for the next N signatures + * generated + * + * This can be useful if the update_private_key function is expensive. + * + * Note that if, N (or more) signatures are already reserved, this won't do + * anything. + */ +bool hss_reserve_signature( + struct hss_working_key *w, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + unsigned sigs_to_reserve, + struct hss_extra_info *info); + +/* + * This will set the autoreserve, so that when the signing process runs out, + * it will automatically reserve N more signatures (in addition to the one + * that is being used for the current signature) + * + * This can be useful if the update_private_key function is expensive, + * setting sigs_to_autoreserve=99 means will actually update the private + * key once every 100 signatures + */ +bool hss_set_autoreserve( + struct hss_working_key *w, + unsigned sigs_to_autoreserve, + struct hss_extra_info *info); + +/* + * This returns the required lengths for the various objects we export + * + * This is the length of the private key (which is written to secure storage) + */ +size_t hss_get_private_key_len(unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type); +#define HSS_MAX_PRIVATE_KEY_LEN (8 + 8 + SEED_LEN + 16) + +/* + * This include file has the functions that contains the lengths of the other + * public objects + */ +#include "hss_common.h" + +/* + * Get the signature length. We don't put this in hss_common because we + * assume we have a loaded private key + * Returns 0 on error + */ +size_t hss_get_signature_len_from_working_key( + struct hss_working_key *working_key); + +/* + * This returns the amount of aux data we use + * This is slightly different from the above routines; given the bound on the + * amount of data the aux_data is allowed to take (max_length), this returns + * the amount of data we'll actually use + */ +size_t hss_get_aux_data_len(size_t max_length, + unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type); + +/* + * This returns the parameter set for a given private key. + * This is here to solve a chicken-and-egg problem: the hss_working_key + * must be initialized to the same parameter set as the private key, + * but (other than this function, or somehow remembering it) there's + * no way to retreive the parameter set. + * + * read_private_key/context will read the private key (if read_private_key is + * NULL, context is assumed to point to the private key) + * + * On success, *levels will be set to the number of levels, and lm_type[] + * and lm_ots_type[] will be set to the lm/ots parameter sets + * + * On success, this returns true; on failure (can't read the private key, or + * the * private key is invalid), returns false + */ +bool hss_get_parameter_set( unsigned *levels, + param_set_t lm_type[ MAX_HSS_LEVELS ], + param_set_t lm_ots_type[ MAX_HSS_LEVELS ], + bool (*read_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context); + +enum hss_error_code { + hss_error_none = 0, /* I don't know nothing about any error */ + + hss_range_normal_failures, /* There errors happen during normal use */ + /* of the signature scheme */ + hss_error_bad_signature, /* Invalid signature */ + hss_error_private_key_expired, /* This private key has generated all */ + /* the signatures it is allowed */ + hss_error_not_that_many_sigs_left, /* Reservation request failed */ + /* because the key couldn't do that many */ + /* signatures */ + + hss_range_bad_parameters, /* These errors are cause by the */ + /* application passing in a bad parameter */ + hss_error_no_randomness, /* No RNG supplied */ + hss_error_bad_param_set, /* Application asked for an illegal parmaeter */ + /* set */ + hss_error_buffer_overflow, /* Buffer provide not big enough */ + hss_error_got_null, /* Application passed in a NULL pointer */ + hss_error_bad_aux, /* Error with provided aux buffer */ + hss_error_no_private_buffer, /* Application didn't provide a place */ + /* to put the private key */ + hss_error_incompatible_param_set, /* The parameter set of the working */ + /* set didn't agree with the private key */ + hss_error_key_uninitialized, /* The working key used had never been */ + /* initialized with a private key */ + hss_error_key_mismatch, /* The working set and the private key */ + /* do not correspond */ + hss_error_ctx_uninitialized, /* The incremental ctx wasn't initialized */ + /* properly */ + hss_error_ctx_already_used, /* The ctx has already been used */ + hss_error_bad_public_key, /* Somehow, we got an invalid public key */ + + hss_range_processing_error, /* These errors are cause by an */ + /* error while processing */ + hss_error_bad_randomness, /* The RNG claimed failure */ + hss_error_private_key_write_failed, /* The write of the private key */ + /* to NVRAM failed */ + hss_error_private_key_read_failed, /* The read of the private key */ + /* from NVRAM failed */ + hss_error_out_of_memory, /* A malloc failure caused us to fail */ + + hss_range_my_problem, /* These are caused by internal errors */ + /* within the HSS implementation */ + hss_error_internal, /* Some internal assertion failed (should */ + /* never happen) */ +}; + +/* + * This is the structure that allows us to pass noncritical information + * to and from the above routines (without requiring us to add each + * one as an additional parameter + */ +struct hss_extra_info { + int num_threads; /* Number of threads we're allowed to ues */ + bool last_signature; /* Set if we just signed the last signature */ + /* allowed by this private key */ + enum hss_error_code error_code; /* The more recent error detected */ +}; + +/* Accessor APIs in case someone doesn't feel comfortable about reaching */ +/* into the structure */ +void hss_init_extra_info( struct hss_extra_info * ); +void hss_extra_info_set_threads( struct hss_extra_info *, int ); +bool hss_extra_info_test_last_signature( struct hss_extra_info * ); +enum hss_error_code hss_extra_info_test_error_code( struct hss_extra_info * ); + +#endif /* HSS_H_ */ diff --git a/src/sig_stateful/lms/external/hss_alloc.c b/src/sig_stateful/lms/external/hss_alloc.c new file mode 100644 index 0000000000..ea435ffa7f --- /dev/null +++ b/src/sig_stateful/lms/external/hss_alloc.c @@ -0,0 +1,555 @@ +/* + * This is the code which allocates a working key (and initializes the fields + * that are independent of the key) + */ +#include +#include +#include +#include "hss.h" +#include "hss_internal.h" +#include "lm_common.h" + +#define MALLOC_OVERHEAD 8 /* Our simplistic model about the overhead */ + /* that malloc takes up is that it adds 8 */ + /* bytes to any request we make. This isn't */ + /* precise (especially if we consider external */ + /* fragmentation), it's just a guideline */ + +/* + * Function to estimate the amount of memory we'd use at a particular level, + * if we went with a particular subtree size + * - i is which tree in the scheme we're talking about; 0 is the root tree + * We have this because we allocate less for the root tree + * - subtree_size is the size of the subtrees we're considering + * - total_length is the size of the trees + * - size_hash is the length of the hash output (always 32 currently) + * - if psubtree_levels is non-NULL, we'll return the number of subtree levels + * here + * - if pstack_total is non-NULL, we'll return the bytes of stack space needed + * by the subtrees of this level here + * The value returned is the amount of space used by the merkle + * level structures, the subtree structures, plus the additional stack + * space required + */ +static size_t compute_level_memory_usage(int i, unsigned subtree_size, + unsigned total_height, unsigned size_hash, + unsigned *psubtree_levels, + size_t *pstack_total) { + /* Compute the number of subtree levels we'd have */ + unsigned subtree_levels = (total_height + subtree_size - 1) / subtree_size; + unsigned top_subtree_size = total_height - (subtree_levels-1)*subtree_size; + /* The top level tree has no next subtrees */ + int have_next_subtree = (i == 0) ? 0 : 1; + size_t stack_total = 0; + + /* Compute the memory this would use */ + size_t memory_used = sizeof(struct merkle_level) + MALLOC_OVERHEAD; + unsigned j; + for (j=0; j 2 +#error We assume that a subtree of size 2 is allowed +#endif + return 2; +} + +/* + * This allocates a working key for a particular parameter set, and sets up + * the data fields that are key independent; it doesn't set anything that + * does depend on the key. memory_target is used to guide time/memory + * trade-offs; it's the target memory budget that we try to stay below if + * possible + */ +struct hss_working_key *allocate_working_key( + unsigned levels, + const param_set_t *lm_type, const param_set_t *lm_ots_type, + size_t memory_target, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + + if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) { + info->error_code = hss_error_bad_param_set; + return 0; + } + + /* Assign the memory target to a *signed* variable; signed so that it */ + /* can take on negative values meaningfully (to account for cases where */ + /* we are "overbudget") */ + signed long mem_target; + if (memory_target > LONG_MAX) { + mem_target = LONG_MAX; + } else { + mem_target = memory_target; + } +#if 0 +signed long initial_mem_target = mem_target; /* DEBUG HACK */ +#endif + + struct hss_working_key *w = malloc( sizeof *w ); + if (!w) { + info->error_code = hss_error_out_of_memory; + return NULL; + } + mem_target -= sizeof(*w) + MALLOC_OVERHEAD; + unsigned i; + w->levels = levels; + w->status = hss_error_key_uninitialized; /* Not usable until we see a */ + /* private key */ + w->autoreserve = 0; + + /* Initialize all the allocated data structures to NULL */ + /* We do this up front so that if we hit an error in the middle, we can */ + /* just free everything */ + for (i=0; isigned_pk[i] = NULL; + } + for (i=0; itree[i] = NULL; + } + w->stack = NULL; + + /* Allocate all the memory for the level signatures */ + size_t signature_len = 4; /* At the same time, ocmpute the sig length */ + for (i=0; i < levels; i++) { + w->siglen[i] = lm_get_signature_len( lm_type[i], lm_ots_type[i] ); + signature_len += w->siglen[i]; + /* Size of this level's Merkle public key */ + size_t pklen = lm_get_public_key_len(lm_type[i]); + if (i != 0) signature_len += pklen; + if (w->siglen[i] == 0) { + hss_free_working_key(w); + info->error_code = hss_error_bad_param_set; + return 0; + } + /* We don't need a allocate a signature for the topmost */ + if (i == 0) continue; + + w->signed_pk_len[i] = w->siglen[i-1] + pklen; + + w->signed_pk[i] = malloc( w->signed_pk_len[i] ); + if (!w->signed_pk[i]) { + hss_free_working_key(w); + info->error_code = hss_error_out_of_memory; + return 0; + } + mem_target -= w->signed_pk_len[i] + MALLOC_OVERHEAD; + } + w->signature_len = signature_len; + + /* Also account for the overhead for the stack allocation (the memory */ + /* used by the stack will be accounted as a part of the tree level size */ + mem_target -= MALLOC_OVERHEAD; + + /* + * Plot out how many subtree sizes we have at each level. We start by + * computing how much memory we'd use if we minimize each level + */ + unsigned subtree_size[MAX_HSS_LEVELS]; + unsigned subtree_levels[MAX_HSS_LEVELS]; + unsigned level_hash[MAX_HSS_LEVELS]; + unsigned level_height[MAX_HSS_LEVELS]; + unsigned hash_size[MAX_HSS_LEVELS]; + unsigned total_height = 0; + + /* Parse the parameter sets */ + for (i=0; ierror_code = hss_error_bad_param_set; + return 0; + } + + total_height += level_height[i]; /* Also track the number of */ + /* signatures we can generate with this parm set */ + } + + /* + * Select which subtree sizes that is faster, and fit within the memory + * we've been given. For the nonbottom levels, we always use what's the + * smallest for that particular tree height; there's no point in wasting + * extra memory to make them faster (in that each one can be done during + * the time the bottom level BUILDING subtrees don't need updating). + */ + size_t stack_usage = 0; + for (i=0; i mem_target) { + /* This would use more memory than we'd like; accept it if */ + /* either we have no solution, or it uses less memory than what */ + /* we've seen */ + if (search_status != nothing_yet && mem > best_mem) continue; + + /* This solution is the best so far (however, it doesn't fit) */ + search_status = found_overbudget; + } else { + /* This is within our budget; accept it if we haven't seen a */ + /* previous solution within our budget, or this uses fewer */ + /* levels than the previous solution */ + if (search_status == found_plenty_memory) { + if (sub_levels > best_levels) { + /* We've already seen a faster solution */ + continue; + } + if (sub_levels == best_levels && mem > best_mem) { + /* We've already seen an equally fast solution that */ + /* uses less memory */ + continue; + } + } + + /* This solution is the best so far (and it fits) */ + search_status = found_plenty_memory; + } + /* This is the best option so far; record it */ + best_j = j; + best_mem = mem; + best_levels = sub_levels; + best_stack_used = stack_used; + } + + if (search_status == nothing_yet) { + /* This can't really happen */ + hss_free_working_key(w); + info->error_code = hss_error_internal; + return 0; + } +#if 0 +printf( "Allocation = %ld\n", initial_mem_target - mem_target + best_mem ); /* DEBUG HACK */ +#endif + + subtree_size[i] = best_j; + subtree_levels[i] = (level_height[i] + best_j - 1) / best_j; + stack_usage += best_stack_used; + + unsigned char *stack; + if (stack_usage == 0) { + stack = NULL; /* Hey! No stack required */ + /* Avoid the malloc, as malloc(0) is allowed to fail */ + } else { + stack = malloc(stack_usage); + if (!stack) { + hss_free_working_key(w); + info->error_code = hss_error_out_of_memory; + return 0; + } + } + w->stack = stack; + size_t stack_index = 0; + + /* + * Ok, we've figured out the sizes for everything; now do the actual + * allocations + */ + for (i = 0; ierror_code = hss_error_out_of_memory; + return 0; + } + unsigned h0 = level_height[i]; + tree->level = h0; + tree->h = level_hash[i]; + tree->hash_size = hash_size[i]; + tree->lm_type = lm_type[i]; + tree->lm_ots_type = lm_ots_type[i]; + /* We'll initialize current_index from the private key */ + tree->max_index = (1L << tree->level) - 1; + tree->sublevels = subtree_levels[i]; + tree->subtree_size = subtree_size[i]; + unsigned top_subtree_size = h0 - (subtree_levels[i]-1)*subtree_size[i]; + tree->top_subtree_size = top_subtree_size; + + unsigned k; + for (j=0; jsubtree[j][k] = NULL; + w->tree[i] = tree; + + unsigned subtree_level = 0; + unsigned levels_below = h0; + for (j=0; jerror_code = hss_error_out_of_memory; + return 0; + } + + s->level = subtree_level; + s->levels_below = levels_below; + tree->subtree[j][k] = s; + if (k == ACTIVE_TREE) { + /* Active trees don't need no stack */ + s->stack = NULL; + } else if (levels_below == 0) { + /* Bottom level subtrees don't need no stack */ + s->stack = NULL; + } else { + s->stack = &stack[stack_index]; + stack_index += hash_size[i] * levels_below; + } + } + + subtree_level += height; + } + } + +/* SANITY CHECK */ + if (stack_index != stack_usage) { + hss_free_working_key(w); + info->error_code = hss_error_internal; + return 0; + } +/* SANITY CHECK */ + + /* Compute the max number of signatures we can generate */ + if (total_height > 64) total_height = 64; /* (bounded by 2**64) */ + w->max_count = ((sequence_t)2 << (total_height-1)) - 1; /* height-1 so */ + /* we don't try to shift by 64, and hit undefined behavior */ + + /* We use the count 0xffff..ffff to signify 'we've used up all our */ + /* signatures'. Make sure that is above max_count, even for */ + /* parameter sets that can literally generate 2**64 signatures (by */ + /* letting them generate only 2**64-1) */ + if (total_height == 64) w->max_count--; + + return w; +} + +void hss_free_working_key(struct hss_working_key *w) { + int i; + if (!w) return; + for (i=0; itree[i]; + if (tree) { + unsigned j, k; + for (j=0; jsubtree[j][k]); + hss_zeroize( tree, sizeof *tree ); /* We have seeds here */ + } + free(tree); + } + for (i=0; isigned_pk[i]); + } + free(w->stack); + hss_zeroize( w, sizeof *w ); /* We have secret information here */ + free(w); +} diff --git a/src/sig_stateful/lms/external/hss_aux.c b/src/sig_stateful/lms/external/hss_aux.c new file mode 100644 index 0000000000..5817b76c81 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_aux.c @@ -0,0 +1,355 @@ +/* + * This is the implementation of the aux data within the HSS tree + */ + +#include +#include "hss_aux.h" +#include "hss_internal.h" +#include "common_defs.h" +#include "lm_common.h" +#include "endian.h" +#include "hash.h" +#include "hss_zeroize.h" + +/* + * The structure of aux data + * + * The current format of the file is: + * [4 bytes of marker]: + * - bit 31 is set (to indicate that the aux data is nonempty; a 0 first byte + * indicates that, yes, we have no bananas); because we store the marker + * in bigendian format, this bit 31 is in the first byte. + * - bit i is set if we have the hashes for intermediate level i + * For each set bit i (in ascending sequence): + * - 1<= len_this_level) { + /* This level fits; add it */ + max_length -= len_this_level; + /* We also set the MSBit to signify that we're saving something */ + aux_level |= 0x80000000UL | ((aux_level_t)1<>= 1) { + if (aux_level & 1) { + temp->data[h] = (void *)aux_data; + aux_data += (size_t)size_hash << h; + } else { + temp->data[h] = 0; /* No data at this level */ + } + } + + /* Now, check if the data is valid */ + if (w) { + /* Check to see if the data is valid */ + size_t expected_len = (aux_data - orig_aux_data) + size_hash; + if (expected_len > len_aux_data) { + /* Either the first 4 bytes were messed up, or the file was */ + /* truncated */ + return 0; + } + if (len_aux_data < 4 + size_hash) return 0; + + /* Now, MAC the entire aux file */ + union hash_context ctx; + unsigned char key[ MAX_HASH ]; + compute_seed_derive( key, w->tree[0]->h, w->working_key_seed, &ctx ); + unsigned char expected_mac[ MAX_HASH ]; + compute_hmac( expected_mac, w->tree[0]->h, size_hash, &ctx, key, + orig_aux_data, aux_data - orig_aux_data ); + hss_zeroize( key, size_hash ); + hss_zeroize( &ctx, sizeof ctx ); + if (0 != memcmp_consttime( expected_mac, aux_data, size_hash)) { + /* The MAC did not agree; ignore the aux data */ + return 0; + } + } + return temp; +} + +/* + * This returns the amount of aux data we would use, given the maximum bound + * on how much aux data we are allowed, and the parameter sets + */ +size_t hss_get_aux_data_len(size_t max_length, + unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type) { + size_t len = 0; + LMS_UNUSED(levels); + if (!hss_optimal_aux_level( max_length, lm_type, lm_ots_type, &len )) { + return 1; /* 1 byte marker to say 'we're not using it */ + } + + return len; +} + +/* + * Save the marker within the aux data + */ +void hss_store_aux_marker( unsigned char *aux_data, aux_level_t aux_level ) { + if (aux_level == 0) { + /* Aux data doesn't help; mark it as unused */ + aux_data[AUX_DATA_MARKER] = NO_AUX_DATA; + } else { + put_bigendian( &aux_data[AUX_DATA_MARKER], aux_level, 4 ); + } +} + +/* + * This is called while we are building the initial top level Merkle tree (to + * compute the root). This is called for each internal node, and allows the + * aux data a chance to save the intermediate value + */ +void hss_save_aux_data( struct expanded_aux_data *data, unsigned level, + unsigned size_hash, merkle_index_t q, + const unsigned char *cur_val ) { + if (!data) return; /* We're not recording anything */ + if (!data->data[level]) return; /* We're not recording anything for */ + /* this level */ + + /* We are recording it; save a copy in the aux data */ + memcpy( data->data[level] + size_hash * q, cur_val, size_hash ); +} + +/* + * This generates the derived value that we'll use as a key the authenticate + * the aux data. We pass the ctx (rather than using a local one) so we have + * one less thing to zeroize + * + * We use a derived key (rather than using the seed directly) because the + * outer hash within the HMAC don't use the diversification factors that every + * other hash within this packet does; hence for HMAC, we use a key that + * is independent of every other hash used + */ +static void compute_seed_derive( unsigned char *result, unsigned hash, + const unsigned char *seed, union hash_context *ctx) { + hss_init_hash_context( hash, ctx ); + unsigned char prefix[ DAUX_PREFIX_LEN ]; + memset( prefix, 0, DAUX_D ); + SET_D( prefix + DAUX_D, D_DAUX ); + hss_update_hash_context( hash, ctx, prefix, sizeof prefix ); + hss_update_hash_context( hash, ctx, seed, SEED_LEN ); + hss_finalize_hash_context( hash, ctx, result ); + + hss_zeroize( &ctx, sizeof ctx ); +} + +static void xor_key( unsigned char *key, unsigned xor_val, unsigned len_key) { + unsigned i; + for (i = 0; idata[i]) { + total_length += (size_t)size_hash << i; + if (!aux) { + aux = data->data[i] - 4; + } + } + } + if (aux) { + compute_hmac( aux+total_length, hash, size_hash, &ctx, aux_seed, + aux, total_length ); + } + + hss_zeroize( &ctx, sizeof ctx ); + hss_zeroize( aux_seed, size_hash ); +} + +/* + * This is called when we need to use aux data; it checks to see if we've + * stored the nodes within the aux data; if we have, it extracts them, + * and returns true + */ +bool hss_extract_aux_data(const struct expanded_aux_data *aux, unsigned level, + const struct hss_working_key *w, unsigned char *dest, + merkle_index_t node_offset, /* Offset of node on this level */ + merkle_index_t node_count) { /* # of nodes to restore */ + if (!aux) return false; /* No aux data */ + if (!aux->data[level]) return false; /* We don't have that specific */ + /* level saved */ + unsigned hash_size = w->tree[0]->hash_size; + + /* We do have the data; copy it to the destination */ + memcpy( dest, + aux->data[level] + node_offset*hash_size, + node_count * hash_size ); + + return true; +} diff --git a/src/sig_stateful/lms/external/hss_aux.h b/src/sig_stateful/lms/external/hss_aux.h new file mode 100644 index 0000000000..634df88684 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_aux.h @@ -0,0 +1,59 @@ +#if !defined( HSS_AUX_H_ ) +#define HSS_AUX_H_ + +/* + * This is the internal API to the subsystem that deals with aux data + * This should not be included by files outside this subsystem + */ + +#include "common_defs.h" +#include +#include + +struct hss_working_key; + +/* This is a bitmap that lists which aux levels we have */ +typedef uint_fast32_t aux_level_t; + +/* This is the expanded version of the aux data */ +struct expanded_aux_data { + unsigned char *data[ MAX_MERKLE_HEIGHT+1 ]; +}; + +/* + * These are some internal routines that handle aux data + */ +/* Internal function used to compute the optimal aux level */ +aux_level_t hss_optimal_aux_level( size_t max_length, + const param_set_t *lm_type, + const param_set_t *lm_ots_type, + size_t *actual_len ); + +/* Generate pointers into a saved aux data */ +/* If w is provided, we do sanity checking on the data within aux_data */ +struct expanded_aux_data *hss_expand_aux_data( const unsigned char *aux_data, + size_t len_aux_data, + struct expanded_aux_data *temp, unsigned size_hash, + struct hss_working_key *w ); + +/* + * Save the marker within the aux data + */ +void hss_store_aux_marker( unsigned char *aux_data, aux_level_t aux_level ); + +/* Save an intermediate node */ +void hss_save_aux_data( struct expanded_aux_data *data, unsigned level, + unsigned size_hash, merkle_index_t q, + const unsigned char *cur_val ); + +/* Do the final touches on the aux data */ +void hss_finalize_aux_data(struct expanded_aux_data *data, + unsigned size_hash, unsigned hash, + const unsigned char *seed); + +/* Get a set of intermediate nodes from the aux data */ +bool hss_extract_aux_data(const struct expanded_aux_data *aux, unsigned level, + const struct hss_working_key *w, unsigned char *dest, + merkle_index_t node_offset, merkle_index_t node_count); + +#endif /* HSS_AUX_H_ */ diff --git a/src/sig_stateful/lms/external/hss_common.c b/src/sig_stateful/lms/external/hss_common.c new file mode 100644 index 0000000000..d07261dd26 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_common.c @@ -0,0 +1,48 @@ +/* + * This is the code that is common between an HSS verifier, and a full HSS + * implementation that both signs and verifies + */ +#include +#include "common_defs.h" +#include "hss_common.h" +#include "lm_common.h" +#include "config.h" +/* + * Get the length of the public key, given this particular parameter set + */ +size_t hss_get_public_key_len(unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type) { + LMS_UNUSED(lm_ots_type); + if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) return 0; + + size_t first_pubkey = lm_get_public_key_len(lm_type[0]); + if (first_pubkey == 0) return 0; + + return 4 + first_pubkey; +} + +/* + * Get the length of a signature, given this particular parameter set + */ +size_t hss_get_signature_len(unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type) { + if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) return 0; + + unsigned i; + size_t tot_len = 4; + for (i=0; i 0 */ + if (i > 0) { + size_t next_pub_len = lm_get_public_key_len(lm_type[i]); + if (next_pub_len == 0) return 0; + tot_len += next_pub_len; + } + } + return tot_len; +} diff --git a/src/sig_stateful/lms/external/hss_common.h b/src/sig_stateful/lms/external/hss_common.h new file mode 100644 index 0000000000..c455b9af5e --- /dev/null +++ b/src/sig_stateful/lms/external/hss_common.h @@ -0,0 +1,22 @@ +#if !defined( HSS_COMMON_H_ ) +#define HSS_COMMON_H_ + +#include +#include "common_defs.h" + +/* + * This returns the length of the public key for the given parameter set + */ +size_t hss_get_public_key_len(unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type); +#define HSS_MAX_PUBLIC_KEY_LEN (4 + 8 + ((I_LEN+3) & ~3) + MAX_HASH) + +/* + * This returns the length of the signature for the given parameter set + */ +size_t hss_get_signature_len(unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type); + +#endif /* HSS_COMMON_H_ */ diff --git a/src/sig_stateful/lms/external/hss_compute.c b/src/sig_stateful/lms/external/hss_compute.c new file mode 100644 index 0000000000..353ec939fb --- /dev/null +++ b/src/sig_stateful/lms/external/hss_compute.c @@ -0,0 +1,174 @@ +/* + * This includes some computation methods that are shared between different + * subsystems of the HSS signature package + */ + +#include +#include "hss_internal.h" +#include "hss.h" +#include "hash.h" +#include "hss_thread.h" +#include "lm_ots_common.h" +#include "lm_ots.h" +#include "endian.h" +#include "hss_derive.h" + +/* Count the number of 1 bits at the end (lsbits) of the integer */ +/* Do it in the obvious way; straightline code may be faster (no */ +/* unpredictable jumps, which are costly), but that would be less scrutable */ +/* (and this code is "fast enough") */ +static int trailing_1_bits(merkle_index_t n) { + int i; + for (i=0; n&1; n>>=1, i++) + ; + return i; +} + +/* + * Compute the value of an internal node within a Merkle tree + */ +static enum hss_error_code hss_compute_internal_node( unsigned char *dest, + merkle_index_t node_num, + const unsigned char *seed, + param_set_t lm_type, + param_set_t lm_ots_type, + unsigned h, + unsigned leaf_level, + const unsigned char *I) { + unsigned hash_size = hss_hash_length(h); + + /* We're store intermediate nodes here */ + unsigned char stack[ MAX_HASH * MAX_MERKLE_HEIGHT]; + + merkle_index_t tree_size = (merkle_index_t)1 << leaf_level; + merkle_index_t r = node_num; + int levels_to_bottom = 0; + if (r == 0) return hss_error_internal; /* So no to infinite loops */ + while (r < tree_size) { + r <<= 1; + levels_to_bottom++; + } + merkle_index_t q = r - tree_size; + + merkle_index_t i; + unsigned ots_len = lm_ots_get_public_key_len(lm_ots_type); + unsigned char pub_key[ LEAF_MAX_LEN ]; + memcpy( pub_key + LEAF_I, I, I_LEN ); + SET_D( pub_key + LEAF_D, D_LEAF ); + + struct seed_derive derive; + if (!hss_seed_derive_init( &derive, lm_type, lm_ots_type, + I, seed)) { + return hss_error_bad_param_set; + } + + for (i=0;; i++, r++, q++) { + /* Generate the next OTS public key */ + hss_seed_derive_set_q( &derive, q ); + if (!lm_ots_generate_public_key(lm_ots_type, I, + q, &derive, pub_key + LEAF_PK, ots_len)) { + return hss_error_bad_param_set; /* The only reason the above */ + /* could fail */ + } + + /* + * For the subtree which this leaf node forms the final piece, put the + * destination to where we'll want it, either on the stack, or if this + * is the final piece, to where the caller specified + */ + unsigned char *current_buf; + int stack_offset = trailing_1_bits( i ); + if (stack_offset == levels_to_bottom) { + current_buf = dest; + } else { + current_buf = &stack[stack_offset * hash_size ]; + } + + /* Hash it to form the leaf node */ + put_bigendian( pub_key + LEAF_R, r, 4); + union hash_context ctx; + hss_hash_ctx( current_buf, h, &ctx, pub_key, LEAF_LEN(hash_size) ); + + /* Work up the stack, combining right nodes with the left nodes */ + /* that we've already computed */ + int sp; + for (sp = 1; sp <= stack_offset; sp++) { + hss_combine_internal_nodes( current_buf, + &stack[(sp-1) * hash_size], current_buf, + h, I, hash_size, + r >> sp ); + } + + /* We're not at a left branch, or at the target node */ + + /* Because we've set current_buf to point to where we want to place */ + /* the result of this loop, we don't need to memcpy it */ + + /* Check if this was the last leaf (and so we've just computed the */ + /* target node) */ + if (stack_offset == levels_to_bottom) { + /* We're at the target node; the node we were asked to compute */ + /* We've already placed the value into dest, so we're all done */ + break; + } + } + + hss_seed_derive_done( &derive ); + + return hss_error_none; +} + +/* + * Combine adjacent left and right nodes within the Merkle tree + * together + */ +void hss_combine_internal_nodes( unsigned char *dest, + const unsigned char *left_node, const unsigned char *right_node, + int h, const unsigned char *I, unsigned hash_size, + merkle_index_t node_num) { + unsigned char hash_val[ INTR_MAX_LEN ]; + memcpy( hash_val + INTR_I, I, I_LEN ); + put_bigendian( hash_val + INTR_R, node_num, 4 ); + SET_D( hash_val + INTR_D, D_INTR ); + + memcpy( hash_val + INTR_PK, left_node, hash_size ); + memcpy( hash_val + INTR_PK + hash_size, right_node, hash_size ); + union hash_context ctx; + hss_hash_ctx( dest, h, &ctx, hash_val, INTR_LEN(hash_size) ); +} + +/* + * This computes an array of intermediate Merkle nodes given by data + * This may be run in a worker (non-main) thread + */ +void hss_gen_intermediate_tree(const void *data, + struct thread_collection *col) { + const struct intermed_tree_detail *d = data; + unsigned hash_len = hss_hash_length(d->h); + unsigned i; + + for (i=0; inode_count; i++) { + unsigned char result[ MAX_HASH ]; + enum hss_error_code status = hss_compute_internal_node( result, + d->node_num + i, + d->seed, + d->lm_type, + d->lm_ots_type, + d->h, + d->tree_height, + d->I); + + /* Report the results */ + hss_thread_before_write(col); + if (status == hss_error_none) { + /* Copy out the resulting hash */ + memcpy( d->dest + i*hash_len, result, hash_len ); + } else { + /* Something went wrong; report the bad news */ + *d->got_error = status; + hss_thread_after_write(col); /* No point in working more */ + return; + } + hss_thread_after_write(col); + } +} diff --git a/src/sig_stateful/lms/external/hss_derive.c b/src/sig_stateful/lms/external/hss_derive.c new file mode 100644 index 0000000000..fc8833594a --- /dev/null +++ b/src/sig_stateful/lms/external/hss_derive.c @@ -0,0 +1,325 @@ +/* + * This is the file that contains the routines that generate various 'random' + * values from the master seed. + * + * Values generated by this routine: + * - OTS private keys + * - Message randomizers (the random value we hash with the message when we + * sign it) + * - I values + * - SEED values (which are the secret to derive all the above for a specific + * LMS tree) + * + * We do things determanisticly, rather than picking things from random, so + * that if we reload from scratch, the values we use after the reload are + * consistent with what we used previously + * + * This provides several different possible derivation methods; they can be + * selected by setting SECRET_METHOD in config.h + */ +#include +#include "hss_derive.h" +#include "hss_internal.h" +#include "hash.h" +#include "endian.h" +#include "config.h" + +#if SECRET_METHOD == 2 + /* We use a hash function based on the parameter set */ +#include "lm_common.h" /* To get the prototype for the parameter set -> */ + /* hash function mapping */ +#else +#if SEED_LEN == 32 +#define HASH HASH_SHA256 /* We always use SHA-256 to derive seeds */ +#else +#error We need to define a hash function for this seed length +#endif +#endif + +#if SECRET_METHOD == 0 || SECRET_METHOD == 2 +/* + * This is the method of deriving LM-OTS keys that conforms to the + * Appendix A method + * As you can see, it's fairly simple + */ + +/* This creates a seed derivation object */ +bool hss_seed_derive_init( struct seed_derive *derive, + param_set_t lm, param_set_t ots, + const unsigned char *I, const unsigned char *seed ) { + derive->I = I; + derive->master_seed = seed; + LMS_UNUSED(ots); + /* q, j will be set later */ +#if SECRET_METHOD == 2 + /* Grab the hash function to use */ + if (!lm_look_up_parameter_set(lm, &derive->hash, &derive->m, 0)) { + return false; + } + + /* Note: currently, this assumes that the hash length is always 256 */ + /* bits; error out if that isn't the case */ + if (derive->m != SEED_LEN) { + return false; + } +#endif + + return true; +} + +/* This sets the internal 'q' value for seed derivation object */ +void hss_seed_derive_set_q( struct seed_derive *derive, merkle_index_t q ) { + derive->q = q; +} + +/* This sets the internal 'j' value for seed derivation object */ +void hss_seed_derive_set_j( struct seed_derive *derive, unsigned j ) { + derive->j = j; +} + + +/* This derives the current seed value. If increment_j is set, it'll then */ +/* reset the object to the next j value */ +void hss_seed_derive( unsigned char *seed, struct seed_derive *derive, + bool increment_j ) { + unsigned char buffer[ PRG_MAX_LEN ]; + memcpy( buffer + PRG_I, derive->I, I_LEN ); + put_bigendian( buffer + PRG_Q, derive->q, 4 ); + put_bigendian( buffer + PRG_J, derive->j, 2 ); + buffer[PRG_FF] = 0xff; + memcpy( buffer + PRG_SEED, derive->master_seed, SEED_LEN ); + +#if SECRET_METHOD == 2 + int hash = derive->hash; /* Our the parameter set's hash function */ +#else + int hash = HASH; /* Use our standard one */ +#endif + + hss_hash( seed, hash, buffer, PRG_LEN(SEED_LEN) ); + + hss_zeroize( buffer, PRG_LEN(SEED_LEN) ); + + if (increment_j) derive->j += 1; +} + +/* This is called when we're done with a seed derivation object */ +void hss_seed_derive_done( struct seed_derive *derive ) { + /* No secrets here */ + LMS_UNUSED(derive); +} + +#elif SECRET_METHOD == 1 +/* + * This is a method of deriving LM-OTS keys that tries to be more + * side-channel resistant; in particular, we never include any + * specific secret value in more than 2**SECRET_MAX distinct + * hashes. + * We do this by deriving subseeds using a tree-based structure; + * each node in the tree has up to 2**SECRET_MAX children, and we use any + * seed within the node (including the root) in no other hash. + * We actually have two levels of trees; one based on q (Merkle tree index), + * the other based on j (Winternitz digit); we could design a single level + * tree that could incorporate both, but it'd be more complex + * + * Much of the complexity that does exist is there to avoid recomputation + */ +#include "lm_common.h" +#include "lm_ots_common.h" +static unsigned my_log2(merkle_index_t n); + +/* This creates a seed derivation object */ +bool hss_seed_derive_init( struct seed_derive *derive, + param_set_t lm, param_set_t ots, + const unsigned char *I, const unsigned char *seed ) { + derive->I = I; + derive->master_seed = seed; + + /* These parameter sets will define the size of the trees we'll use */ + unsigned height, p; + if (!lm_look_up_parameter_set(lm, 0, 0, &height) || + !lm_ots_look_up_parameter_set(ots, 0, 0, 0, &p, 0)) { + return false; + } + + p += NUM_ARTIFICIAL_SEEDS; /* We use one artifical value for the */ + /* randomizer and two artificial values to generate seed, I */ + /* for child trees */ + + /* Compute the number of r-levels we have */ + derive->q_levels = (height + SECRET_MAX - 1)/SECRET_MAX; + + /* And which bit to set when converting 'q' to 'r' */ + derive->r_mask = (merkle_index_t)1 << height; + + /* Compute the number of j-levels we have */ + unsigned j_height = my_log2(p); + derive->j_levels = (j_height + SECRET_MAX - 1)/SECRET_MAX; + + /* And which bit to set when writing q values into the hash */ + derive->j_mask = 1 << j_height; + + /* We reset the current 'q' value to am impossible value; we do this so */ + /* that the initial 'q' value given to use by the application will */ + /* rebuild the entire path through the tree */ + derive->q = derive->r_mask; + + return true; +} + +/* This sets the internal 'q' value for seed derivation object */ +/* This also updates our internal q-path (the q_index/q_seed arrays) */ +/* to reflect the new 'q' value, while minimizing the number of hashes */ +/* done (by reusing as much of the previous path as possible) */ +void hss_seed_derive_set_q( struct seed_derive *derive, merkle_index_t q ) { + merkle_index_t change = q ^ derive->q; + derive->q = q; + unsigned bits_change = my_log2(change); + unsigned q_levels = derive->q_levels; + + /* levels_change will be the number of levels of the q-tree we'll */ + /* need to recompute */ + unsigned levels_change = (bits_change + SECRET_MAX - 1) / SECRET_MAX; + if (levels_change > q_levels) levels_change = q_levels; + + int i; + union hash_context ctx; + unsigned char buffer[ QTREE_MAX_LEN ]; + merkle_index_t r = q | derive->r_mask; + + for (i = levels_change; i > 0; i--) { + int j = q_levels - i; + int shift = (i-1) * SECRET_MAX; + + memcpy( buffer + QTREE_I, derive->I, I_LEN ); + put_bigendian( buffer + QTREE_Q, r >> shift, 4 ); + SET_D( buffer + QTREE_D, D_QTREE ); + if (j == 0) { + memcpy( buffer + QTREE_SEED, derive->master_seed, SEED_LEN ); + } else { + memcpy( buffer + QTREE_SEED, derive->q_seed[j-1], SEED_LEN ); + } + + hss_hash_ctx( derive->q_seed[j], HASH, &ctx, buffer, QTREE_LEN ); + } + + hss_zeroize( buffer, PRG_LEN(SEED_LEN) ); + hss_zeroize( &ctx, sizeof ctx ); +} + +/* Helper function to recompute the j_seed[i] value, based on the */ +/* j_value[i] already set */ +/* ctx, buffer are passed are areas this function can use; we reuse those */ +/* areas so we need to zeroize those buffers only once */ +static void set_j_seed( struct seed_derive *derive, int i, + union hash_context *ctx, unsigned char *buffer) { + + memcpy( buffer + PRG_I, derive->I, I_LEN ); + put_bigendian( buffer + PRG_Q, derive->q, 4 ); + put_bigendian( buffer + PRG_J, derive->j_value[i], 2 ); + buffer[PRG_FF] = 0xff; + if (i == 0) { + /* The root of this tree; it gets its seed from the bottom level */ + /* of the q-tree */ + memcpy( buffer + PRG_SEED, derive->q_seed[ derive->q_levels-1], + SEED_LEN ); + } else { + /* Non-root node; it gets its seed from its parent */ + memcpy( buffer + PRG_SEED, derive->j_seed[i-1], SEED_LEN ); + } + + hss_hash_ctx( derive->j_seed[i], HASH, ctx, buffer, PRG_LEN(SEED_LEN) ); +} + +/* This sets the internal 'j' value for seed derivation object */ +/* This computes the entire path to the 'j' value. Because this is used */ +/* immediately after resetting the q value, we don't try to reuse the */ +/* previous hashes (as there won't be anything there we could reuse) */ +/* Note that we don't try to take advantage of any preexisting hashes */ +/* in the j_seed array; we don't bother because this function is typically */ +/* used only immediately after a set_q call, and so there aren't any */ +/* hashes we could take advantage of */ +void hss_seed_derive_set_j( struct seed_derive *derive, unsigned j ) { + int i; + unsigned j_levels = derive->j_levels; + unsigned shift = SECRET_MAX * j_levels; + + unsigned j_mask = derive->j_mask; + j &= j_mask-1; /* Set the high-order bit; clear any bits above that */ + j |= j_mask; /* This ensures that when we do the hashes, that the */ + /* prefix for the hashes at two different levels of the */ + /* tree are distinct */ + + union hash_context ctx; + unsigned char buffer[ PRG_MAX_LEN ]; + + for (i = 0; ij_value[i] = (j >> shift); + set_j_seed( derive, i, &ctx, buffer ); + } + + hss_zeroize( &ctx, sizeof ctx ); + hss_zeroize( buffer, PRG_LEN(SEED_LEN) ); +} + +/* This derives the current seed value (actually, we've already computed */ +/* it); we just need to copy it to the buffer) */ +/* If increment_j is set, it'll then reset the object to the next j value */ +/* (which means incrementally computing that path) */ +void hss_seed_derive( unsigned char *seed, struct seed_derive *derive, + bool increment_j ) { + memcpy( seed, derive->j_seed[ derive->j_levels - 1], SEED_LEN ); + + if (increment_j) { + int i; + + /* Update the j_values, and figure out which hashes we'll need */ + /* to recompute */ + for (i = derive->j_levels-1;; i--) { + unsigned index = derive->j_value[i]; + index += 1; + derive->j_value[i] = index; + if (0 != (index & SECRET_MAX_MASK)) { + /* The increment didn't cause a carry to the next level; */ + /* we can stop propogating the increment here (and we */ + /* also know this is the top level that we need to */ + /* recompute the hashes */ + break; + } + if (i == 0) { + /* This is the top level; stop here */ + break; + } + } + + /* Recompute the hashes that need updating; we need to do it */ + /* top-down, as each hash depends on the previous one */ + union hash_context ctx; + unsigned char buffer[ PRG_MAX_LEN ]; + for (; i < derive->j_levels; i++) { + set_j_seed( derive, i, &ctx, buffer ); + } + hss_zeroize( &ctx, sizeof ctx ); + hss_zeroize( buffer, PRG_LEN(SEED_LEN) ); + } +} + +/* This is called when we're done with a seed derivation object */ +/* This makes sure any secret values are zeroized */ +void hss_seed_derive_done( struct seed_derive *derive ) { + /* These values are secret, and should never be leaked */ + hss_zeroize( derive->q_seed, sizeof derive->q_seed ); + hss_zeroize( derive->j_seed, sizeof derive->j_seed ); +} + +static unsigned my_log2(merkle_index_t n) { + unsigned lg; + for (lg = 0; n > 0; lg++) n >>= 1; + return lg; +} + +#else + +#error Unknown secret method + +#endif diff --git a/src/sig_stateful/lms/external/hss_derive.h b/src/sig_stateful/lms/external/hss_derive.h new file mode 100644 index 0000000000..ee47eb6cfc --- /dev/null +++ b/src/sig_stateful/lms/external/hss_derive.h @@ -0,0 +1,74 @@ +#if !defined( HSS_DERIVE_H_ ) +#define HSS_DERIVE_H_ + +#include "common_defs.h" + +#include "config.h" + +#if SECRET_MAX > 31 +#error The code is not designed for a SECRET_MAX that high +#endif +#define SECRET_MAX_MASK (((merkle_index_t)1 << SECRET_MAX) - 1) + +struct seed_derive { + const unsigned char *I; + const unsigned char *master_seed; + merkle_index_t q; + unsigned j; +#if SECRET_METHOD == 2 + unsigned hash; /* Hash function to use */ + unsigned m; /* Length of hash function */ +#endif + +#if SECRET_METHOD == 1 + unsigned q_levels, j_levels; + merkle_index_t r_mask; + unsigned j_mask; +#define MAX_Q_HEIGHT ((MAX_MERKLE_HEIGHT + SECRET_MAX - 1) / SECRET_MAX) +#define MAX_J_HEIGHT (( 9 + SECRET_MAX - 1) / SECRET_MAX) + /* '9' is the number of bits a maximum 'p' can take up */ + + unsigned j_value[MAX_J_HEIGHT]; /* these are the values we insert */ + /* into the hash. The lower SECRET_MAX bits are which child of */ + /* the parent it is; the higher bits indicate the parents' */ + /* identities */ + + unsigned char q_seed[MAX_Q_HEIGHT][SEED_LEN]; + unsigned char j_seed[MAX_Q_HEIGHT][SEED_LEN]; +#endif +}; + +bool hss_seed_derive_init( struct seed_derive *derive, + param_set_t lm, param_set_t ots, + const unsigned char *I, const unsigned char *seed ); + +/* This sets the internal 'q' value */ +/* If we've already have a 'q' value set, it'll try to minimize the number */ +/* of hashes done */ +/* Once you've done that, you'll need to reset the 'h' */ +void hss_seed_derive_set_q( struct seed_derive *derive, merkle_index_t q ); + +/* This sets the internal 'j' value */ +void hss_seed_derive_set_j( struct seed_derive *derive, unsigned j ); + +#define NUM_ARTIFICIAL_SEEDS 3 /* 3 seeds are listed below */ + /* This is the j value used when we're deriving the seed value */ + /* for child Merkle trees */ +#define SEED_CHILD_SEED (~1) + /* This is the j value used when we're deriving the I value */ + /* used; either in the context of the parent tree, or of this tree */ +#define SEED_CHILD_I (SEED_CHILD_SEED + 1) + /* This is the j value used when we're asking for the randomizer C */ + /* for signing a message */ +#define SEED_RANDOMIZER_INDEX (~2) + +/* This generates the current seed. If increment_j is set, this will set */ +/* up for the next j value */ +void hss_seed_derive( unsigned char *seed, struct seed_derive *derive, + bool increment_j ); + +/* This needs to be called when we done with a seed_derive */ +/* That structure contains keying data, this makes sure those are cleaned */ +void hss_seed_derive_done( struct seed_derive *derive ); + +#endif /* HSS_DERIVE_H_ */ diff --git a/src/sig_stateful/lms/external/hss_generate.c b/src/sig_stateful/lms/external/hss_generate.c new file mode 100644 index 0000000000..be2c991081 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_generate.c @@ -0,0 +1,930 @@ +/* + * This is the routine that generates the ephemeral ("working") key from the + * short private value. It builds all the various current, building and + * next subtrees for the various levels (to at least the extent required + * for the current count within the key). + * + * The code is made considerably more complex because we try to take + * advantage of parallelism. To do this, we explicitly list the parts + * of the subtrees we need to build (which is most of the computation), and + * have different worker threads build the various parts, + * + * However, it turns out that this is sometimes insufficient; sometimes, + * the work consists of one or two expensive nodes (perhaps the top level + * subtree), and a lot of comparatively cheap ones; in this case, we'd have + * most of our threads go through the cheap ones quickly, and have one or + * two threads working on the expensive one, and everyone will end up waiting + * for that. To mitigate that, we attempt to subdivide the most expensive + * requests; instead of having a single thread computing the expensive node, + * we may issue four or eight threads to compute the nodes two or three + * levels below (and have the main thread do the final computation when + * all the threads are completed). + * + * This works out pretty good; however man does add complexity :-( + */ +#include +#include +#include "hss.h" +#include "hss_internal.h" +#include "hss_aux.h" +#include "hash.h" +#include "hss_thread.h" +#include "hss_reserve.h" +#include "lm_ots_common.h" +#include "endian.h" + +#define DO_FLOATING_POINT 1 /* If clear, we avoid floating point operations */ + /* You can turn this off for two reasons: */ + /* - Your platform doesn't implement floating point */ + /* - Your platform is single threaded (we use floating point to figure */ + /* out how to split up tasks between threads; if the same thread */ + /* will do all the work, dividing it cleverly doesn't buy anything */ + /* (and that's a quite a bit of code that gets eliminated) */ + /* On the other hand, if you are threaded, you'd really want this if */ + /* at all possible; without this, one thread ends up doing the bulk of */ + /* the work, and so we end up going not that much faster than single */ + /* threaded mode */ + +/* + * This routine assumes that we have filled in the bottom node_count nodes of + * the subtree; it tries to compute as many internal nodes as possible + */ +static void fill_subtree(const struct merkle_level *tree, + struct subtree *subtree, + merkle_index_t node_count, + const unsigned char *I) { + if (node_count <= 1) return; /* If we can't compute any more nodes, */ + /* don't bother trying */ + unsigned h_subtree = (subtree->level == 0) ? tree->top_subtree_size : + tree->subtree_size; + + /* Index into the node array where we're starting */ + merkle_index_t lower_index = ((merkle_index_t)1 << h_subtree) - 1; + + unsigned hash_size = tree->hash_size; + + /* The node identier (initially of the bottom left node of the */ + /* subtree */ + merkle_index_t node_id = (((merkle_index_t)1 << tree->level) + + subtree->left_leaf) + >> subtree->levels_below; + + /* Fill in as many levels of internal nodes as possible */ + int sublevel; + for (sublevel = h_subtree-1; sublevel >= 0; sublevel--) { + node_count >>= 1; + if (node_count == 0) break; /* Can't do any more */ + merkle_index_t prev_lower_index = lower_index; + lower_index >>= 1; + node_id >>= 1; + + merkle_index_t i; + for (i=0; inodes[ hash_size *(lower_index + i)], + &subtree->nodes[ hash_size *(prev_lower_index + 2*i)], + &subtree->nodes[ hash_size *(prev_lower_index + 2*i+1)], + tree->h, I, hash_size, + node_id + i); + } + } +} + +/* + * This routine takes the 2**num_level hashes, and computes up num_level's, + * returning the value of the top node. This is sort of like fill_tree, + * except that it returns only the top node, not the intermediate ones + * One warning: this does modify the passed value of hashes; our current + * caller doesn't care about that. + */ +static void hash_subtree( unsigned char *dest, + unsigned char *hashes, + unsigned num_level, merkle_index_t node_index, + unsigned hash_size, + int h, const unsigned char *I) { + + /* Combine the nodes to form the tree, until we get to the two top nodes */ + /* This will overwrite the hashes array; that's OK, because we don't */ + /* need those anymore */ + for (; num_level > 1; num_level--) { + unsigned i; + merkle_index_t this_level_node_index = node_index << (num_level-1); + for (i = 0; i < (1<<(num_level-1)); i++) { + hss_combine_internal_nodes( + &hashes[ hash_size * i ], + &hashes[ hash_size * (2*i) ], + &hashes[ hash_size * (2*i + 1) ], + h, I, hash_size, + this_level_node_index + i); + } + } + + /* Combine the top two nodes to form our actual target */ + hss_combine_internal_nodes( + dest, + &hashes[ 0 ], + &hashes[ hash_size ], + h, I, hash_size, + node_index); +} + +#if DO_FLOATING_POINT +/* + * This structure is a note reminding us that we've decided to split this + * init_order into several requests, which can be run on independent threads + */ +struct sub_order { + unsigned num_hashes; /* The number of hashes this suborder is */ + /* split up into */ + unsigned level; /* Levels deep into the tree we go */ + merkle_index_t node_num_first_target; /* The node number of the left */ + /* most hash that we're standing in for */ + unsigned char h[1]; /* The hashes go here; we'll malloc */ + /* enough space to let them fit */ +}; +#endif + +/* + * This is an internal request to compute the bottom N nodes (starting from the + * left) of a subtree (and to contruct the internal nodes that based solely on + * those N leaf nodes) + */ +struct init_order { + const struct merkle_level *tree; + struct subtree *subtree; + merkle_index_t count_nodes; /* # of bottom level nodes we need to */ + /* generate */ + const unsigned char *prev_node; /* For nonbottom subtrees, sometimes one */ + /* of the nodes is the root of the */ + /* next level subtree that we compute in */ + /* its entirety. If so, this is a pointer */ + /* to where we will find the precomputed */ + /* value. This allows us to avoid */ + /* computing that specific node */ + merkle_index_t prev_index; /* This is the index of the */ + /* precomputed node, where 0 is the */ + /* leftmost bottom node of this subtree */ + char next_tree; /* If clear, we do this on the current */ + /* tree level (seed, I values); if set, */ + /* we do this on the next */ + char already_computed_lower; /* If set, we've already computed the */ + /* lower nodes (and all we need to do is */ + /* fill the upper); no need to ask the */ + /* threads do do anything */ + /* We may still need to build the */ + /* interiors of the subtrees, of course */ +#if DO_FLOATING_POINT + float cost; /* Approximate number of hash compression */ + /* operations per node */ + struct sub_order *sub; /* If non-NULL, this gives details on how */ + /* we want to subdivide the order between */ + /* different threads */ +#endif +}; + +#if DO_FLOATING_POINT + /* This comparison function sorts the most expensive orders first */ +static int compare_order_by_cost(const void *a, const void *b) { + const struct init_order *p = a; + const struct init_order *q = b; + + if (p->cost > q->cost) return -1; + if (p->cost < q->cost) return 1; + + return 0; +} +#else + /* This comparison function sorts the higher level subtrees first */ +static int compare_order_by_subtree_level(const void *a, const void *b) { + const struct init_order *p = a; + unsigned p_subtree = p->subtree->level; + const struct init_order *q = b; + unsigned q_subtree = q->subtree->level; + + if (p_subtree < q_subtree) return -1; + if (p_subtree > q_subtree) return 1; + + return 0; +} +#endif + +#if DO_FLOATING_POINT +static float estimate_total_cost(struct init_order *order, + unsigned count_order); + +/* + * This is a simple minded log function, returning an int. Yes, using the + * built-in log() function would be easier, however I don't want to pull in + * the -lm library just for this + */ +static unsigned my_log2(float f) { +#define MAX_LOG 10 + unsigned n; + for (n=1; f > 2 && n < MAX_LOG; n++) + f /= 2; + return n; +} +#endif + +/* + * This is the point of this entire file. + * + * It fills in an already allocated working key, based on the private key + */ +bool hss_generate_working_key( + bool (*read_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + const unsigned char *aux_data, size_t len_aux_data, /* Optional */ + struct hss_working_key *w, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + + if (!w) { + info->error_code = hss_error_got_null; + return false; + } + w->status = hss_error_key_uninitialized; /* In case we detect an */ + /* error midway */ + + if (!read_private_key && !context) { + info->error_code = hss_error_no_private_buffer; + return false; + } + + /* Read the private key */ + unsigned char private_key[ PRIVATE_KEY_LEN ]; + if (read_private_key) { + if (!read_private_key( private_key, PRIVATE_KEY_LEN, context)) { + info->error_code = hss_error_private_key_read_failed; + goto failed; + } + } else { + memcpy( private_key, context, PRIVATE_KEY_LEN ); + } + + /* + * Make sure that the private key and the allocated working key are + * compatible; that the working_key was initialized with the same + * parameter set + */ + { + if (w->levels > MAX_HSS_LEVELS) { + info->error_code = hss_error_internal; + goto failed; + } + unsigned char compressed[PRIVATE_KEY_PARAM_SET_LEN]; + param_set_t lm_type[MAX_HSS_LEVELS], lm_ots_type[MAX_HSS_LEVELS]; + unsigned i; + for (i=0; ilevels; i++) { + lm_type[i] = w->tree[i]->lm_type; + lm_ots_type[i] = w->tree[i]->lm_ots_type; + } + + if (!hss_compress_param_set( compressed, w->levels, + lm_type, lm_ots_type, + sizeof compressed )) { + /* We're passed an unsupported param set */ + info->error_code = hss_error_internal; + goto failed; + } + if (0 != memcmp( private_key + PRIVATE_KEY_PARAM_SET, compressed, + PRIVATE_KEY_PARAM_SET_LEN )) { + /* The working set was initiallized with a different parmset */ + info->error_code = hss_error_incompatible_param_set; + goto failed; + } + } + + sequence_t current_count = get_bigendian( + private_key + PRIVATE_KEY_INDEX, PRIVATE_KEY_INDEX_LEN ); + if (current_count > w->max_count) { + info->error_code = hss_error_private_key_expired; /* Hey! We */ + goto failed; /* can't generate any more signatures */ + } + hss_set_reserve_count(w, current_count); + + memcpy( w->private_key, private_key, PRIVATE_KEY_LEN ); + + /* Initialize all the levels of the tree */ + + /* Initialize the current count for each level (from the bottom-up) */ + sequence_t i; + sequence_t count = current_count; + for (i = w->levels - 1; i >= 0 ; i--) { + struct merkle_level *tree = w->tree[i]; + unsigned index = count & tree->max_index; + count >>= tree->level; + tree->current_index = index; + } + + /* Initialize the I values */ + for (i = 0; i < w->levels; i++) { + struct merkle_level *tree = w->tree[i]; + + /* Initialize the I, I_next elements */ + if (i == 0) { + /* The root seed, I value is derived from the secret key */ + hss_generate_root_seed_I_value( tree->seed, tree->I, + private_key+PRIVATE_KEY_SEED ); + /* We don't use the I_next value */ + } else { + /* The seed, I is derived from the parent's values */ + + /* Where we are in the Merkle tree */ + struct merkle_level *parent = w->tree[i-1]; + merkle_index_t index = parent->current_index; + + hss_generate_child_seed_I_value( tree->seed, tree->I, + parent->seed, parent->I, + index, parent->lm_type, + parent->lm_ots_type ); + /* The next seed, I is derived from either the parent's I */ + /* or the parent's next value */ + if (index == tree->max_index) { + hss_generate_child_seed_I_value( tree->seed_next, tree->I_next, + parent->seed_next, parent->I_next, + 0, parent->lm_type, + parent->lm_ots_type); + } else { + hss_generate_child_seed_I_value( tree->seed_next, tree->I_next, + parent->seed, parent->I, + index+1, parent->lm_type, + parent->lm_ots_type); + } + } + } + + /* Generate the expanded aux data structure (or NULL if we don't have a */ + /* viable aux structure */ + struct expanded_aux_data *expanded_aux, temp_aux; + expanded_aux = hss_expand_aux_data( aux_data, len_aux_data, &temp_aux, + w->tree[0]->hash_size, w ); + + /* + * Now, build all the subtrees within the tree + * + * We initialize the various data structures, and create a list of + * the nodes on the bottom levels of the subtrees that need to be + * initialized + */ + /* There are enough structures in this array to handle the maximum */ + /* number of orders we'll ever see */ + struct init_order order[MAX_HSS_LEVELS * MAX_SUBLEVELS * NUM_SUBTREE]; + struct init_order *p_order = order; + int count_order = 0; + + /* Step through the levels, and for each Merkle tree, compile a list of */ + /* the orders to initialize the bottoms of the subtrees that we'll need */ + for (i = w->levels - 1; i >= 0 ; i--) { + struct merkle_level *tree = w->tree[i]; + unsigned hash_size = tree->hash_size; + /* The current count within this tree */ + merkle_index_t tree_count = tree->current_index; + /* The index of the leaf we're on */ + merkle_index_t leaf_index = tree_count; + + /* Generate the active subtrees */ + int j; + /*int bot_level_subtree = (int)tree->level;*/ /* The level of the bottom of */ + /* the subtree */ + unsigned char *active_prev_node = 0; + unsigned char *next_prev_node = 0; + for (j=tree->sublevels-1; j>=0; j--) { + /* The height of this subtree */ + int h_subtree = (j == 0) ? tree->top_subtree_size : + tree->subtree_size; + + /* Initialize the active tree */ + struct subtree *active = tree->subtree[j][ACTIVE_TREE]; + + /* Total number of leaf nodes below this subtree */ + merkle_index_t size_subtree = (merkle_index_t)1 << + (h_subtree + active->levels_below); + /* Fill in the leaf index that's on the left side of this subtree */ + /* This is the index of the leaf that we did when we first */ + /* entered the active subtree */ + merkle_index_t left_leaf = leaf_index & ~(size_subtree - 1); + /* This is the number of leaves we've done in this subtree */ + merkle_index_t subtree_count = leaf_index - left_leaf; + /* If we're not in the bottom tree, it's possible that the */ + /* update process will miss the very first update before we */ + /* need to sign. To account for that, generate one more */ + /* node than what our current count would suggest */ + if (i != w->levels - 1) { + subtree_count++; + } + active->current_index = 0; + active->left_leaf = left_leaf; + merkle_index_t num_bottom_nodes = (merkle_index_t)1 << h_subtree; + + /* Check if we have aux data at this level */ + int already_computed_lower = 0; + if (i == 0) { + merkle_index_t lower_index = num_bottom_nodes-1; + merkle_index_t node_offset = active->left_leaf>>active->levels_below; + if (hss_extract_aux_data(expanded_aux, active->level+h_subtree, + w, &active->nodes[ hash_size * lower_index ], + node_offset, num_bottom_nodes)) { + /* We do have it precomputed in our aux data */ + already_computed_lower = 1; + } + } + /* No aux data at this level; schedule the bottom row to be computed */ + /* Schedule the creation of the entire active tree */ + p_order->tree = tree; + p_order->subtree = active; + p_order->count_nodes = (merkle_index_t)1 << h_subtree; /* All */ + /* the nodes in this subtree */ + p_order->next_tree = 0; + /* Mark the root we inherented from the subtree just below us */ + p_order->prev_node = already_computed_lower ? NULL : active_prev_node; + p_order->prev_index = (tree->current_index >> active->levels_below) & (num_bottom_nodes-1); + + p_order->already_computed_lower = already_computed_lower; + p_order++; count_order++; + + /* For the next subtree, here's where our root will be */ + active_prev_node = &active->nodes[0]; + + /* And initialize the building tree, assuming there is one, and */ + /* assuming that the active subtree isn't at the right edge of */ + /* the Merkle tree */ + if (j > 0 && (leaf_index + size_subtree <= tree->max_index )) { + struct subtree *building = tree->subtree[j][BUILDING_TREE]; + + /* The number of leaves that make up one bottom node */ + /* of this subtree */ + merkle_index_t size_below_tree = (merkle_index_t)1 << building->levels_below; + /* We need to initialize the building tree current index */ + /* to a value at least as large as subtree_count */ + /* We'd prefer not to have to specificallly initialize */ + /* the stack, and so we round up to the next place the */ + /* stack is empty */ + merkle_index_t building_count = + (subtree_count + size_below_tree - 1) & + ~(size_below_tree - 1); + /* # of bottom level nodes we've building right now */ + merkle_index_t num_nodes = building_count >> building->levels_below; + building->left_leaf = left_leaf + size_subtree; + building->current_index = building_count; + + /* Check if this is already in the aux data */ + already_computed_lower = 0; + if (i == 0) { + merkle_index_t lower_index = num_bottom_nodes-1; + merkle_index_t node_offset = building->left_leaf>>building->levels_below; + if (hss_extract_aux_data(expanded_aux, building->level+h_subtree, + w, &building->nodes[ hash_size * lower_index ], + node_offset, num_nodes)) { + /* We do have it precomputed in our aux data */ + already_computed_lower = 1; + } + } + + /* Schedule the creation of the subset of the building tree */ + p_order->tree = tree; + p_order->subtree = building; + /* # of nodes to construct */ + p_order->count_nodes = num_nodes; + p_order->next_tree = 0; + /* We generally can't use the prev_node optimization */ + p_order->prev_node = NULL; + p_order->prev_index = 0; + + p_order->already_computed_lower = already_computed_lower; + p_order++; count_order++; + } else if (j > 0) { + tree->subtree[j][BUILDING_TREE]->current_index = 0; + } + + /* And the NEXT_TREE (which is always left-aligned) */ + if (i > 0) { + struct subtree *next = tree->subtree[j][NEXT_TREE]; + next->left_leaf = 0; + merkle_index_t leaf_size = + (merkle_index_t)1 << next->levels_below; + + merkle_index_t next_index = tree_count; + /* If we're not in the bottom tree, it's possible that the */ + /* update process will miss the very first update before we */ + /* need to sign. To account for that, potetially generate */ + /* one more node than what our current count would suggest */ + if (i != w->levels - 1) { + next_index++; + } + + /* Make next_index the # of leaves we'll need to process to */ + /* forward this NEXT subtree to this state */ + next_index = (next_index + leaf_size - 1)/leaf_size; + + /* This is set if we have a previous subtree */ + merkle_index_t prev_subtree = (next->levels_below ? 1 : 0); + merkle_index_t num_nodes; + unsigned char *next_next_node = 0; + + /* If next_index == 1, then if we're on a nonbottom subtree */ + /* the previous subtree is still building (and so we */ + /* needn't do anything). The exception is if we're on the */ + /* bottom level, then there is no subtree, and so we still */ + /* need to build the initial left leaf */ + if (next_index <= prev_subtree) { + /* We're not started on this subtree yet */ + next->current_index = 0; + num_nodes = 0; + } else if (next_index < num_bottom_nodes) { + /* We're in the middle of building this tree */ + next->current_index = next_index << next->levels_below; + num_nodes = next_index; + } else { + /* We've completed building this tree */ + /* How we note "we've generated this entire subtree" */ + next->current_index = MAX_SUBINDEX; + num_nodes = num_bottom_nodes; + /* We've generated this entire tree; allow it to */ + /* be inhereited for the next one */ + next_next_node = &next->nodes[0]; + } + if (num_nodes > 0) { + /* Schedule the creation of these nodes */ + p_order->tree = tree; + p_order->subtree = next; + /* # of nodes to construct */ + p_order->count_nodes = num_nodes; + p_order->next_tree = 1; + p_order->prev_node = next_prev_node; + p_order->prev_index = 0; + + p_order->already_computed_lower = 0; + p_order++; count_order++; + } + next_prev_node = next_next_node; + } + +// bot_level_subtree -= h_subtree; + } + } + +#if DO_FLOATING_POINT + /* Fill in the cost estimates */ + for (i=0; i<(sequence_t)count_order; i++) { + p_order = &order[i]; + + /* + * While we're here, NULL out all the suborders; we'll fill them in + * later if necessary + */ + p_order->sub = 0; + if (p_order->already_computed_lower) { + /* If we pulled the data from the aux, no work required */ + p_order->cost = 0; + continue; + } + unsigned winternitz = 8; + unsigned p = 128; + (void)lm_ots_look_up_parameter_set(p_order->tree->lm_ots_type, 0, 0, + &winternitz, &p, 0); + + struct subtree *subtree = p_order->subtree; + unsigned levels_below = subtree->levels_below; + + /* + * Estimate the number of hashes that we'll need to compute to compute + * one node; this is the number of leaf nodes times the number of + * hashes used during a winternitz computation. This ignores a few + * other hashes, but gets the vast bulk of them + */ + p_order->cost = (float)((merkle_index_t)1<num_threads); + if (num_tracks == 0) num_tracks = 4; /* Divide by 0; just say no */ + float est_max_per_work_item = est_total / num_tracks; + + /* Scan through the items, and see which ones should be subdivided */ + for (i=0; i<(sequence_t)count_order; i++) { + p_order = &order[i]; + if (p_order->cost <= est_max_per_work_item) { + break; /* Break because once we hit this point, the rest of the */ + /* items will be cheaper */ + } + + /* Try to subdivide each item into subdiv pieces */ + unsigned subdiv = my_log2(p_order->cost / est_max_per_work_item); + struct subtree *subtree = p_order->subtree; + /* Make sure we don't try to subdivide lower than what the */ + /* Merkle tree structure allows */ + if (subdiv > subtree->levels_below) subdiv = subtree->levels_below; + if (subdiv == 0) continue; + merkle_index_t max_subdiv = (merkle_index_t)1 << subtree->levels_below; + if (subdiv > max_subdiv) subdiv = max_subdiv; + if (subdiv <= 1) continue; + + const struct merkle_level *tree = p_order->tree; + size_t hash_len = tree->hash_size; + merkle_index_t count_nodes = p_order->count_nodes; + size_t total_hash = (hash_len * count_nodes) << subdiv; + unsigned h_subtree = (subtree->level == 0) ? tree->top_subtree_size : + tree->subtree_size; + struct sub_order *sub = malloc( sizeof *sub + total_hash ); + if (!sub) continue; /* On malloc failure, don't bother trying */ + /* to subdivide */ + + /* Fill in the details of this suborder */ + sub->level = subdiv; + sub->num_hashes = 1 << subdiv; + sub->node_num_first_target = + (subtree->left_leaf >> subtree->levels_below) + + ((merkle_index_t)1 << (h_subtree + subtree->level)); + p_order->sub = sub; + } +#endif + + /* Now, generate all the nodes we've listed in parallel */ + struct thread_collection *col = hss_thread_init(info->num_threads); + enum hss_error_code got_error = hss_error_none; + + /* We use this to decide the granularity of the requests we make */ +#if DO_FLOATING_POINT + unsigned core_target = 5 * hss_thread_num_tracks(info->num_threads); + float prev_cost = 0; +#endif + + for (i=0; i<(sequence_t)count_order; i++) { + p_order = &order[i]; + if (p_order->already_computed_lower) continue; /* If it's already */ + /* done, we needn't bother */ + /* If this work order is cheaper than what we've issued, allow */ + /* for a greater amount of consolidation */ +#if DO_FLOATING_POINT + if (prev_cost > 0) { + if (p_order->cost <= 2 * prev_cost) { + /* The cost per node has decreased by a factor of 2 (at */ + /* least); allow a single core to do more of the work */ + float ratio = prev_cost / p_order->cost; + if (ratio > 1000) { + core_target = 1; + } else { + core_target = core_target / ratio; + if (core_target == 0) core_target = 1; + } + prev_cost = p_order->cost; + } + } else { + prev_cost = p_order->cost; + } +#endif + + const struct merkle_level *tree = p_order->tree; + struct subtree *subtree = p_order->subtree; + unsigned h_subtree = (subtree->level == 0) ? tree->top_subtree_size : + tree->subtree_size; + merkle_index_t lower_index = ((merkle_index_t)1 << h_subtree) - 1; + unsigned hash_size = tree->hash_size; +#if DO_FLOATING_POINT + unsigned max_per_request = p_order->count_nodes / core_target; + if (max_per_request == 0) max_per_request = 1; +#else + unsigned max_per_request = UINT_MAX; +#endif + + /* If we're skipping a value, make sure we compute up to there */ + merkle_index_t right_side = p_order->count_nodes; + if (p_order->prev_node && right_side > p_order->prev_index) { + right_side = p_order->prev_index; + } + + merkle_index_t n; + struct intermed_tree_detail detail; + + detail.seed = (p_order->next_tree ? tree->seed_next : tree->seed); + detail.lm_type = tree->lm_type; + detail.lm_ots_type = tree->lm_ots_type; + detail.h = tree->h; + detail.tree_height = tree->level; + detail.I = (p_order->next_tree ? tree->I_next : tree->I); + detail.got_error = &got_error; + +#if DO_FLOATING_POINT + /* Check if we're actually doing a suborder */ + struct sub_order *sub = p_order->sub; + if (sub) { + /* Issue all the orders separately */ + unsigned hash_len = tree->hash_size; + for (n = 0; n < p_order->count_nodes; n++ ) { + if (n == right_side) continue; /* Skip the omitted value */ + unsigned char *dest = &sub->h[ n * sub->num_hashes * hash_len ]; + merkle_index_t node_num = (sub->node_num_first_target+n) << sub->level; + unsigned k; + for (k=0; k < sub->num_hashes; k++) { + detail.dest = dest; + dest += hash_len; + detail.node_num = node_num; + node_num++; + detail.node_count = 1; + + hss_thread_issue_work(col, hss_gen_intermediate_tree, + &detail, sizeof detail ); + } + } + continue; + } +#endif + { + /* We're not doing a suborder; issue the request in as large of */ + /* a chunk as we're allowed */ + for (n = 0; n < p_order->count_nodes; ) { + merkle_index_t this_req = right_side - n; + if (this_req > max_per_request) this_req = max_per_request; + if (this_req == 0) { + /* We hit the value we're skipping; skip it, and go on to */ + /* the real right side */ + n++; + right_side = p_order->count_nodes; + continue; + } + + /* Issue a work order for the next this_req elements */ + detail.dest = &subtree->nodes[ hash_size * (lower_index + n)]; + detail.node_num = (subtree->left_leaf >> subtree->levels_below) + + n + ((merkle_index_t)1 << (h_subtree + subtree->level)); + detail.node_count = this_req; + + hss_thread_issue_work(col, hss_gen_intermediate_tree, + &detail, sizeof detail ); + + n += this_req; + } + } + } + + /* We've issued all the order; now wait until all the work is done */ + hss_thread_done(col); + if (got_error != hss_error_none) { + /* One of the worker threads detected an error */ +#if DO_FLOATING_POINT + /* Don't leak suborders on an intermediate error */ + for (i=0; i<(sequence_t)count_order; i++) { + free( order[i].sub ); + } +#endif + info->error_code = got_error; + goto failed; + } + +#if DO_FLOATING_POINT + /* + * Now, if we did have suborders, recombine them into what was actually + * wanted + */ + for (i=0; i<(sequence_t)count_order; i++) { + p_order = &order[i]; + struct sub_order *sub = p_order->sub; + if (!sub) continue; /* This order wasn't subdivided */ + + const struct merkle_level *tree = p_order->tree; + const unsigned char *I = (p_order->next_tree ? tree->I_next : tree->I); + struct subtree *subtree = p_order->subtree; + unsigned hash_size = tree->hash_size; + unsigned h_subtree = (subtree->level == 0) ? tree->top_subtree_size : + tree->subtree_size; + merkle_index_t lower_index = ((merkle_index_t)1 << h_subtree) - 1; + + merkle_index_t n; + for (n = 0; n < p_order->count_nodes; n++ ) { + if (p_order->prev_node && n == p_order->prev_index) continue; + + hash_subtree( &subtree->nodes[ hash_size * (lower_index + n)], + &sub->h[ hash_size * sub->num_hashes * n ], + sub->level, sub->node_num_first_target + n, + hash_size, tree->h, I); + } + + free( sub ); + p_order->sub = 0; + } +#endif + + /* + * Now we have generated the lower level nodes of the subtrees; go back and + * fill in the higher level nodes. + * We do this in backwards order, so that we do the lower levels of the trees + * first (as lower levels are cheaper, they'll be listed later in the + * array; that's how we sorted, them, remember?). + * That means if any subtrees inherit the root values of lower trees, + * we compute those root values first + */ + for (i=count_order; i>0; i--) { + p_order = &order[i-1]; + const struct merkle_level *tree = p_order->tree; + const unsigned char *I = (p_order->next_tree ? tree->I_next : tree->I); + struct subtree *subtree = p_order->subtree; + + if (p_order->prev_node) { + /* This subtree did have a bottom node that was the root node */ + /* of a lower subtree; fill it in */ + unsigned hash_size = tree->hash_size; + unsigned h_subtree = (subtree->level == 0) ? tree->top_subtree_size : + tree->subtree_size; + merkle_index_t lower_index = ((merkle_index_t)1 << h_subtree) - 1; + + /* Where in the subtree we place the previous root */ + unsigned set_index = (lower_index + p_order->prev_index) * hash_size; + memcpy( &subtree->nodes[ set_index ], p_order->prev_node, hash_size ); + } + + /* Now, fill in all the internal nodes of the subtree */ + fill_subtree(tree, subtree, p_order->count_nodes, I); + } + + /* + * Hey; we've initialized all the subtrees (at least, as far as what + * they'd be expected to be given the current count); hurray! + */ + + /* + * Now, create all the signed public keys + * Again, we could parallelize this; it's also fast enough not to be worth + * the complexity + */ + for (i = 1; i < w->levels; i++) { + if (!hss_create_signed_public_key( w->signed_pk[i], w->siglen[i-1], + w->tree[i], w->tree[i-1], w )) { + info->error_code = hss_error_internal; /* Really shouldn't */ + /* happen */ + goto failed; + } + } + hss_zeroize( private_key, sizeof private_key ); + + /* + * And, we make each level as not needing an update from below (as we've + * initialized them as already having the first update) + */ + for (i = 0; i < w->levels - 1; i++) { + w->tree[i]->update_count = UPDATE_DONE; + } + + w->status = hss_error_none; /* This working key has been officially */ + /* initialized, and now can be used */ + return true; + +failed: + hss_zeroize( private_key, sizeof private_key ); + return false; +} + +#if DO_FLOATING_POINT +/* + * This goes through the order, and estimates the total amount + * This assumes that the highest cost element is listed first + * + * It returns the estimated number of hash compression operations total + * + * We use floating point because the number of hash compression functions can + * vary a *lot*; floating point has great dynamic range. + */ +static float estimate_total_cost( struct init_order *order, + unsigned count_order ) { + if (count_order == 0) return 0; + float total_cost = 0; + + unsigned i; + + for (i=0; i +#include "common_defs.h" +#include "hss.h" +#include "config.h" + +/* + * This is the central internal include file for the functions that make up + * this subsystem. It should not be used by applications + */ + +#define PARAM_SET_COMPRESS_LEN 1 /* We assume that we can compress the */ + /* lm_type and the lm_ots type for a */ + /* single level into 1 byte */ + +#define PARM_SET_END 0xff /* We set this marker in the parameter set */ + /* when fewer than the maximum levels are used */ + + +/* + * The internal structure of a private key + */ +#define PRIVATE_KEY_INDEX 0 +#define PRIVATE_KEY_INDEX_LEN 8 /* 2**64 signatures should be enough for */ + /* everyone */ +#define PRIVATE_KEY_PARAM_SET (PRIVATE_KEY_INDEX + PRIVATE_KEY_INDEX_LEN) +#define PRIVATE_KEY_PARAM_SET_LEN (PARAM_SET_COMPRESS_LEN * MAX_HSS_LEVELS) +#define PRIVATE_KEY_SEED (PRIVATE_KEY_PARAM_SET + PRIVATE_KEY_PARAM_SET_LEN) +#if SECRET_METHOD == 2 +#define PRIVATE_KEY_SEED_LEN (SEED_LEN + I_LEN) +#else +#define PRIVATE_KEY_SEED_LEN SEED_LEN +#endif +#define PRIVATE_KEY_LEN (PRIVATE_KEY_SEED + PRIVATE_KEY_SEED_LEN) /* That's */ + /* 48 bytes */ + +struct merkle_level; +struct hss_working_key { + unsigned levels; + enum hss_error_code status; /* What is the status of this key */ + /* hss_error_none if everything looks ok */ + /* Otherwise, the error code we report if */ + /* we try to use this key to sign */ + sequence_t reserve_count; /* The value written to the private key */ + /* Will be higher than the 'current count' */ + /* if some signaures are 'reserved' */ + sequence_t max_count; /* The maximum count we can ever have */ + unsigned autoreserve; /* How many signatures to attempt to */ + /* reserve if the signing process hits */ + /* the end of the current reservation */ + + size_t signature_len; /* The length of the HSS signature */ + + unsigned char *stack; /* The stack memory used by the subtrees */ + + /* The private key (in its entirety) */ + unsigned char private_key[PRIVATE_KEY_LEN]; + /* The pointer to the seed (contained within the private key) */ + /* Warning: nonsyntaxic macro; need to be careful how we use this */ +#define working_key_seed private_key + PRIVATE_KEY_SEED + + size_t siglen[MAX_HSS_LEVELS]; /* The lengths of the signatures */ + /* generated by the various levels */ + size_t signed_pk_len[MAX_HSS_LEVELS]; /* The lengths of the signed */ + /* public keys for the various levels */ + unsigned char *signed_pk[MAX_HSS_LEVELS]; /* The current signed public */ + /* keys for the nontop levels */ + /* Each array element is that level's */ + /* current root value, signed by the */ + /* previous level. Unused for the */ + /* topmost level */ + struct merkle_level *tree[MAX_HSS_LEVELS]; /* The structures that manage */ + /* each individual level */ +}; + +#define MIN_SUBTREE 2 /* All subtrees (other than the root subtree) have */ + /* at least 2 levels */ +#define MAX_SUBLEVELS ((MAX_MERKLE_HEIGHT + MIN_SUBTREE - 1) / MIN_SUBTREE) +#if MAX_SUBLEVELS > (1 << (MIN_MERKLE_HEIGHT-1)) - 2 +#error We need to rethink our parent tree update logic, as there is a +#error possibility we do not give the tree enough updates between signatures +/* One possible fix would be to increase the subtree size for extremely */ +/* tall trees */ +#endif + +struct merkle_level { + unsigned level; /* Total number of levels */ + unsigned h, hash_size; /* Hash function, width */ + param_set_t lm_type; + param_set_t lm_ots_type; /* OTS parameter */ + merkle_index_t current_index; /* The number of signatures this tree has */ + /* generated so far */ + merkle_index_t max_index; /* 1<levels) */ + unsigned level; /* The level that the root of this subtree */ + /* is within the larger Merkle tree */ + unsigned levels_below; /* The number of levels below this subtree */ + /* in the Merkle tree */ + unsigned char *stack; /* Pointer to the stack used when */ + /* generating nodes; will be a pointer */ + /* into the hss_working_key::stack array */ + /* Used to incrementally compute bottom */ + /* node values */ + unsigned char nodes[1]; /* The actual subtree node values */ + /* 2*(1< +#include +#include "common_defs.h" +#include "hss.h" +#include "hss_internal.h" +#include "hss_aux.h" +#include "endian.h" +#include "hash.h" +#include "hss_thread.h" +#include "lm_common.h" +#include "lm_ots_common.h" + +/* Count the number of 1 bits at the end (lsbits) of the integer */ +/* Do it in the obvious way; straightline code may be faster (no */ +/* unpredictable jumps, which are costly), but that would be less scrutable */ +static int trailing_1_bits(merkle_index_t n) { + int i; + for (i=0; n&1; n>>=1, i++) + ; + return i; +} + +/* + * This creates a private key (and the correspond public key, and optionally + * the aux data for that key) + * Parameters: + * generate_random - the function to be called to generate randomness. This + * is assumed to be a pointer to a cryptographically secure rng, + * otherwise all security is lost. This function is expected to fill + * output with 'length' uniformly distributed bits, and return 1 on + * success, 0 if something went wrong + * levels - the number of levels for the key pair (2-8) + * lm_type - an array of the LM registry entries for the various levels; + * entry 0 is the topmost + * lm_ots_type - an array of the LM-OTS registry entries for the various + * levels; again, entry 0 is the topmost + * update_private_key, context - the function that is called when the + * private key is generated; it is expected to store it to secure NVRAM + * If this is NULL, then the context pointer is reinterpretted to mean + * where in RAM the private key is expected to be placed + * public_key - where to store the public key + * len_public_key - length of the above buffer; see hss_get_public_key_len + * if you need a hint. + * aux_data - where to store the optional aux data. This is not required, but + * if provided, can be used to speed up the hss_generate_working_key + * process; + * len_aux_data - the length of the above buffer. This is not fixed length; + * the function will run different time/memory trade-offs based on the + * length provided + * + * This returns true on success, false on failure + */ +bool hss_generate_private_key( + bool (*generate_random)(void *output, size_t length), + unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + unsigned char *public_key, size_t len_public_key, + unsigned char *aux_data, size_t len_aux_data, + struct hss_extra_info *info) { + + struct hss_extra_info info_temp = { 0 }; + if (!info) info = &info_temp; + + if (!generate_random) { + /* We *really* need random numbers */ + info->error_code = hss_error_no_randomness; + return false; + } + if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS) { + /* parameter out of range */ + info->error_code = hss_error_bad_param_set; + return false; + } + + unsigned h0; /* The height of the root tree */ + unsigned h; /* The hash function used */ + unsigned size_hash; /* The size of each hash that would appear in the */ + /* aux data */ + if (!lm_look_up_parameter_set(lm_type[0], &h, &size_hash, &h0)) { + info->error_code = hss_error_bad_param_set; + return false; + } + + /* Check the public_key_len */ + if (4 + 4 + 4 + I_LEN + size_hash > len_public_key) { + info->error_code = hss_error_buffer_overflow; + /* public key won't fit in the buffer we're given */ + return false; + } + + /* If you provide an aux_data buffer, we have to write something */ + /* into it (at least, enough to mark it as 'we're not really using */ + /* aux data) */ + if (aux_data && len_aux_data == 0) { + /* not enough aux data buffer to mark it as 'not really used' */ + info->error_code = hss_error_bad_aux; + return false; + } + + unsigned len_ots_pub = lm_ots_get_public_key_len(lm_ots_type[0]); + if (len_ots_pub == 0) { + info->error_code = hss_error_bad_param_set; + return false; + } + + unsigned char private_key[ PRIVATE_KEY_LEN ]; + + /* First step: format the private key */ + put_bigendian( private_key + PRIVATE_KEY_INDEX, 0, + PRIVATE_KEY_INDEX_LEN ); + if (!hss_compress_param_set( private_key + PRIVATE_KEY_PARAM_SET, + levels, lm_type, lm_ots_type, + PRIVATE_KEY_PARAM_SET_LEN )) { + info->error_code = hss_error_bad_param_set; + return false; + } + if (!(*generate_random)( private_key + PRIVATE_KEY_SEED, + PRIVATE_KEY_SEED_LEN )) { + info->error_code = hss_error_bad_randomness; + return false; + } + + /* Now make sure that the private key is written to NVRAM */ + if (update_private_key) { + if (!(*update_private_key)( private_key, PRIVATE_KEY_LEN, context)) { + /* initial write of private key didn't take */ + info->error_code = hss_error_private_key_write_failed; + hss_zeroize( private_key, sizeof private_key ); + return false; + } + } else { + if (context == 0) { + /* We weren't given anywhere to place the private key */ + info->error_code = hss_error_no_private_buffer; + hss_zeroize( private_key, sizeof private_key ); + return false; + } + memcpy( context, private_key, PRIVATE_KEY_LEN ); + } + + /* Figure out what would be the best trade-off for the aux level */ + struct expanded_aux_data *expanded_aux_data = 0, aux_data_storage; + if (aux_data != NULL) { + aux_level_t aux_level = hss_optimal_aux_level( len_aux_data, lm_type, + lm_ots_type, NULL ); + hss_store_aux_marker( aux_data, aux_level ); + + /* Set up the aux data pointers */ + expanded_aux_data = hss_expand_aux_data( aux_data, len_aux_data, + &aux_data_storage, size_hash, 0 ); + } + + unsigned char I[I_LEN]; + unsigned char seed[SEED_LEN]; + if (!hss_generate_root_seed_I_value( seed, I, private_key+PRIVATE_KEY_SEED)) { + info->error_code = hss_error_internal; + hss_zeroize( private_key, sizeof private_key ); + return false; + } + + /* Now, it's time to generate the public key, which means we need to */ + /* compute the entire top level Merkle tree */ + + /* First of all, figure out the appropriate level to compute up to */ + /* in parallel. We'll do the lower of the bottom-most level that */ + /* appears in the aux data, and 4*log2 of the number of core we have */ + unsigned num_cores = hss_thread_num_tracks(info->num_threads); + unsigned level; + unsigned char *dest = 0; /* The area we actually write to */ + void *temp_buffer = 0; /* The buffer we need to free when done */ + for (level = h0-1; level > 2; level--) { + /* If our bottom-most aux data is at this level, we want it */ + if (expanded_aux_data && expanded_aux_data->data[level]) { + /* Write directly into the aux area */ + dest = expanded_aux_data->data[level]; + break; + } + + /* If going to a higher levels would mean that we wouldn't */ + /* effectively use all the cores we have, use this level */ + if ((1<num_threads); + + struct intermed_tree_detail details; + /* Set the values in the details structure that are constant */ + details.seed = seed; + details.lm_type = lm_type[0]; + details.lm_ots_type = lm_ots_type[0]; + details.h = h; + details.tree_height = h0; + details.I = I; + enum hss_error_code got_error = hss_error_none; /* This flag is set */ + /* on an error */ + details.got_error = &got_error; + + merkle_index_t j; + /* # of nodes at this level */ + merkle_index_t level_nodes = (merkle_index_t)1 << level; + /* the index of the node we're generating right now */ + merkle_index_t node_num = level_nodes; + /* + * We'd prefer not to issue a separate work item for every node; we + * might be doing millions of node (if we have a large aux data space) + * and we end up malloc'ing a large structure for every work order. + * So, if we do have a large number of requires, aggregate them + */ + merkle_index_t increment = level_nodes / (10 * num_cores); +#define MAX_INCREMENT 20000 + if (increment > MAX_INCREMENT) increment = MAX_INCREMENT; + if (increment == 0) increment = 1; + for (j=0; j < level_nodes; ) { + unsigned this_increment; + if (level_nodes - j < increment) { + this_increment = level_nodes - j; + } else { + this_increment = increment; + } + + /* Set the particulars of this specific work item */ + details.dest = dest + j*size_hash; + details.node_num = node_num; + details.node_count = this_increment; + + /* Issue a separate work request for every node at this level */ + hss_thread_issue_work(col, hss_gen_intermediate_tree, + &details, sizeof details ); + + j += this_increment; + node_num += this_increment; + } + /* Now wait for all those work items to complete */ + hss_thread_done(col); + + hss_zeroize( seed, sizeof seed ); + + /* Check if something went wrong. It really shouldn't have, however if */ + /* something returns an error code, we really should try to handle it */ + if (got_error != hss_error_none) { + /* We failed; give up */ + info->error_code = got_error; + hss_zeroize( private_key, sizeof private_key ); + if (update_private_key) { + (void)(*update_private_key)(private_key, PRIVATE_KEY_LEN, context); + } else { + hss_zeroize( context, PRIVATE_KEY_LEN ); + } + free(temp_buffer); + return false; + } + + /* Now, we complete the rest of the tree. This is actually fairly fast */ + /* (one hash per node) so we don't bother to parallelize it */ + + unsigned char stack[ MAX_HASH * (MAX_MERKLE_HEIGHT+1) ]; + unsigned char root_hash[ MAX_HASH ]; + + /* Generate the top levels of the tree, ending with the root node */ + merkle_index_t r, leaf_node; + for (r=level_nodes, leaf_node = 0; leaf_node < level_nodes; r++, leaf_node++) { + + /* Walk up the stack, combining the current node with what's on */ + /* the atack */ + merkle_index_t q = leaf_node; + + /* + * For the subtree which this leaf node forms the final piece, put the + * destination to where we'll want it, either on the stack, or if this + * is the final piece, to where the caller specified + */ + unsigned char *current_buf; + unsigned stack_offset = trailing_1_bits( leaf_node ); + if (stack_offset == level) { + current_buf = root_hash; + } else { + current_buf = &stack[stack_offset * size_hash ]; + } + memcpy( current_buf, dest + leaf_node * size_hash, size_hash ); + + unsigned sp; + unsigned cur_lev = level; + for (sp = 1;; sp++, cur_lev--, q >>= 1) { + /* Give the aux data routines a chance to save the */ + /* intermediate value. Note that we needn't check for the */ + /* bottommost level; if we're saving aux data at that level, */ + /* we've already placed it there */ + if (sp > 1) { + hss_save_aux_data( expanded_aux_data, cur_lev, + size_hash, q, current_buf ); + } + + if (sp > stack_offset) break; + + + hss_combine_internal_nodes( current_buf, + &stack[(sp-1) * size_hash], current_buf, + h, I, size_hash, + r >> sp ); + } + } + /* The top entry in the stack is the root value (aka the public key) */ + + /* Complete the computation of the aux data */ + hss_finalize_aux_data( expanded_aux_data, size_hash, h, + private_key+PRIVATE_KEY_SEED ); + + /* We have the root value; now format the public key */ + put_bigendian( public_key, levels, 4 ); + public_key += 4; len_public_key -= 4; + put_bigendian( public_key, lm_type[0], 4 ); + public_key += 4; len_public_key -= 4; + put_bigendian( public_key, lm_ots_type[0], 4 ); + public_key += 4; len_public_key -= 4; + memcpy( public_key, I, I_LEN ); + public_key += I_LEN; len_public_key -= I_LEN; + memcpy( public_key, root_hash, size_hash ); + public_key += size_hash; len_public_key -= size_hash; + + /* Hey, what do you know -- it all worked! */ + hss_zeroize( private_key, sizeof private_key ); /* Zeroize local copy of */ + /* the private key */ + free(temp_buffer); + return true; +} + +/* + * The length of the private key + */ +size_t hss_get_private_key_len(unsigned levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type) { + /* A private key is a 'public object'? Yes, in the sense that we */ + /* export it outside this module */ + LMS_UNUSED(levels); + LMS_UNUSED(lm_type); + LMS_UNUSED(lm_ots_type); + return PRIVATE_KEY_LEN; +} diff --git a/src/sig_stateful/lms/external/hss_param.c b/src/sig_stateful/lms/external/hss_param.c new file mode 100644 index 0000000000..a1c20ab14c --- /dev/null +++ b/src/sig_stateful/lms/external/hss_param.c @@ -0,0 +1,153 @@ +#include +#include "hss.h" +#include "hss_internal.h" +#include "endian.h" +#include "hss_zeroize.h" + +/* + * Convert a parameter set into the compressed version we use within a private + * key. This is the private key that'll end up being updated constantly, and + * so we try to make it as small as possible + */ +bool hss_compress_param_set( unsigned char *compressed, + int levels, + const param_set_t *lm_type, + const param_set_t *lm_ots_type, + size_t len_compressed ) { + int i; + + for (i=0; i 0x0e || b > 0x0e) return false; + /* Make sure the parm sets are supported */ + switch (a) { + case LMS_SHA256_N32_H5: case LMS_SHA256_N32_H10: + case LMS_SHA256_N32_H15: case LMS_SHA256_N32_H20: + case LMS_SHA256_N32_H25: + break; + default: + return false; + } + switch (b) { + case LMOTS_SHA256_N32_W1: case LMOTS_SHA256_N32_W2: + case LMOTS_SHA256_N32_W4: case LMOTS_SHA256_N32_W8: + break; + default: + return false; + } + + *compressed++ = (a<<4) + b; + len_compressed--; + } + + while (len_compressed) { + *compressed++ = PARM_SET_END; + len_compressed--; + } + + return true; +} + +/* + * This returns the parameter set for a given private key. + * This is here to solve a chicken-and-egg problem: the hss_working_key + * must be initialized to the same parameter set as the private key, + * but (other than this function, or somehow remembering it) there's + * no way to retreive the parameter set. + * + * read_private_key/context will read the private key (if read_private_key is + * NULL, context is assumed to point to the private key) + * + * On success, *levels will be set to the number of levels, and lm_type[] + * and lm_ots_type[] will be set to the lm/ots parameter sets + * + * On success, this returns true; on failure (can't read the private key, or + * the private key is invalid), returns false + */ +bool hss_get_parameter_set( unsigned *levels, + param_set_t lm_type[ MAX_HSS_LEVELS ], + param_set_t lm_ots_type[ MAX_HSS_LEVELS ], + bool (*read_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context) { + unsigned char private_key[ PRIVATE_KEY_LEN ]; + bool success = false; + + if (read_private_key) { + if (!read_private_key( private_key, PRIVATE_KEY_SEED, context )) { + goto failed; + } + } else { + if (!context) return false; + memcpy( private_key, context, PRIVATE_KEY_SEED ); + } + + /* Scan through the private key to recover the parameter sets */ + unsigned total_height = 0; + unsigned level; + for (level=0; level < MAX_HSS_LEVELS; level++) { + unsigned char c = private_key[PRIVATE_KEY_PARAM_SET + level]; + if (c == PARM_SET_END) break; + /* Decode this level's parameter set */ + param_set_t lm = (c >> 4); + param_set_t ots = (c & 0x0f); + /* Make sure both are supported */ + /* While we're here, add up the total Merkle height */ + switch (lm) { + case LMS_SHA256_N32_H5: total_height += 5; break; + case LMS_SHA256_N32_H10: total_height += 10; break; + case LMS_SHA256_N32_H15: total_height += 15; break; + case LMS_SHA256_N32_H20: total_height += 20; break; + case LMS_SHA256_N32_H25: total_height += 25; break; + default: goto failed; + } + switch (ots) { + case LMOTS_SHA256_N32_W1: + case LMOTS_SHA256_N32_W2: + case LMOTS_SHA256_N32_W4: + case LMOTS_SHA256_N32_W8: + break; + default: goto failed; + } + lm_type[level] = lm; + lm_ots_type[level] = ots; + } + + if (level < MIN_HSS_LEVELS || level > MAX_HSS_LEVELS) goto failed; + + *levels = level; + + /* Make sure that the rest of the private key has PARM_SET_END */ + unsigned i; + for (i = level+1; i 64) total_height = 64; /* (bounded by 2**64) */ + sequence_t max_count = ((sequence_t)2 << (total_height-1)) - 1; + /* height-1 so we don't try to shift by 64, and hit U.B. */ + + /* We use the count 0xffff..ffff to signify 'we've used up all our */ + /* signatures'. Make sure that is above max_count, even for */ + /* parameter sets that can literally generate 2**64 signatures (by */ + /* letting them generate only 2**64-1) */ + if (total_height == 64) max_count--; + sequence_t current_count = get_bigendian( + private_key + PRIVATE_KEY_INDEX, PRIVATE_KEY_INDEX_LEN ); + + if (current_count > max_count) goto failed; /* Private key expired */ + + success = true; /* It worked! */ +failed: + /* There might be private keying material here */ + hss_zeroize( private_key, sizeof private_key ); + return success; +} diff --git a/src/sig_stateful/lms/external/hss_reserve.c b/src/sig_stateful/lms/external/hss_reserve.c new file mode 100644 index 0000000000..7ef8585560 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_reserve.c @@ -0,0 +1,194 @@ +#include +#include "common_defs.h" +#include "hss_internal.h" +#include "hss_reserve.h" +#include "endian.h" + +/* + * Initialize the reservation count to the given value + */ +void hss_set_reserve_count(struct hss_working_key *w, sequence_t count) { + w->reserve_count = count; +} + +/* + * Set the autoreserve count + */ +bool hss_set_autoreserve(struct hss_working_key *w, + unsigned sigs_to_autoreserve, struct hss_extra_info *info) { + if (!w) { + if (info) info->error_code = hss_error_got_null; + return false; + } + + /* Note: we do not check if the working key is in a usable state */ + /* There are a couple of odd-ball scenarios (e.g. when they've */ + /* manually allocated the key, but haven't loaded it yet) that we */ + /* don't have a good reason to disallow */ + + w->autoreserve = sigs_to_autoreserve; + return true; +} + +/* + * This is called when we generate a signature; it checks if we need + * to write out a new private key (and advance the reservation); if it + * decides it needs to write out a new private key, it also decides how + * far it needs to advance it + */ +bool hss_advance_count(struct hss_working_key *w, sequence_t cur_count, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + struct hss_extra_info *info, bool *trash_private_key) { + + if (cur_count == w->max_count) { + /* We hit the end of the root; this will be the last signature */ + /* this private key can do */ + w->status = hss_error_private_key_expired; /* Fail if they try to */ + /* sign any more */ + info->last_signature = true; + /* Make sure we zeroize the private key */ + *trash_private_key = true; /* We can't trash our copy of the */ + /* private key until after we've generated the signature */ + /* We can trash the copy in secure storage, though */ + if (update_private_key) { + unsigned char private_key[PRIVATE_KEY_LEN]; + memset( private_key, PARM_SET_END, PRIVATE_KEY_LEN ); + if (!update_private_key(private_key, PRIVATE_KEY_LEN, context)) { + info->error_code = hss_error_private_key_write_failed; + return false; + } + } else { + memset( context, PARM_SET_END, PRIVATE_KEY_LEN ); + } + return true; + } + sequence_t new_count = cur_count + 1; + + if (new_count > w->reserve_count) { + /* We need to advance the reservation */ + + /* Check if we have enough space to do the entire autoreservation */ + if (w->max_count - new_count > w->autoreserve) { + new_count += w->autoreserve; + } else { + /* If we don't have enough space, reserve what we can */ + new_count = w->max_count; + } + + put_bigendian( w->private_key + PRIVATE_KEY_INDEX, new_count, + PRIVATE_KEY_INDEX_LEN ); + if (update_private_key) { + if (!update_private_key(w->private_key, PRIVATE_KEY_INDEX_LEN, + context)) { + /* Oops, we couldn't write the private key; undo the */ + /* reservation advance (and return an error) */ + info->error_code = hss_error_private_key_write_failed; + put_bigendian( w->private_key + PRIVATE_KEY_INDEX, + w->reserve_count, PRIVATE_KEY_INDEX_LEN ); + return false; + } + } else { + put_bigendian( context, new_count, PRIVATE_KEY_INDEX_LEN ); + } + w->reserve_count = new_count; + } + + return true; +} + +/* + * This will make sure that (at least) N signatures are reserved; that is, we + * won't need to actually call the update function for the next N signatures + * generated + * + * This can be useful if the update_private_key function is expensive. + * + * Note that if, N (or more) signatures are already reserved, this won't do + * anything. + */ +bool hss_reserve_signature( + struct hss_working_key *w, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + unsigned sigs_to_reserve, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + if (!w) { + info->error_code = hss_error_got_null; + return false; + } + if (w->status != hss_error_none) { + info->error_code = w->status;; + return false; + } + + if (sigs_to_reserve > w->max_count) { + info->error_code = hss_error_not_that_many_sigs_left; + return false; /* Very funny */ + } + + /* + * If we're given a raw private key, make sure it's the one we're + * thinking of. + * I have no idea why someone would reserve signatures if they have + * a raw private key (which is cheap to update), however there's no + * reason we shouldn't support it + */ + if (!update_private_key) { + if (0 != memcmp( context, w->private_key, PRIVATE_KEY_LEN)) { + info->error_code = hss_error_key_mismatch; + return false; /* Private key mismatch */ + } + } + + /* Figure out what the current count is */ + sequence_t current_count = 0; + unsigned i; + for (i = 0; ilevels; i++) { + struct merkle_level *tree = w->tree[i]; + /* -1 because the current_index counts the signatures to the */ + /* current next level */ + current_count = (current_count << tree->level) + + tree->current_index - 1; + } + current_count += 1; /* The bottom-most tree isn't advanced */ + + sequence_t new_reserve_count; /* This is what the new reservation */ + /* setting would be (if we accept the reservation) */ + if (current_count > w->max_count - sigs_to_reserve) { + /* Not that many sigantures left */ + /* Reserve as many as we can */ + new_reserve_count = w->max_count; + } else { + new_reserve_count = current_count + sigs_to_reserve; + } + + if (new_reserve_count <= w->reserve_count) { + /* We already have (at least) that many reserved; do nothing */ + return true; + } + + /* Attempt to update the count in the private key */ + put_bigendian( w->private_key + PRIVATE_KEY_INDEX, new_reserve_count, + PRIVATE_KEY_INDEX_LEN ); + /* Update the copy in NV storage */ + if (update_private_key) { + if (!update_private_key(w->private_key, PRIVATE_KEY_INDEX_LEN, + context)) { + /* Oops, couldn't update it */ + put_bigendian( w->private_key + PRIVATE_KEY_INDEX, + w->reserve_count, PRIVATE_KEY_INDEX_LEN ); + info->error_code = hss_error_private_key_write_failed; + return false; + } + } else { + memcpy( context, w->private_key, PRIVATE_KEY_INDEX_LEN ); + } + w->reserve_count = new_reserve_count; + + return true; +} diff --git a/src/sig_stateful/lms/external/hss_reserve.h b/src/sig_stateful/lms/external/hss_reserve.h new file mode 100644 index 0000000000..3b101c1130 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_reserve.h @@ -0,0 +1,21 @@ +#if !defined( HSS_RESERVE_H_ ) +#define HSS_RESERVE_H_ + +/* + * This is the internal include file for the reservation functions for this + * subsystem. It should not be used by applications + */ + +#include "common_defs.h" + +struct hss_working_key; + +void hss_set_reserve_count(struct hss_working_key *w, sequence_t count); + +bool hss_advance_count(struct hss_working_key *w, sequence_t new_count, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + struct hss_extra_info *info, bool *trash_private_key); + +#endif /* HSS_RESERVE_H_ */ diff --git a/src/sig_stateful/lms/external/hss_sign.c b/src/sig_stateful/lms/external/hss_sign.c new file mode 100644 index 0000000000..5838829f1e --- /dev/null +++ b/src/sig_stateful/lms/external/hss_sign.c @@ -0,0 +1,736 @@ +/* + * This is an implementation of the HSS signature scheme from LMS + * This is the part that actually generates the signature + */ +#include +#include +#include "common_defs.h" +#include "hss.h" +#include "hash.h" +#include "endian.h" +#include "hss_internal.h" +#include "hss_aux.h" +#include "hss_thread.h" +#include "hss_reserve.h" +#include "lm_ots.h" +#include "lm_ots_common.h" +#include "hss_derive.h" + +/* + * This adds one leaf to the building and next subtree. + */ +enum subtree_build_status { + subtree_got_error, /* Oops, something broke */ + subtree_more_to_do, /* Processed node, still more to do */ + subtree_did_last_node, /* Processed last node */ + subtree_all_done /* We're good */ +}; +static enum subtree_build_status subtree_add_next_node( + struct subtree *subtree, + struct merkle_level *tree, + int next_tree, + struct thread_collection *col) { + unsigned subtree_size = (subtree->level>0 ? tree->subtree_size : + tree->top_subtree_size); + unsigned log_leafs = subtree_size + subtree->levels_below; + merkle_index_t max_index = (merkle_index_t)1 << log_leafs; + /* Check if there is anything more to do */ + if (subtree->current_index == max_index) return subtree_all_done; + unsigned hash_size = tree->hash_size; + unsigned char cur_val[MAX_HASH]; + + /* Compute the leaf node */ + merkle_index_t i; + unsigned ots_len = lm_ots_get_public_key_len(tree->lm_ots_type); + unsigned char pub_key[ LEAF_MAX_LEN ]; + const unsigned char *I = (next_tree ? tree->I_next : tree->I); + memcpy( pub_key + LEAF_I, I, I_LEN ); + SET_D( pub_key + LEAF_D, D_LEAF ); + merkle_index_t r = subtree->left_leaf + subtree->current_index; + merkle_index_t q = r | ((merkle_index_t)1 << tree->level); + put_bigendian( pub_key + LEAF_R, q, 4); + + const unsigned char *seed = (next_tree ? tree->seed_next : tree->seed); + struct seed_derive derive; + if (!hss_seed_derive_init( &derive, tree->lm_type, tree->lm_ots_type, + I, seed )) return subtree_got_error; + hss_seed_derive_set_q(&derive, r); + if (!lm_ots_generate_public_key(tree->lm_ots_type, I, + r, &derive, pub_key + LEAF_PK, ots_len)) { + hss_seed_derive_done(&derive); + return subtree_got_error; + } + hss_seed_derive_done(&derive); + + /* Hash it to form the leaf node */ + union hash_context ctx; + hss_hash_ctx( cur_val, tree->h, &ctx, pub_key, LEAF_LEN(hash_size)); + + /* Where in the subtree we store the values */ + merkle_index_t subtree_index = subtree->current_index + + ((merkle_index_t)1 << log_leafs); + enum subtree_build_status status = subtree_more_to_do; + + /* Walk up the stack, and then up the tree */ + for (i=0;; i++) { + if (i >= subtree->levels_below) { + /* This node is within the subtree; save it */ + memcpy( &subtree->nodes[ (subtree_index-1) * hash_size ], cur_val, hash_size ); + } + if (subtree_index == 1) { /* Hit the root */ + status = subtree_did_last_node; + break; + } + if ((q & 1) == 0) break; /* Hit a left node */ + q >>= 1; + + /* This is a right node; combine it with the left node */ + unsigned char *left_node; + if (i >= subtree->levels_below) { + /* The left node is in the tree */ + left_node = &subtree->nodes[ (subtree_index-2) * hash_size ]; + } else { + /* The left node is on the stack */ + left_node = subtree->stack + (i * hash_size); + } + hss_combine_internal_nodes( cur_val, + left_node, cur_val, + tree->h, I, hash_size, + q); + subtree_index >>= 1; + } + + /* If we haven't got out of the stack, put the value there */ + if (i < subtree->levels_below) { + if (col) hss_thread_before_write(col); + memcpy( subtree->stack + (i * hash_size), cur_val, hash_size ); + if (col) hss_thread_after_write(col); + } + + /* Ok, we've done another node */ + subtree->current_index += 1; + + return status; +} + +/* + * This steps the next tree by one. We need to do this 2**tree->level times, + * and then the next tree will be ready + */ +static int hss_step_next_tree (struct merkle_level *tree, + const struct hss_working_key *w, + struct thread_collection *col) { + struct subtree *prev_subtree = 0; + struct subtree *subtree = 0; + int j; + + LMS_UNUSED(w); + /* Search for the subtree to update */ + for (j = tree->sublevels-1; j>=0; j--) { + subtree = tree->subtree[j][NEXT_TREE]; + if (subtree->current_index < MAX_SUBINDEX) break; + prev_subtree = subtree; + } + unsigned height_subtree = (j == 0) ? tree->top_subtree_size : + tree->subtree_size; + if (j >= 0) { + /* For subtrees other than the bottom one, we get the first */ + /* node 'for free' (as it's the root of the previous subtree */ + if (subtree->current_index == 0 && prev_subtree) { + /* For the initial node of the subtree, reuse the root */ + /* of the previous one */ + unsigned hash_size = tree->hash_size; + memcpy( &subtree->nodes[ hash_size * (((merkle_index_t)1<nodes[ 0 ], + hash_size ); + subtree->current_index = ((merkle_index_t)1 << subtree->levels_below); + } + + /* Add the next node */ + switch (subtree_add_next_node( subtree, tree, 1, col )) { + case subtree_got_error: default: return 0; /* Huh? */ + case subtree_more_to_do: + break; + case subtree_did_last_node: + case subtree_all_done: + /* Mark this subtree as 'all processed' */ + subtree->current_index = MAX_SUBINDEX; + break; + } + } + + return 1; +} + +/* + * Generate the next Merkle signature for a given level + */ +static int generate_merkle_signature( + unsigned char *signature, unsigned signature_len, + struct merkle_level *tree, + const struct hss_working_key *w, + const void *message, size_t message_len) { + /* First off, write the index value */ + LMS_UNUSED(w); + if (signature_len < 4) return 0; + merkle_index_t current_index = tree->current_index; + put_bigendian( signature, current_index, 4 ); + signature += 4; signature_len -= 4; + + /* Write the OTS signature */ + size_t ots_sig_size = lm_ots_get_signature_len( tree->lm_ots_type ); + if (ots_sig_size == 0 || ots_sig_size > signature_len) return 0; + if (message == NULL) { + /* Internal interface: if message = NULL, we're supposed to */ + /* generate everything *except* the OTS signature */ + memset( signature, 0, ots_sig_size ); + } else { + struct seed_derive derive; + if (!hss_seed_derive_init( &derive, + tree->lm_type, tree->lm_ots_type, + tree->I, tree->seed )) return 0; + hss_seed_derive_set_q(&derive, current_index); + bool success = lm_ots_generate_signature( tree->lm_ots_type, tree->I, + current_index, &derive, + message, message_len, false, + signature, ots_sig_size); + hss_seed_derive_done(&derive); + if (!success) return 0; + } + signature += ots_sig_size; signature_len -= ots_sig_size; + + /* Write the LM parameter set */ + if (signature_len < 4) return 0; + put_bigendian( signature, tree->lm_type, 4 ); + signature += 4; signature_len -= 4; + + /* Now, write the authentication path */ + int i, j; + merkle_index_t index = current_index; + unsigned n = tree->hash_size; + for (i = tree->sublevels-1; i>=0; i--) { + int height = (i == 0) ? tree->top_subtree_size : tree->subtree_size; + struct subtree *subtree = tree->subtree[i][ACTIVE_TREE]; + merkle_index_t subtree_index = (index & + (((merkle_index_t)1 << height) - 1)) + + ((merkle_index_t)1 << height); + for (j = height-1; j>=0; j--) { + if (signature_len < n) return 0; + memcpy( signature, subtree->nodes + n * ((subtree_index^1) - 1), n ); + signature += n; signature_len -= n; + subtree_index >>= 1; + } + index >>= height; + } + + /* Mark that we've generated a signature */ + tree->current_index = current_index + 1; + + return 1; +} + +/* + * This signed the root of tree with the parent; it places both the signature + * and the public key into signed_key + */ +bool hss_create_signed_public_key(unsigned char *signed_key, + size_t len_signature, + struct merkle_level *tree, + struct merkle_level *parent, + struct hss_working_key *w) { + /* Where we place the public key */ + unsigned char *public_key = signed_key + len_signature; + + /* Place the public key there */ + put_bigendian( public_key + 0, tree->lm_type, 4 ); + put_bigendian( public_key + 4, tree->lm_ots_type, 4 ); + memcpy( public_key + 8, tree->I, I_LEN ); + unsigned hash_size = tree->hash_size; + /* This is where the root hash is */ + memcpy( public_key + 8 + I_LEN, + tree->subtree[0][ACTIVE_TREE]->nodes, + hash_size ); + unsigned len_public_key = 8 + I_LEN + hash_size; + + /* Now, generate the signature */ + if (!generate_merkle_signature( signed_key, len_signature, + parent, w, public_key, len_public_key)) { + return false; + } + + parent->update_count = UPDATE_NEXT; /* The parent has generated a */ + /* signature; it's now eligible for another */ + /* round of updates */ + + return true; +} + +struct gen_sig_detail { + unsigned char *signature; + size_t signature_len; + const unsigned char *message; + size_t message_len; + struct hss_working_key *w; + enum hss_error_code *got_error; +}; +/* This does the actual signature generation */ +/* It is (potentially) run within a thread */ +static void do_gen_sig( const void *detail, struct thread_collection *col) { + const struct gen_sig_detail *d = detail; + size_t signature_len = d->signature_len; + unsigned char *signature = d->signature; + struct hss_working_key *w = d->w; + unsigned levels = w->levels; + + /* The number of signed public keys */ + if (signature_len < 4) goto failed; + put_bigendian( signature, levels - 1, 4 ); + signature += 4; signature_len -= 4; + /* The signed public keys */ + unsigned i; + for (i=1; isigned_pk_len[i]; + if (signature_len < len_signed_pk) goto failed; + memcpy( signature, w->signed_pk[i], len_signed_pk ); + signature += len_signed_pk; signature_len -= len_signed_pk; + } + /* And finally the signature of the actual message */ + if (signature_len < w->siglen[levels-1]) goto failed; /* Oops, not enough room */ + + const unsigned char *message = d->message; + size_t message_len = d->message_len; + + if (!generate_merkle_signature(signature, signature_len, + w->tree[ levels-1 ], w, message, message_len)) { + goto failed; + } + + /* Success! */ + return; + +failed: + /* Report failure */ + hss_thread_before_write(col); + *d->got_error = hss_error_internal; + hss_thread_after_write(col); +} + +struct step_next_detail { + struct hss_working_key *w; + struct merkle_level *tree; + enum hss_error_code *got_error; +}; +/* This steps the next tree */ +/* It is (potentially) run within a thread */ +static void do_step_next( const void *detail, struct thread_collection *col) { + const struct step_next_detail *d = detail; + struct hss_working_key *w = d->w; + struct merkle_level *tree = d->tree; + + if (!hss_step_next_tree( tree, w, col )) { + /* Report failure */ + hss_thread_before_write(col); + *d->got_error = hss_error_internal; + hss_thread_after_write(col); + } +} + +struct step_building_detail { + struct merkle_level *tree; + struct subtree *subtree; + enum hss_error_code *got_error; +}; +/* This steps the building tree */ +/* It is (potentially) run within a thread */ +static void do_step_building( const void *detail, + struct thread_collection *col) { + const struct step_building_detail *d = detail; + struct merkle_level *tree = d->tree; + struct subtree *subtree = d->subtree; + + switch (subtree_add_next_node( subtree, tree, 0, col )) { + case subtree_got_error: default: + /* Huh? Report failure */ + hss_thread_before_write(col); + *d->got_error = hss_error_internal; + hss_thread_after_write(col); + break; + case subtree_more_to_do: + case subtree_did_last_node: + case subtree_all_done: + break; + } +} + +struct update_parent_detail { + struct hss_working_key *w; + enum hss_error_code *got_error; +}; +/* + * This gives an update to the parent (non-bottom Merkle trees) + */ +static void do_update_parent( const void *detail, + struct thread_collection *col) { + const struct update_parent_detail *d = detail; + struct hss_working_key *w = d->w; + unsigned levels = w->levels; + unsigned current_level = levels - 2; /* We start with the first */ + /* non-bottom level */ + for (;;) { + struct merkle_level *tree = w->tree[current_level]; + switch (tree->update_count) { + case UPDATE_DONE: return; /* No more updates needed */ + case UPDATE_NEXT: /* Our job is to update the next tree */ + tree->update_count = UPDATE_PARENT; + if (current_level == 0) return; /* No next tree to update */ + if (!hss_step_next_tree( tree, w, col )) goto failed; + return; + case UPDATE_PARENT: /* Our job is to update our parent */ + tree->update_count = UPDATE_BUILDING + 0; + if (current_level == 0) return; /* No parent to update */ + current_level -= 1; + continue; + default: { + /* Which building tree we need to update */ + unsigned level_to_update = + (tree->update_count - UPDATE_BUILDING) + 1; + if (level_to_update >= tree->sublevels) { + /* We've completed all the updates we need to do (until */ + /* the next time we need to sign something) */ + tree->update_count = UPDATE_DONE; + return; + } + + /* Next time, update the next BUILDING subtree */ + tree->update_count += 1; + + struct subtree *subtree = + tree->subtree[level_to_update][BUILDING_TREE]; + + /* The number of leaves in this tree */ + merkle_index_t tree_leaves = (merkle_index_t)1 << tree->level; + + /* Check if we'd actually use the building tree */ + if (subtree->left_leaf >= tree_leaves) { + /* We'll never use it; don't bother updating it */ + return; + } + + /* We'll use the BUILDING_TREE, actually add a node */ + switch (subtree_add_next_node( subtree, tree, 0, col )) { + case subtree_got_error: default: goto failed; /* Huh? */ + case subtree_did_last_node: + case subtree_all_done: + case subtree_more_to_do: + /* We're done everything we need to do for this step */ + return; + } + } + } + } + +failed: + /* Huh? Report failure */ + hss_thread_before_write(col); + *d->got_error = hss_error_internal; + hss_thread_after_write(col); +} + +/* + * Code to actually generate the signature + */ +bool hss_generate_signature( + struct hss_working_key *w, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + const void *message, size_t message_len, + unsigned char *signature, size_t signature_buf_len, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + unsigned i; + bool trash_private_key = false; + + info->last_signature = false; + + if (!w) { + info->error_code = hss_error_got_null; + goto failed; + } + if (w->status != hss_error_none) { + info->error_code = w->status; + goto failed; + } + + /* If we're given a raw private key, make sure it's the one we're */ + /* thinking of */ + if (!update_private_key) { + if (0 != memcmp( context, w->private_key, PRIVATE_KEY_LEN)) { + info->error_code = hss_error_key_mismatch; + return false; /* Private key mismatch */ + } + } + + /* Check if the buffer we were given is too short */ + if (w->signature_len > signature_buf_len) { + /* The signature would overflow the buffer */ + info->error_code = hss_error_buffer_overflow; + goto failed; + } + + unsigned levels = w->levels; + /* + * Compile the current count + */ + sequence_t current_count = 0; + for (i=0; i < levels; i++) { + struct merkle_level *tree = w->tree[i]; + current_count <<= tree->level; + /* We subtract 1 because the nonbottom trees are already advanced */ + current_count += (sequence_t)tree->current_index - 1; + } + current_count += 1; /* Bottom most tree isn't already advanced */ + + /* Ok, try to advance the private key */ + if (!hss_advance_count(w, current_count, + update_private_key, context, info, + &trash_private_key)) { + /* hss_advance_count fills in the error reason */ + goto failed; + } + + /* Ok, now actually generate the signature */ + + /* We'll be doing several things in parallel */ + struct thread_collection *col = hss_thread_init(info->num_threads); + enum hss_error_code got_error = hss_error_none; + + /* Generate the signature */ + { + struct gen_sig_detail gen_detail; + gen_detail.signature = signature; + gen_detail.signature_len = w->signature_len; + gen_detail.message = message; + gen_detail.message_len = message_len; + gen_detail.w = w; + gen_detail.got_error = &got_error; + + hss_thread_issue_work(col, do_gen_sig, &gen_detail, sizeof gen_detail); + } + + /* Update the bottom level next tree */ + if (levels > 1) { + struct step_next_detail step_detail; + step_detail.w = w; + step_detail.tree = w->tree[levels-1]; + step_detail.got_error = &got_error; + + hss_thread_issue_work(col, do_step_next, &step_detail, sizeof step_detail); + } + + /* Issue orders to step each of the building subtrees in the bottom tree */ + int skipped_a_level = 0; /* Set if the below issued didn't issue an */ + /* order for at least one level */ + { + struct merkle_level *tree = w->tree[levels-1]; + merkle_index_t updates_before_end = tree->max_index - tree->current_index + 1; + int h_subtree = tree->subtree_size; + for (i=1; isublevels; i++) { + struct subtree *subtree = tree->subtree[i][BUILDING_TREE]; + /* Check if there is a building tree */ + if (updates_before_end < (merkle_index_t)1 << + (subtree->levels_below + h_subtree)) { + /* No; we're at the last subtree within this tree */ + skipped_a_level = 1; + continue; + } + struct step_building_detail step_detail; + step_detail.tree = tree; + step_detail.subtree = subtree; + step_detail.got_error = &got_error; + + hss_thread_issue_work(col, do_step_building, &step_detail, sizeof step_detail); + + } + /* If there's only one sublevel, act as if we always skipped a sublevel */ + if (tree->sublevels == 1) skipped_a_level = 1; + } + + /* + * And, if we're allowed to give the parent a chance to update, and + * there's a parent with some updating that needs to be done, schedule + * that to be done + */ + if (skipped_a_level && + levels > 1 && w->tree[levels-2]->update_count != UPDATE_DONE) { + struct update_parent_detail detail; + detail.w = w; + detail.got_error = &got_error; + hss_thread_issue_work(col, do_update_parent, &detail, sizeof detail); + } + + /* Wait for all of them to finish */ + hss_thread_done(col); + + /* Check if any of them reported a failure */ + if (got_error != hss_error_none) { + info->error_code = got_error; + goto failed; + } + + current_count += 1; /* The new count is one more than what is */ + /* implied by the initial state of the Merkle trees */ + + /* + * Now, we scan to see if we exhausted a Merkle tree, and need to update it + * At the same time, we check to see if we need to advance the subtrees + */ + sequence_t cur_count = current_count; + unsigned merkle_levels_below = 0; + int switch_merkle = w->levels; + struct merkle_level *tree; + for (i = w->levels-1; i>=0; i--, merkle_levels_below += tree->level) { + tree = w->tree[i]; + + if (0 == (cur_count & (((sequence_t)1 << (merkle_levels_below + tree->level))-1))) { + /* We exhausted this tree */ + if (i == 0) { + /* We've run out of signatures; we've already caught this */ + /* above; just make *sure* we've marked the key as */ + /* unusable, and give up */ + w->status = hss_error_private_key_expired; + break; + } + + /* Remember we'll need to switch to the NEXT_TREE */ + switch_merkle = i; + continue; + } + + /* Check if we need to advance any of the subtrees */ + unsigned subtree_levels_below = 0; + unsigned j; + for (j = tree->sublevels-1; j>0; j--) { + subtree_levels_below += tree->subtree_size; + if (0 != (cur_count & (((sequence_t)1 << (merkle_levels_below + subtree_levels_below))-1))) { + /* We're in the middle of this subtree */ + goto done_advancing; + } + + /* Switch to the building subtree */ + struct subtree *next = tree->subtree[j][BUILDING_TREE]; + struct subtree *prev = tree->subtree[j][ACTIVE_TREE]; + unsigned char *stack = next->stack; /* Stack stays with */ + /* building tree */ + tree->subtree[j][ACTIVE_TREE] = next; + /* We need to reset the parameters on the new building subtree */ + prev->current_index = 0; + prev->left_leaf += (merkle_index_t)2 << subtree_levels_below; + tree->subtree[j][BUILDING_TREE] = prev; + next->stack = NULL; + prev->stack = stack; + } + } +done_advancing: + /* Check if we used up any Merkle trees; if we have, switch to the */ + /* NEXT_TREE (which we've built in our spare time) */ + for (i = switch_merkle; i < w->levels; i++) { + struct merkle_level *tree = w->tree[i]; + struct merkle_level *parent = w->tree[i-1]; + unsigned j; + + /* Rearrange the subtrees */ + for (j=0; jsublevels; j++) { + /* Make the NEXT_TREE active; replace it with the current active */ + struct subtree *active = tree->subtree[j][NEXT_TREE]; + struct subtree *next = tree->subtree[j][ACTIVE_TREE]; + unsigned char *stack = active->stack; /* Stack stays with */ + /* next tree */ + + active->left_leaf = 0; + next->current_index = 0; + next->left_leaf = 0; + tree->subtree[j][ACTIVE_TREE] = active; + tree->subtree[j][NEXT_TREE] = next; + active->stack = NULL; + next->stack = stack; + if (j > 0) { + /* Also reset the building tree */ + struct subtree *building = tree->subtree[j][BUILDING_TREE]; + building->current_index = 0; + merkle_index_t size_subtree = (merkle_index_t)1 << + (tree->subtree_size + building->levels_below); + building->left_leaf = size_subtree; + } + } + + /* Copy in the value of seed, I we'll use for the new tree */ + memcpy( tree->seed, tree->seed_next, SEED_LEN ); + memcpy( tree->I, tree->I_next, I_LEN ); + + /* Compute the new next I, which is derived from either the parent's */ + /* I or the parent's I_next value */ + merkle_index_t index = parent->current_index; + if (index == parent->max_index) { + hss_generate_child_seed_I_value(tree->seed_next, tree->I_next, + parent->seed_next, parent->I_next, 0, + parent->lm_type, + parent->lm_ots_type); + } else { + hss_generate_child_seed_I_value( tree->seed_next, tree->I_next, + parent->seed, parent->I, index+1, + parent->lm_type, + parent->lm_ots_type); + } + + tree->current_index = 0; /* We're starting this from scratch */ + + /* Generate the signature of the new level */ + if (!hss_create_signed_public_key( w->signed_pk[i], w->siglen[i-1], + tree, parent, w )) { + info->error_code = hss_error_internal; + goto failed; + } + } + + /* And we've set things up for the next signature... */ + + if (trash_private_key) { + memset( w->private_key, PARM_SET_END, PRIVATE_KEY_LEN ); + } + + return true; + +failed: + + if (trash_private_key) { + memset( w->private_key, PARM_SET_END, PRIVATE_KEY_LEN ); + } + + /* On failure, make sure that we don't return anything that might be */ + /* misconstrued as a real signature */ + memset( signature, 0, signature_buf_len ); + return false; +} + +/* + * Get the signature length + */ +size_t hss_get_signature_len_from_working_key(struct hss_working_key *w) { + if (!w || w->status != hss_error_none) return 0; + + int levels = w->levels; + if (levels > MAX_HSS_LEVELS) return 0; + param_set_t lm[MAX_HSS_LEVELS], ots[MAX_HSS_LEVELS]; + int i; + for (i=0; itree[i]->lm_type; + ots[i] = w->tree[i]->lm_ots_type; + } + + return hss_get_signature_len(levels, lm, ots); +} diff --git a/src/sig_stateful/lms/external/hss_sign_inc.c b/src/sig_stateful/lms/external/hss_sign_inc.c new file mode 100644 index 0000000000..e455b5cd2b --- /dev/null +++ b/src/sig_stateful/lms/external/hss_sign_inc.c @@ -0,0 +1,218 @@ +/* + * This is the code that implements the hierarchical part of the LMS hash + * based signatures; in this case, incremental signing + */ +#include +#include "hss.h" +#include "common_defs.h" +#include "hss_verify_inc.h" +#include "lm_verify.h" +#include "lm_common.h" +#include "lm_ots.h" +#include "lm_ots_verify.h" +#include "hash.h" +#include "endian.h" +#include "hss_internal.h" +#include "hss_sign_inc.h" +#include "hss_derive.h" + +/* + * Start the process of creating an HSS signature incrementally. Parameters: + * ctx - The state we'll use to track the incremental signature + * working_key - the in-memory version of the in-memory private key + * update_private_key - function to call to update the master private key + * context - context pointer for above + * siganture - the buffer to hold the signature + * signature_len - the length of the buffer + * this_is_the_last_signature - if non-NULL, this will be set if this + * signature is the last for this private key + */ +bool hss_sign_init( + struct hss_sign_inc *ctx, + struct hss_working_key *w, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + unsigned char *signature, size_t signature_len, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 };; + if (!info) info = &temp_info; + + if (!ctx) { + info->error_code = hss_error_got_null; + return false; + } + ctx->status = hss_error_ctx_uninitialized; /* Until we hear otherwise, */ + /* we got a failure */ + + if (!w) { + info->error_code = hss_error_got_null; + return false; + } + if (w->status != hss_error_none) { + info->error_code = w->status; + return false; + } + + struct merkle_level *bottom = w->tree[ w->levels - 1 ]; + + unsigned char I[I_LEN]; + memcpy( I, bottom->I, I_LEN ); + + /* Compute the value of C we'll use */ + merkle_index_t q = bottom->current_index; + ctx->q = q; + int h = bottom->h; + ctx->h = h; + + struct seed_derive derive; + if (!hss_seed_derive_init( &derive, bottom->lm_type, bottom->lm_ots_type, + bottom->I, bottom->seed )) return false; + hss_seed_derive_set_q(&derive, q); + lm_ots_generate_randomizer( ctx->c, bottom->hash_size, &derive ); + hss_seed_derive_done(&derive); + + /* + * Ask the signature generation process to do everything *except* + * the bottom level OTS signature + */ + bool success = hss_generate_signature( w, + update_private_key, context, + NULL, 0, /* <--- we don't have the message yet */ + signature, signature_len, info ); + if (!success) { + /* On failure, hss_generate_signature fills in the failure reason */ + ctx->status = info->error_code; + hss_zeroize( &ctx->c, sizeof ctx->c ); /* People don't get to */ + /* learn what randomizer we would have used */ + return false; + } + + /* Now, initialize the context */ + hss_init_hash_context( h, &ctx->hash_ctx ); + { + unsigned char prefix[ MESG_PREFIX_MAXLEN ]; + memcpy( prefix + MESG_I, I, I_LEN ); + unsigned q_bin[4]; put_bigendian( q_bin, q, 4 ); + memcpy( prefix + MESG_Q, q_bin, 4 ); /* q */ + SET_D( prefix + MESG_D, D_MESG ); + int n = bottom->hash_size; + memcpy( prefix + MESG_C, ctx->c, n ); /* C */ + hss_update_hash_context(h, &ctx->hash_ctx, prefix, MESG_PREFIX_LEN(n) ); + } + + /* It succeeded so far... */ + ctx->status = hss_error_none; + return true; +} + +/* This adds another piece of the message to validate */ +bool hss_sign_update( + struct hss_sign_inc *ctx, + const void *message_segment, + size_t len_message_segment) { + if (!ctx || ctx->status != hss_error_none) return false; + + hss_update_hash_context(ctx->h, &ctx->hash_ctx, + message_segment, len_message_segment ); + + return true; +} + +/* We've added all the pieces of the messages, now do the validation */ +bool hss_sign_finalize( + struct hss_sign_inc *ctx, + const struct hss_working_key *working_key, + unsigned char *signature, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + + if (!ctx) { + info->error_code = hss_error_got_null; + return false; + } + if (ctx->status != hss_error_none) { + info->error_code = ctx->status; + return false; + } + + /* Success or fail, we can't use the context any more */ + ctx->status = hss_error_ctx_already_used; + + int L = working_key->levels; + + /* Step through the signature, looking for the place to put the OTS */ + /* signature, and (while we're at it) recovering the I and seed values */ + const unsigned char *I = working_key->tree[0]->I; + const unsigned char *seed = working_key->tree[0]->seed; + /* Note: we alternate buffers during generation in case */ + /* hss_generate_child_seed_I_value doesn't allow new values to */ + /* overwrite old ones */ + unsigned char I_buff[2][I_LEN]; + unsigned char seed_buff[2][SEED_LEN]; + + /* Q: should we double check the various fixed fields of the signatures */ + /* (e.g. the number of signed keys, the parameter sets? */ + + signature += 4; + + int i; + for (i=0; i working_key->tree[i]->max_index) { + hss_zeroize( seed_buff, sizeof seed_buff ); + return 0; + } + if (!hss_generate_child_seed_I_value( seed_buff[i&1], I_buff[i&1], + seed, I, q, + working_key->tree[i]->lm_type, + working_key->tree[i]->lm_ots_type )) { + hss_zeroize( seed_buff, sizeof seed_buff ); + info->error_code = hss_error_internal; + return false; + } + + seed = seed_buff[i&1]; + I = I_buff[i&1]; + + /* Step to the end of this signed key */ + signature += lm_get_signature_len( working_key->tree[i]->lm_type, + working_key->tree[i]->lm_ots_type); + signature += lm_get_public_key_len(working_key->tree[i+1]->lm_type); + } + + /* Now, signature points to where the bottom LMS signature should go */ + /* It starts with the q value */ + put_bigendian( signature, ctx->q, 4 ); + signature += 4; + /* And then the LM-OTS signature */ + + /* Copy in the C value into the signature */ + memcpy( signature+4, ctx->c, 32 ); + + /* Generate the final hash */ + unsigned char hash[ MAX_HASH ]; + hss_finalize_hash_context( ctx->h, &ctx->hash_ctx, hash ); + + /* And the final OTS signature based on that hash */ + param_set_t lm_type = working_key->tree[i]->lm_type; + param_set_t ots_type = working_key->tree[i]->lm_ots_type; + struct seed_derive derive; + bool success = hss_seed_derive_init( &derive, lm_type, ots_type, + I, seed ); + if (success) { + hss_seed_derive_set_q( &derive, ctx->q ); + success = lm_ots_generate_signature( + ots_type, I, ctx->q, &derive, hash, 0, true, + signature, lm_ots_get_signature_len( ots_type )); + + hss_seed_derive_done( &derive ); + } + if (!success) { + info->error_code = hss_error_internal; + } + + hss_zeroize( seed_buff, sizeof seed_buff ); + return success; +} diff --git a/src/sig_stateful/lms/external/hss_sign_inc.h b/src/sig_stateful/lms/external/hss_sign_inc.h new file mode 100644 index 0000000000..426d271abd --- /dev/null +++ b/src/sig_stateful/lms/external/hss_sign_inc.h @@ -0,0 +1,81 @@ +#if !defined( HSS_SIGN_INC_H_ ) +#define HSS_SIGN_INC_H_ +#include +#include +#include "hash.h" +#include "common_defs.h" + +/* + * These are the functions to sign a message incrementally. + * That is, we assume that we don't have the entire message at + * once, instead, we have it in pieces (for example, the signature + * is of a multigigabyte file) + * + * Usage: + * struct hss_sign_inc ctx; + * bool success = hss_sign_init( &ctx, working_key, + * update_private_key, private_key_context, + * signature, signature_buffer_len, + * &lsat_signature ); + * hss_sign_update( &ctx, message_part_1, len_1 ); + * hss_sign_update( &ctx, message_part_2, len_2 ); + * hss_sign_update( &ctx, message_part_3, len_3 ); + * success = hss_sign_finalize( &ctx, working_key, signature ); + * if (success) printf( "We generated the signature\n" ); + * + * This is in its own include file because we need to import some + * 'not-generally-for-general-consumption' include files to make + * it work (as they're in the hss_sign_inc structure) + */ + +/* + * This is the context structure that holds the intermedate results of an + * in-process signature + * It's a application-visible structure for ease of use: the application can + * allocate it as an automatic, and if the application aborts in the middle of + * signing, it doesn't cause a memory leak + */ +struct hss_sign_inc { + enum hss_error_code status; /* Either hss_error_none if we're in */ + /* process, or the reason why we'd fail */ + + int h; /* The hash function */ + merkle_index_t q; /* The index of the bottom level signature */ + union hash_context hash_ctx; /* For the running hash we use */ + + unsigned char c[MAX_HASH]; /* The C value we used */ +}; + +struct hss_extra_info; + +/* Starts off the process of incrementally signing a message */ +/* If it detects a failure, this returns false */ +/* Handing the return code is optional; if this fails, the finalization */ +/* step will fail too */ +bool hss_sign_init( + struct hss_sign_inc *ctx, + struct hss_working_key *working_key, + bool (*update_private_key)(unsigned char *private_key, + size_t len_private_key, void *context), + void *context, + unsigned char *signature, size_t signature_len, + struct hss_extra_info *info); + +/* This adds another piece of the message to sign */ +/* Again, the result code is optional */ +bool hss_sign_update( + struct hss_sign_inc *ctx, + const void *message_segment, + size_t len_message_segment); + +/* This finalizes the signature generation */ +/* This returns true if the signature was generated properly */ +/* We ask the caller to pass in the working key again, we need to review */ +/* the private key (we don't want to place it in the context) */ +bool hss_sign_finalize( + struct hss_sign_inc *ctx, + const struct hss_working_key *working_key, + unsigned char *signature, + struct hss_extra_info *info); + +#endif /* HSS_SIGN_INC_H_ */ diff --git a/src/sig_stateful/lms/external/hss_thread.h b/src/sig_stateful/lms/external/hss_thread.h new file mode 100644 index 0000000000..fbf572ad4b --- /dev/null +++ b/src/sig_stateful/lms/external/hss_thread.h @@ -0,0 +1,135 @@ +#if !defined( HSS_THREAD_H_ ) +#define HSS_THREAD_H_ +/* + * This is our internal abstraction of multithreading; this allows the + * "application" (in this case, the HSS code) to issue multiple requests that + * can potentially run on different threads, in a way that doesn't depend on + * the actual threading capability of the OS. If we don't actually have + * multiple threads avaiable (either because the OS doesn't provide us with + * multiple threads, or we hit an internal error trying to generate new + * threads), this will just have the main thread do all the work (and hence + * the application doesn't have to worry its pretty little head about error + * handling, or whether we actually implement threads in the first place) + * + * This is designed to handle this sort of task: we have a series of + * computational problems to do; each can be done independently of the others, + * and each problem results in a fairly short answer. All the children do is + * computation; there's no I/O or any other interaction with the OS at all. + * + * The general paradigm is: + * - The main thread generates a thread collection (via the hss_thread_init + * call) + * - The main thread then issues a series of tasks (via the + * hss_thread_issue_work call). This may spawn off other threads (which + * will then call the function passed); alternatively, the main thread may + * call the function. + * - The main thread then waits for all the tasks to be done (via the + * hss_thread_done call) + * The function(s) passed to the hss_thread_issue_work call will be completed + * by the time hss_thread_done returns + */ +#include + +/* This is our abstract object that stands for a set of threads */ +struct thread_collection; + +/* + * This is called to initialize a set of threads, and returns the identifier. + * Note that this cannot fail; if it returns 0, it's not a failure; instead, + * it's a valid return (which essentially means we're running in nonthreaded + * mode) + * The integer passed is a recommendation on the number of threads + */ +struct thread_collection *hss_thread_init(int); + +/* + * This issues another work item to our collection of threads. At some point + * (between when hss_thread_issue_work is called and when hss_thread_done + * returns), we'll have function called, with a pointer to a copy of the detail + * structure. function may be called by this thread, or it may be called by a + * different one. + * + * The passed detail structure will not be referenced after this returns, and + * hence it is safe if the caller modifies (or frees) it afterwards. If the + * function isn't completed by the time hss_thread_issue_work returns, we'll + * squirrel away a copy of detail (which is why we ask the caller to + * pass size_detail_structure; so we know how much to copy) + * + * We suggest that the application issue the work orders in largest-to-smallest + * order. The ordering doesn't matter for correctness (the API makes no + * guarrantees about when the requests will be completed), however we suggest + * this for expected performance reasons. hss_thread_done will not return + * until all threads are done; what we want to avoid is scenarios where all but + * one of the threads are done, and that last thread is working on an expensive + * function; that would slow things down, and the entire point of this thread + * library is to speed things up. Assigning work items to threads optimally is + * an NP-hard problem, however the simple heuristic of packing 'largest first' + * works fairly well in practice (and is easy to implement). The thread library + * does try to make a best effort attempt to preserve the issue order (assuming + * no intermediate malloc or thread spawn issues; in those cases, the library + * prioritizes correctness over efficiency) + */ +void hss_thread_issue_work(struct thread_collection *col, + void (*function)(const void *detail, + struct thread_collection *col), + const void *detail, size_t size_detail_structure); + +/* + * This waits for all the work items we have issued (via hss_thread_issue_work) + * to be completed (that is, 'function' has returned, and cleans up the + * collection + * + * col must not be used after this; if it was malloc'ed, this will free it + */ +void hss_thread_done(struct thread_collection *col); + +/* + * This should be called before a thread writes to common data + * + * We do this because we sometimes have different threads write data to + * adjacent memory locations; if the compiler has the CPU do a + * read/modify/write to the entire word (or however the CPU has memory + * organized), this could cause a race condition. Forcing those writes to be + * serialized avoids the issue; such a race condition would actually be fairly + * unlikely, but would be a *really* difficult bug to track down if it did + * occur, so it makes sense to go the extra mile to avoid the possibility + * + * Doing this locking also means that the working thread can safely do things + * such as incrementing a global [1] counter to report its results, should + * that be appropriate + * + * We don't bother doing this if we're writing into a malloc'ed region, *if* + * we're the only thread that will be writing into that specific region; we + * assume that the malloc infrastructure will separate distinct malloc'ed + * regions enough to avoid such race conditions + * + * [1] actually, automatic to the main thread; there are no literal globals + * in this package, apart from the verbose debugging flag + */ +void hss_thread_before_write(struct thread_collection *collect); + +/* + * This should be called after a thread writes to common data; it releases + * the lock + */ +void hss_thread_after_write(struct thread_collection *collect); + +/* + * This gives the application guidance for how many worker threads we have + * available, that is, how many work items we can expect to run at once + * + * This is used to decide the level of granularity we need; we we have only 2 + * cores, there's no point is splitting the job up to 50 separate requests; + * however if there are 100 cores, we want (if possible) to do at least 100 + * + * The issue with having not enough requests is that we will have idle threads + * (which could potentially do useful work, if we are able to divide the work + * further). The issue with having too many requests is that the requests use + * up some memory, and we'd prefer not to use up too much memory (we don't + * fail on malloc failure, however we do drop back to a single threaded model) + * + * The value passed is the value we'll pass to hss_thread_init + */ +unsigned hss_thread_num_tracks(int num_threads); + +#endif /* HSS_THREAD_H_ */ diff --git a/src/sig_stateful/lms/external/hss_thread_pthread.c b/src/sig_stateful/lms/external/hss_thread_pthread.c new file mode 100644 index 0000000000..5860a9ea38 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_thread_pthread.c @@ -0,0 +1,298 @@ +#include "hss_thread.h" + +#include +#include + +/* + * This is an implementation of our threaded abstraction using the + * POSIX pthread API + * + * C11 has a similar (but not precisely identical) API to the one that POSIX + * defines (at least for what we do; all we need is thread create/join and + * mutex's, which *any* thread library should provide). I'd code up the + * support for that API as well (using the same base logic, with typedef's and + * helper inlines to isolate the differences), however I don't have a C11 + * implementation handy to test it + */ + +#define MAX_THREAD 16 /* Number try to create more than 16 threads, no */ + /* matter what the application tries to tell us */ +#define DEFAULT_THREAD 16 /* The number of threads to run if the */ + /* application doesn't tell us otherwise (e.g. */ + /* passes in 0) */ + +#define MIN_DETAIL 16 /* So the alignment kludge we do doesn't waste space */ + +/* The information we track about a thread we may have launched */ +struct thread_state { + pthread_t thread_id; + enum { never_was, alive, dead } state; +}; + +struct work_item { + struct work_item *link; /* They're in a linked list */ + + void (*function)(const void *detail, /* Function to call */ + struct thread_collection *col); + + /* These two items are used to pass the thread state to the thread */ + /* if this is the first work item for the thread to process */ + struct thread_collection *col; /* The parent thread_collection */ + struct thread_state *state; /* The pointer into the thread collection */ + /* state for the state of this thread */ + + /* The detail structure that we pass to the function */ + /* We'll malloc enough space to hold the entire structure */ + union { /* union here so that the detail array is */ + void *align1; /* correctly aligned for various datatypes */ + long long align2; + void (*align3)(void); + unsigned char detail[MIN_DETAIL]; + } x; +}; + +struct thread_collection { + pthread_mutex_t lock; /* Must be locked before this structure is */ + /* accessed if there might be a thread */ + pthread_mutex_t write_lock; /* Must be locked before common user data is */ + /* written */ + + unsigned num_thread; + unsigned current_ptr; /* There two are here to avoid O(N) table */ + unsigned num_alive; /* scanning in the most common scenarios */ + + /* Information about the worker threads we may have created */ + struct thread_state threads[MAX_THREAD]; + + /* + * Queue (FIFO) of work items submitted, and which can't be processed + * immedately. We do a FIFO, rather than a stack, so that we perform + * the requests in the order they were issued (which isn't something + * the interface guarantees; however it doesn't interfere with the + * request ordering we ask applications to make) + */ + struct work_item *top_work_queue; + struct work_item *end_work_queue; +}; + +/* + * Allocate a thread control structure + */ +struct thread_collection *hss_thread_init(int num_thread) { + if (num_thread == 0) num_thread = DEFAULT_THREAD; + if (num_thread <= 1) return 0; /* Not an error: an indication to run */ + /* single threaded */ + if (num_thread > MAX_THREAD) num_thread = MAX_THREAD; + + struct thread_collection *col = malloc( sizeof *col ); + if (!col) return 0; /* On malloc failure, run single threaded */ + + col->num_thread = num_thread; + + if (0 != pthread_mutex_init( &col->lock, 0 )) { + free(col); + return 0; + } + + if (0 != pthread_mutex_init( &col->write_lock, 0 )) { + pthread_mutex_destroy( &col->lock ); + free(col); + return 0; + } + + col->current_ptr = 0; + col->num_alive = 0; + int i; + for (i=0; ithreads[i].state = never_was; + } + col->top_work_queue = 0; + col->end_work_queue = 0; + + return col; +} + +/* + * This is the base routine that a worker thread runs + */ +static void *worker_thread( void *arg ) { + struct work_item *w = arg; /* The initial work item */ + struct thread_collection *col = w->col; + struct thread_state *state = w->state; + + for (;;) { + /* Perform the work item in front of us */ + (w->function)(w->x.detail, col); + + /* Ok, we did that */ + free(w); + + /* Check if there's anything else to do */ + pthread_mutex_lock( &col->lock ); + + w = col->top_work_queue; + if (w) { + /* More work; pull it off the queue */ + col->top_work_queue = w->link; + if (w == col->end_work_queue) col->end_work_queue = 0; + + /* And go handle it */ + pthread_mutex_unlock( &col->lock ); + continue; + } + + /* No more work for us to do; post our obituary */ + state->state = dead; + col->num_alive -= 1; + pthread_mutex_unlock( &col->lock ); + + /* And that's all folks */ + return 0; + } +} + +/* + * This adds function/details to the list of things that need to be done + * It either creates a thread to do it, or (if we're maxed out) add it to + * our honey-do list (or, as last resort, just does it itself) + */ +void hss_thread_issue_work(struct thread_collection *col, + void (*function)(const void *detail, + struct thread_collection *col), + const void *detail, size_t size_detail_structure) { + + /* If we're running in single-threaded mode */ + if (!col) { + function( detail, col ); + return; + } + + /* Allocate a work structure to hold this request */ + size_t extra_space; + if (size_detail_structure < MIN_DETAIL) extra_space = 0; + else extra_space = size_detail_structure - MIN_DETAIL; + struct work_item *w = malloc(sizeof *w + extra_space); + + if (!w) { + /* Can't allocate the work structure; fall back to single-threaded */ + function( detail, col ); + return; + } + w->col = col; + w->function = function; + memcpy( w->x.detail, detail, size_detail_structure ); + + unsigned num_thread = col->num_thread; + + pthread_mutex_lock( &col->lock ); + + /* Check if we can spawn a new thread */ + if (col->num_alive < num_thread) { + /* There's supposed to be room for another */ + /* Look for the empty slot */ + unsigned i, j; + j = col->current_ptr; /* Do round-robin (so we don't bang on */ + /* slot 0 whenever we try to start a thread) */ + for (i=0; ithreads[j]; + switch (p->state) { + case alive: continue; /* This one's busy */ + case dead: + { + /* This one just died; grab its status (not that we care, */ + /* however that'll tell the thread library it can clean up) */ + pthread_t thread_id = p->thread_id; + void *status; /* Ignored, but we need to place thread */ + /* status somewhere */ + pthread_mutex_unlock( &col->lock ); + pthread_join( thread_id, &status ); + pthread_mutex_lock( &col->lock ); + p->state = never_was; + } + /* FALL THROUGH */ + case never_was: + /* Now, we can spawn a new thread */ + w->state = p; + if (0 != pthread_create( &p->thread_id, + NULL, worker_thread, w )) { + /* Hmmm, couldn't spawn it; fall back */ + default: /* On error condition */ + pthread_mutex_unlock( &col->lock ); + free(w); + function( detail, col ); + return; + } + + /* We've kicked off the thread */ + p->state = alive; + col->num_alive += 1; + /* For the next request, start scanning at the next */ + /* thread object */ + col->current_ptr = (j+1) % num_thread; + pthread_mutex_unlock( &col->lock ); + return; + } + } + col->num_alive = num_thread; /* Hmmmm, everything was alive??? */ + } + + /* We can't create any more threads; enqueue this (and someone will get */ + /* to it) */ + w->link = 0; + if (col->end_work_queue) { + col->end_work_queue->link = w; + } + col->end_work_queue = w; + if (!col->top_work_queue) col->top_work_queue = w; + + pthread_mutex_unlock( &col->lock ); +} + +/* + * This will wait for all the work items we'e issued to complete + */ +void hss_thread_done(struct thread_collection *col) { + if (!col) return; + + unsigned i; + pthread_mutex_lock( &col->lock ); + for (i=0; inum_thread; i++) { + /* + * Wait for each thread that we have spawned. + * We're the only one that will spawn them, and so we don't have to + * worry about any new ones appearing while we scan through the list + */ + if (col->threads[i].state != never_was) { + void *status; + pthread_t thread_id = col->threads[i].thread_id; + pthread_mutex_unlock( &col->lock ); + pthread_join( thread_id, &status ); + pthread_mutex_lock( &col->lock ); + } + } + pthread_mutex_unlock( &col->lock ); + + /* Ok, all the threads have finished; tear things down */ + + pthread_mutex_destroy( &col->lock ); + pthread_mutex_destroy( &col->write_lock ); + free(col); +} + +void hss_thread_before_write(struct thread_collection *col) { + if (!col) return; + pthread_mutex_lock( &col->write_lock ); +} + +void hss_thread_after_write(struct thread_collection *col) { + if (!col) return; + pthread_mutex_unlock( &col->write_lock ); +} + + +unsigned hss_thread_num_tracks(int num_thread) { + if (num_thread == 0) num_thread = DEFAULT_THREAD; + if (num_thread <= 1) return 1; + if (num_thread >= MAX_THREAD) return MAX_THREAD; + return num_thread; +} diff --git a/src/sig_stateful/lms/external/hss_thread_single.c b/src/sig_stateful/lms/external/hss_thread_single.c new file mode 100644 index 0000000000..d844385293 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_thread_single.c @@ -0,0 +1,63 @@ +#include "hss_thread.h" +#include "config.h" + +/* + * This is a trivial implementation of our threading abstraction. + * It's used if we don't have any threading support + */ + +/* + * This requests that an object that tracks the threads be created. We have + * no threads, hence we don't need such an object + */ +struct thread_collection *hss_thread_init(int num_thread) { + LMS_UNUSED(num_thread); + return 0; +} + +/* + * This asks that function be called sometime between now, and when + * hss_thread_done is called. We just go ahead, and do it now + */ +void hss_thread_issue_work(struct thread_collection *collect, + void (*function)(const void *detail, + struct thread_collection *col), + const void *detail, size_t size_detail_structure) { + LMS_UNUSED(size_detail_structure); + /* If we were asked to make sure something is done, just do it */ + function( detail, collect ); +} + +/* + * This asks for all the work requests we've issued to completed, and that + * the collection object be freed. We did all the work when it was + * requested, and we never allocated a collection object in the first place + */ +void hss_thread_done(struct thread_collection *collect) { + LMS_UNUSED(collect); +} + +/* + * A thread calls this when it will write into a common area (so that no + * other thread will access it at the same time). No threads means that + * there is no need to lock + */ +void hss_thread_before_write(struct thread_collection *collect) { + LMS_UNUSED(collect); +} + +/* + * This releases the above lock + */ +void hss_thread_after_write(struct thread_collection *collect) { + LMS_UNUSED(collect); +} + +/* + * This tells the application that we really have only one thread + * (the main one) + */ +unsigned hss_thread_num_tracks(int num_thread) { + LMS_UNUSED(num_thread); + return 1; +} diff --git a/src/sig_stateful/lms/external/hss_verify.c b/src/sig_stateful/lms/external/hss_verify.c new file mode 100644 index 0000000000..089bdbd1ef --- /dev/null +++ b/src/sig_stateful/lms/external/hss_verify.c @@ -0,0 +1,196 @@ +/* + * This is the code that implements the hierarchical part of the LMS hash + * based signatures + */ +#include +#include "common_defs.h" +#include "hss_verify.h" +#include "lm_verify.h" +#include "lm_common.h" +#include "lm_ots_verify.h" +#include "hash.h" +#include "endian.h" +#include "hss_thread.h" +#include "hss_internal.h" +#include "hss.h" + +/* The HSS public key consists of: */ +/* Number of levels (1-8) (4 bytes) */ +/* The top level LM public key */ + +/* The HSS signature consists of: */ +/* A word giving the number of levels - 1 == L-1 */ +/* L-1 iterations of (i = 1..L-1): */ +/* - LMS Signature of public key i (signed by the pub key of level i-1) */ +/* - LMS Public key (of level i) */ +/* - LMS Signature of the message, signed by the bottomost pub key */ + +/* This is the routine that runs on a thread to validate an LMS signature */ +void validate_internal_sig(const void *data, + struct thread_collection *col) { + const struct verify_detail *d = data; + + bool success = lm_validate_signature(d->public_key, + d->message, d->message_len, false, + d->signature, d->signature_len); + + if (!success) { + /* Drat, it failed; call the failure in */ + hss_thread_before_write(col); + *d->got_error = hss_error_bad_signature; + hss_thread_after_write(col); + } +} + +/* + * Validate an HSS signature, using a public key. Parameters: + * public_key - pointer to the public key + * message - the mmessage that was supposedly signed + * message_len - the size of the message + * siganture - the signature we're checking + * signature_len - the length of the signature + * + * This returns true if everything checks out and the signature verifies + * false on error (whether the error is because the signature didn't verify, + * or we hit some sort of error on the way) + */ +bool hss_validate_signature( + const unsigned char *public_key, + const void *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + unsigned i; + + /* Get the number of levels the signature claims */ + if (signature_len < 4) { + info->error_code = hss_error_bad_signature; + return false; + } + uint_fast32_t levels = get_bigendian( signature, 4 ) + 1; + /* +1 because what's in the signature is levels-1 */ + signature += 4; signature_len -= 4; + if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS || + levels != get_bigendian( public_key, 4 )) { + info->error_code = hss_error_bad_signature; + return false; + } + + /* Compare that to what the public key says */ + uint_fast32_t pub_levels = get_bigendian( public_key, 4 ); + if (levels != pub_levels) { + /* Signature and public key don't agree */ + info->error_code = hss_error_bad_signature; + return false; + } + /* We'll use the LMS public key embedded in the HSS public key as the */ + /* key to use to validate the top level signature */ + public_key += 4; + + struct thread_collection *col = hss_thread_init(info->num_threads); + enum hss_error_code got_error = hss_error_none; + struct verify_detail detail; + detail.got_error = &got_error; + + /* Parse through the signature, kicking off the tasks to validate */ + /* individual LMS signatures within it as we go */ + for (i=0; i + * where: + * - Signature A is the LMS signature of Public Key B + * - Public Key B is the message we're verifying (and will be + * interpreted as a public key in the next iteration) + * public_key points to Public Key A, which is the public key that + * we use to verify Signature A + */ + + /* Get the length of Signature A */ + param_set_t lm_type = get_bigendian( public_key, 4 ); + param_set_t lm_ots_type = get_bigendian( public_key+4, 4 ); + unsigned l_siglen = lm_get_signature_len(lm_type, lm_ots_type); + if (l_siglen == 0 || l_siglen > signature_len) { + info->error_code = hss_error_bad_signature; + goto failed; + } + + /* Retain a pointer to Signature A, and advance the current */ + /* pointer to Public Key B */ + const unsigned char *l_sig = signature; + signature += l_siglen; signature_len -= l_siglen; + + /* The next thing is the next level public key (Public Key B) */ + /* which we need to validate) */ + if (signature_len < 4) { + info->error_code = hss_error_bad_signature; + goto failed; + } + /* + * Get how long Public Key B would be, assuming it is a valid + * public key. If it's not a valid public key (that is, if + * someone other than the valid signer modified it), then + * Signature A will not validate, and so we'll catch that + */ + lm_type = get_bigendian( signature, 4 ); + unsigned l_pubkeylen = lm_get_public_key_len(lm_type); + if (l_pubkeylen == 0 || l_pubkeylen > signature_len) { + info->error_code = hss_error_bad_signature; + goto failed; + } + + /* Retain a pointer to Public Key B, and advance the current */ + /* pointer past it (to the data the next iteration cares about) */ + const unsigned char *l_pubkey = signature; + signature += l_pubkeylen; signature_len -= l_pubkeylen; + + /* Now, schedule the validation of Signature A */ + detail.public_key = public_key; /* Public key A */ + detail.message = l_pubkey; /* Public key B, that is, */ + /* the message to validate */ + detail.message_len = l_pubkeylen; + detail.signature = l_sig; /* Signature A */ + detail.signature_len = l_siglen; + hss_thread_issue_work( col, validate_internal_sig, + &detail, sizeof detail ); + + /* We validated this level's public key (or, at least, scheduled */ + /* it, if it turns out not to validate, we'll catch it below) */ + /* Use the current Public Key B as the next level's Public Key A */ + public_key = l_pubkey; + } + + /* + * We're at the bottom level; now, the current position in the signature + * looks like (or, rather, is *supposed to look like*) this: + * + * where: + * - Signature A is the bottom signature, which signs the actual + * message + * public_key points to the bottom level public key, which is used to + * validate the signature + * + * Just go ahead and schedule the validation + */ + detail.public_key = public_key; /* Public key to use */ + detail.message = message; /* The user's message that needs */ + detail.message_len = message_len; /* validation */ + detail.signature = signature; /* Bottom level LMS signature */ + detail.signature_len = signature_len; + hss_thread_issue_work( col, validate_internal_sig, + &detail, sizeof detail ); + + /* Wait for all the threads to complete */ + hss_thread_done(col); + + /* It succeeded if none of the threads reported an error */ + if (got_error == hss_error_none) return true; + info->error_code = got_error; + return false; + +failed: /* If we get an intermediate failure */ + hss_thread_done(col); + return false; +} diff --git a/src/sig_stateful/lms/external/hss_verify.h b/src/sig_stateful/lms/external/hss_verify.h new file mode 100644 index 0000000000..7a29deb275 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_verify.h @@ -0,0 +1,23 @@ +#if !defined( HSS_VERIFY_H_ ) +#define HSS_VERIFY_H_ + +#include + +struct hss_extra_info; +/* + * This is the function to validate a signature; return true if it validates, + * false if it doesn't + * + * public_key is the pointer to the public key + * + * message, message_len is the message to validate + * + * signature, signature_len is the signature to validate + */ +bool hss_validate_signature( + const unsigned char *public_key, + const void *message, size_t message_len, + const unsigned char *signature, size_t signature_len, + struct hss_extra_info *info); + +#endif /* HSS_VERIFY_H_ */ diff --git a/src/sig_stateful/lms/external/hss_verify_inc.c b/src/sig_stateful/lms/external/hss_verify_inc.c new file mode 100644 index 0000000000..451082f8de --- /dev/null +++ b/src/sig_stateful/lms/external/hss_verify_inc.c @@ -0,0 +1,203 @@ +/* + * This is the code that implements the hierarchical part of the LMS hash + * based signatures; in this case, incremental verification + */ +#include +#include "common_defs.h" +#include "hss_verify_inc.h" +#include "lm_verify.h" +#include "lm_common.h" +#include "lm_ots_verify.h" +#include "hash.h" +#include "endian.h" +#include "hss_thread.h" +#include "hss_internal.h" +#include "lm_ots_common.h" +#include "hss.h" + +/* + * Start the process of validating an HSS signature incrementally. Parameters: + * ctx - The state we'll use to track the incremental validation + * public_key - pointer to the public key + * siganture - the signature we're checking + * signature_len - the length of the signature + */ +bool hss_validate_signature_init( + struct hss_validate_inc *ctx, + const unsigned char *public_key, + const unsigned char *signature, size_t signature_len, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + unsigned i; + if (!ctx) { + info->error_code = hss_error_got_null; + return false; + } + ctx->status = hss_error_ctx_uninitialized; /* Until we hear otherwise, */ + /* we got a failure */ + + const unsigned char *orig_signature = signature; +; + /* Get the number of levels the signature claims */ + if (signature_len < 4) { + ctx->status = info->error_code = hss_error_bad_signature; + return false; + } + uint_fast32_t levels = get_bigendian( signature, 4 ) + 1; + /* +1 because what's in the signature is levels-1 */ + signature += 4; signature_len -= 4; + if (levels < MIN_HSS_LEVELS || levels > MAX_HSS_LEVELS || + levels != get_bigendian( public_key, 4 )) { + ctx->status = info->error_code = hss_error_bad_signature; + return false; + } + uint_fast32_t pub_levels = get_bigendian( public_key, 4 ); + if (levels != pub_levels) { + /* Signature and public key don't agree */ + ctx->status = info->error_code = hss_error_bad_signature; + return false; + } + public_key += 4; + + /* Validate the upper levels of the signature */ + struct thread_collection *col = NULL; + if (levels > 1) { + col = hss_thread_init(info->num_threads); + enum hss_error_code got_error = hss_error_none; + struct verify_detail detail; + detail.got_error = &got_error; + + /* Scan through the signature, kicking off the tasks to validate it */ + /* as we go. Note that we don't validate the bottom level yet */ + for (i=0; i signature_len) goto failed; + const unsigned char *l_sig = signature; + signature += l_siglen; signature_len -= l_siglen; + + /* The next thing is the next level public key (which we need */ + /* to validate) */ + if (signature_len < 4) goto failed; + lm_type = get_bigendian( signature, 4 ); + unsigned l_pubkeylen = lm_get_public_key_len(lm_type); + if (l_pubkeylen == 0 || l_pubkeylen > signature_len) goto failed; + const unsigned char *l_pubkey = signature; + signature += l_pubkeylen; signature_len -= l_pubkeylen; + + /* Validate the signature of this level's public key */ + detail.public_key = public_key; + detail.message = l_pubkey; + detail.message_len = l_pubkeylen; + detail.signature = l_sig; + detail.signature_len = l_siglen; + hss_thread_issue_work( col, validate_internal_sig, + &detail, sizeof detail ); + + /* We validated this level's public key (or, at least, */ + /* scheduled it, if it turns out not to validate, we'll catch */ + /* it below), use it to validate the next level */ + public_key = l_pubkey; + } + + /* Wait for all the threads to complete */ + hss_thread_done(col); + col = NULL; + + if (got_error != hss_error_none) { + ctx->status = info->error_code = got_error; + return false; + } + } + + ctx->signature_offset = signature - orig_signature; + ctx->signature_len = signature_len; + + /* We have the public key in front of us; stash a copy */ + /* Right now, we have a fixed length public key */ + /* If that changes, we'll need to investigate the parmaeter set */ + memcpy( ctx->final_public_key, public_key, 8 + I_LEN + MAX_HASH ); + + /* Now, initialize the context */ + param_set_t ots_type = get_bigendian( public_key+4, 4 ); + + unsigned h, n; + if (!lm_ots_look_up_parameter_set(ots_type, &h, &n, NULL, NULL, NULL)) { + /* Because we're checking in parallel, this may be caused by */ + /* a bad signature */ + ctx->status = info->error_code = hss_error_bad_signature; + return false; + } + ctx->h = h; + hss_init_hash_context( h, &ctx->hash_ctx ); + { + unsigned char prefix[ MESG_PREFIX_MAXLEN ]; + memcpy( prefix + MESG_I, ctx->final_public_key+8, I_LEN ); + memcpy( prefix + MESG_Q, signature, 4 ); /* q */ + SET_D( prefix + MESG_D, D_MESG ); + memcpy( prefix + MESG_C, signature+8, n ); /* C */ + hss_update_hash_context(h, &ctx->hash_ctx, prefix, MESG_PREFIX_LEN(n) ); + } + + /* It succeeded so far... */ + ctx->status = hss_error_none; + return true; + +failed: /* If we get an intermediate failure */ + if (col) hss_thread_done(col); + ctx->status = info->error_code = hss_error_bad_signature; + return false; +} + +/* This adds another piece of the message to validate */ +bool hss_validate_signature_update( + struct hss_validate_inc *ctx, + const void *message_segment, + size_t len_message_segment) { + if (!ctx || ctx->status != hss_error_none) return false; + + hss_update_hash_context(ctx->h, &ctx->hash_ctx, + message_segment, len_message_segment ); + + return true; +} + +/* We've added all the pieces of the messages, now do the validation */ +bool hss_validate_signature_finalize( + struct hss_validate_inc *ctx, + const unsigned char *signature, + struct hss_extra_info *info) { + struct hss_extra_info temp_info = { 0 }; + if (!info) info = &temp_info; + + if (!ctx) { + info->error_code = hss_error_got_null; + return false; + } + if (ctx->status != hss_error_none) { + info->error_code = ctx->status; + return false; + } + + /* Success or fail, we can't use the context any more */ + ctx->status = hss_error_ctx_already_used; + + /* Generate the final hash */ + unsigned char hash[ MAX_HASH ]; + unsigned h = ctx->h; + hss_finalize_hash_context( h, &ctx->hash_ctx, hash ); + + /* It passes iff the final signature validates */ + if (lm_validate_signature( + ctx->final_public_key, + hash, sizeof hash, true, + signature + ctx->signature_offset, ctx->signature_len)) { + return true; + } + + info->error_code = hss_error_bad_signature; + return false; +} diff --git a/src/sig_stateful/lms/external/hss_verify_inc.h b/src/sig_stateful/lms/external/hss_verify_inc.h new file mode 100644 index 0000000000..147308b23c --- /dev/null +++ b/src/sig_stateful/lms/external/hss_verify_inc.h @@ -0,0 +1,82 @@ +#if !defined( HSS_VERIFY_INC_H_ ) +#define HSS_VERIFY_INC_H_ +#include +#include +#include "hash.h" +#include "common_defs.h" +#include "hss.h" + +/* + * These are the functions to validate a signature incrementally. + * That is, we assume that we don't have the entire message at + * once, instead, we have it in pieces (for example, the signature + * is of a multigigabyte file) + * + * Usage: + * struct hss_validate_inc ctx; + * bool success = hss_validate_init( &ctx, public_key, signature ); + * hss_validate_update( &ctx, message_part_1, len_1 ); + * hss_validate_update( &ctx, message_part_2, len_2 ); + * hss_validate_update( &ctx, message_part_3, len_3 ); + * success = hss_validate_finalize( &ctx, signature ); + * if (success) printf( "The signature validated\n" ); + * + * This is in its own include file because we need to import some + * 'not-generally-for-general-consumption' include files to make + * it work (as they're in the hss_validate_inc structure) + */ + +/* + * This is the context structure that holds the intermedate results of an + * in-process validation + * It's a application-visible structure for ease of use: the application can + * allocate it as an automatic, and if the application aborts in the middle of + * the validation, it doesn't cause a memory leak + */ +struct hss_validate_inc { + enum hss_error_code status; /* Either hss_error_none if we're in */ + /* process, or the reason why we'd fail */ + size_t signature_offset; /* Offset of the final signature within the */ + /* HSS signature */ + size_t signature_len; /* Length of the final signature */ + + unsigned h; /* Hash function used */ + + /* The final public key. We need this at finalization time, */ + /* however they might not be in the signature (L=1 case) */ + unsigned char final_public_key[8 + I_LEN + MAX_HASH]; + + union hash_context hash_ctx; /* For the running hash we use */ +}; + +struct hss_extra_info; + +/* Starts off the process of incrementally validating a signature */ +/* If it detects a failure, this returns false */ +/* Handing the return code is optional; if this fails, the finalization */ +/* step will fail too */ +bool hss_validate_signature_init( + struct hss_validate_inc *ctx, + const unsigned char *public_key, + const unsigned char *signature, size_t signature_len, + struct hss_extra_info *info); + +/* This adds another piece of the message to validate */ +/* Again, the result code is optional */ +bool hss_validate_signature_update( + struct hss_validate_inc *ctx, + const void *message_segment, + size_t len_message_segment); + +/* This finalizes the signature validation */ +/* This returns true if the signature validates (and we didn't detect any */ +/* intermediate failures) */ +/* We ask the caller to pass in the signature again, because we'd prefer */ +/* not having to place the final LMS signature in the ctx structure; that'd */ +/* make it larger than we'd like */ +bool hss_validate_signature_finalize( + struct hss_validate_inc *ctx, + const unsigned char *signature, + struct hss_extra_info *info); + +#endif /* HSS_VERIFY_INC_H_ */ diff --git a/src/sig_stateful/lms/external/hss_zeroize.c b/src/sig_stateful/lms/external/hss_zeroize.c new file mode 100644 index 0000000000..f2bd334903 --- /dev/null +++ b/src/sig_stateful/lms/external/hss_zeroize.c @@ -0,0 +1,49 @@ +#include "hss_zeroize.h" +#include + +/* + * This is a function to zeroize a section of memory + * + * We do this because when we release a section of memory (either because it's + * a local variable going out of scope, or we free it), it's possible that + * the memory will retain its contents after another allocation (possibly + * done by someone outside this module). So, to avoid this potential security + * issue, we scrub the memory (at least, the parts that have data that would + * make it possible to forge if it leaked) before releasing it. + * + * Now, there's a bunch of things we don't mind being exposed (e.g. internal + * node values of Merkle trees), so we don't use this everywhere; only where + * it is needed + * + * We use this, rather than having routines simply call memset, to avoid + * potential problems with overenthusiastic optimizers. Generally, we zeroize + * an area immediately before it goes out of scope or we free it, however an + * optimizer might conclude "they're about to release the memory, there's no + * need to write to it first" + * + * For similar reasons, this function is in its own source file (so that a + * compiler optimizer who doesn't examine more than one source at a time can't + * eliminate it). If we are worried about optimizers who can be even more + * enthusiastic, there are other things we can try; however we're not going to + * worry about that right now + */ +void hss_zeroize( void *area, size_t len ) { +#if defined( __STDC_LIB_EXT1__ ) + /* + * C11 defines a version of memset that does precisely what we want, and is + * guaranteed not to be molested by the optimizer + * Note that the first 'len' is supposed to be the length of the buffer + * we're cleaning and the second 'len' is the area to clear. Since we + * expect the caller to ask us to clear the entire area (and hence gives + * us only one length), we use the same for both + */ + memset_s( area, len, 0, len ); +#else + /* + * Fallback code for pre-C11 versions + */ + volatile unsigned char *p = area; + + while (len--) *p++ = 0; +#endif +} diff --git a/src/sig_stateful/lms/external/hss_zeroize.h b/src/sig_stateful/lms/external/hss_zeroize.h new file mode 100644 index 0000000000..702d91137b --- /dev/null +++ b/src/sig_stateful/lms/external/hss_zeroize.h @@ -0,0 +1,10 @@ +#if !defined( HSS_ZEROIZE_H_ ) +#define HSS_ZEROIZE_H_ + +#include + +/* Zeroize an area, that is, scrub it from holding any potentially secret */ +/* information */ +void hss_zeroize( void *area, size_t len ); + +#endif /* HSS_ZEROIZE_H_ */ diff --git a/src/sig_stateful/lms/external/license.txt b/src/sig_stateful/lms/external/license.txt new file mode 100644 index 0000000000..80b9ea7f55 --- /dev/null +++ b/src/sig_stateful/lms/external/license.txt @@ -0,0 +1,29 @@ +****************************************************************************** +Copyright (c) 2017 Cisco Systems, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. +Neither the name of the Cisco Systems, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. +****************************************************************************** diff --git a/src/sig_stateful/lms/external/lm_common.c b/src/sig_stateful/lms/external/lm_common.c new file mode 100644 index 0000000000..e3eb56f0f0 --- /dev/null +++ b/src/sig_stateful/lms/external/lm_common.c @@ -0,0 +1,79 @@ +/* + * This is the code that implements the tree part of the LMS hash + * based signatures + */ +#include +#include "lm_common.h" +#include "hash.h" +#include "common_defs.h" +#include "lm_ots_common.h" + +/* + * Internal utility to convert encoded parameter sets into what they represent + */ +bool lm_look_up_parameter_set(param_set_t parameter_set, + unsigned *h, unsigned *n, unsigned *height) { + unsigned v_h, v_n, v_height; + switch (parameter_set) { + case LMS_SHA256_N32_H5: + v_h = HASH_SHA256; v_n = 32; v_height = 5; break; + case LMS_SHA256_N32_H10: + v_h = HASH_SHA256; v_n = 32; v_height = 10; break; + case LMS_SHA256_N32_H15: + v_h = HASH_SHA256; v_n = 32; v_height = 15; break; + case LMS_SHA256_N32_H20: + v_h = HASH_SHA256; v_n = 32; v_height = 20; break; + case LMS_SHA256_N32_H25: + v_h = HASH_SHA256; v_n = 32; v_height = 25; break; + default: return false; + } + + if (h) *h = v_h; + if (n) *n = v_n; + if (height) *height = v_height; + + return true; +} + +/* The LM public key consists of: */ +#define LM_PUB_PARM_SET 0 /* The parameter set (4 bytes) */ +#define LM_PUB_OTS_PARM_SET 4 /* The OTS parameter set (4 bytes) */ +#define LM_PUB_I 8 /* Our nonce (I) value (16 bytes) */ +/* The root value comes here */ + +/* + * XDR requires us to pad the I value out to a multiple of 4 + * This computes how long the field will be after padding + * That is, it rounds len_I up to the next multiple of 4 + */ +#define padded_length(len_I) (((len_I) + 3) & ~3) + +/* The public key just consists of the parameter sets, plus I, plus root hash */ +size_t lm_get_public_key_len(param_set_t lm_type) { + unsigned n; + if (!lm_look_up_parameter_set( lm_type, 0, &n, 0)) + return 0; + + return LM_PUB_I + padded_length(I_LEN) + n; +} + +/* + * The amount of space we use for signature + */ +size_t lm_get_signature_len(param_set_t lm_type, + param_set_t lm_ots_type) { + unsigned n, height; + if (!lm_look_up_parameter_set( lm_type, 0, &n, &height )) + return 0; + + int ots_sig_len = lm_ots_get_signature_len(lm_ots_type); + if (ots_sig_len == 0) + return 0; + + /* + * The LM signature consists of the type code, the diversification factor, + * the LM-OTS signature (which includes the OTS type code), and the + * authentication path (which is an array of height hashes) + */ + return 4 + 4 + ots_sig_len + n*height; +} diff --git a/src/sig_stateful/lms/external/lm_common.h b/src/sig_stateful/lms/external/lm_common.h new file mode 100644 index 0000000000..027eda2214 --- /dev/null +++ b/src/sig_stateful/lms/external/lm_common.h @@ -0,0 +1,20 @@ +#if !defined(LM_COMMON_H_) +#define LM_COMMON_H_ + +#include +#include "common_defs.h" + +size_t lm_get_public_key_len(param_set_t lm_type); +size_t lm_get_signature_len(param_set_t lm_type, + param_set_t lm_ots_type); + +bool lm_look_up_parameter_set(param_set_t parameter_set, + unsigned *h, unsigned *n, unsigned *height); + +/* The format of an LM public key; it consists of: */ +#define LM_PUB_PARM_SET 0 /* The parameter set (4 bytes) */ +#define LM_PUB_OTS_PARM_SET 4 /* The OTS parameter set (4 bytes) */ +#define LM_PUB_I 8 /* Our nonce (I) value (32 or 64 bytes) */ +/* The root value comes here */ + +#endif /* LM_COMMON_H_ */ diff --git a/src/sig_stateful/lms/external/lm_ots.h b/src/sig_stateful/lms/external/lm_ots.h new file mode 100644 index 0000000000..4fcf690342 --- /dev/null +++ b/src/sig_stateful/lms/external/lm_ots.h @@ -0,0 +1,64 @@ +#if !defined( LM_OTS_H_ ) +#define LM_OTS_H_ + +#include "common_defs.h" +#include + +/* + * These are routines that implement the OTS signature scheme. These routines + * never actually form a "private key"; instead, the signer passes the 'seed' + * (and public data) to form the public key and to do the actual signature. + * We do this because the LM routines are actually better suited for doing + * seed management. + */ +struct seed_derive; + +/* + * Compute the public key. Note that it doesn't compute a 'private key'; + * the signature algorithm gets that data when we pass the parameters again + * Parameters: + * lm_ots_type - The parameter set + * I - The I public identifier to use + * q - The diversification string, passed as a 4 byte integer + * seed - The structure used to generate seeds + * public_key - Where to place the public key + * public_key_len - The length of the above buffer + * This returns true on success + */ +bool lm_ots_generate_public_key( + param_set_t lm_ots_type, + const unsigned char *I, /* Public key identifier */ + merkle_index_t q, /* Diversification string, 4 bytes value */ + struct seed_derive *seed, + unsigned char *public_key, size_t public_key_len); + +/* + * Sign a message. Warning: the caller is expected to make sure that it signs + * only one message with a given seed/I/q set + * Parameters: + * lm_ots_type - The parameter set + * I - The I public identifier to use + * q - The diversification string, passed as a 4 byte integer + * seed - The structure used to generate seeds + * message - Message to sign + * message_len - Length of the message + * prehashed - Set if the message hashing has already taken place + * signature - Where to place the signature + * signature_len - The length of the above buffer + * This returns true on success + */ +bool lm_ots_generate_signature( + param_set_t lm_ots_type, + const unsigned char *I, + merkle_index_t q, + struct seed_derive *seed, + const void *message, size_t message_len, bool prehashed, + unsigned char *signature, size_t signature_len); + +/* The include file for the verification routine */ +#include "lm_ots_verify.h" + +/* The include file for the common access routines */ +#include "lm_ots_common.h" + +#endif /* LM_OTS_H_ */ diff --git a/src/sig_stateful/lms/external/lm_ots_common.c b/src/sig_stateful/lms/external/lm_ots_common.c new file mode 100644 index 0000000000..45672e18b2 --- /dev/null +++ b/src/sig_stateful/lms/external/lm_ots_common.c @@ -0,0 +1,99 @@ +/* + * This is the code that implements the one-time-signature part of the LMS hash + * based signatures + */ +#include "lm_ots_common.h" +#include "common_defs.h" +#include "hash.h" + +/* + * Convert the external name of a parameter set into the set of values we care + * about + */ +bool lm_ots_look_up_parameter_set(param_set_t parameter_set, + unsigned *h, unsigned *n, unsigned *w, unsigned *p, unsigned *ls) { + unsigned v_h, v_n, v_w, v_p, v_ls; + switch (parameter_set) { + case LMOTS_SHA256_N32_W1: + v_h = HASH_SHA256; v_n = 32; v_w = 1; v_p = 265; v_ls = 7; break; + case LMOTS_SHA256_N32_W2: + v_h = HASH_SHA256; v_n = 32; v_w = 2; v_p = 133; v_ls = 6; break; + case LMOTS_SHA256_N32_W4: + v_h = HASH_SHA256; v_n = 32; v_w = 4; v_p = 67; v_ls = 4; break; + case LMOTS_SHA256_N32_W8: + v_h = HASH_SHA256; v_n = 32; v_w = 8; v_p = 34; v_ls = 0; break; + default: return false; + } + + if (h) *h = v_h; + if (n) *n = v_n; + if (w) *w = v_w; + if (p) *p = v_p; + if (ls) *ls = v_ls; + + return true; +} + +/* The public key just consists of the bare hash */ +size_t lm_ots_get_public_key_len(param_set_t lm_ots_type) { + unsigned n; + if (!lm_ots_look_up_parameter_set( lm_ots_type, 0, &n, 0, 0, 0 )) + return 0; + + return n; +} + +/* Return the length of a signature */ +size_t lm_ots_get_signature_len(param_set_t lm_ots_type) { + unsigned n, p; + + if (!lm_ots_look_up_parameter_set( lm_ots_type, 0, &n, 0, &p, 0 )) + return 0; + + return 4 + n + p*n; +} + +/* Return the number of hashes we need to compute to generate a public key */ +unsigned lm_ots_hashes_per_public_key(param_set_t lm_ots_type) { + unsigned wint, num_dig; + if (!lm_ots_look_up_parameter_set(lm_ots_type, + NULL, NULL, &wint, &num_dig, NULL)) { + return 0; + } + + /* Total number of hash invocations: + * For each digit, we expand the seed (1), and then perform (2**wint-1) + * haashes to obtain the end of the chain + * Then, we hash all the ends of the chains together + * If we were to return the number of hash compression operations, + * the final 1 would be a bit larger + */ + return num_dig * (1 << wint) + 1; +} + +/* Todo: some of these values depend only on w; why do we need to recompute */ +/* them each time??? */ +unsigned lm_ots_coef(const unsigned char *Q, unsigned i, unsigned w) { + unsigned index = (i * w) / 8; /* Which byte holds the coefficient */ + /* we want */ + unsigned digits_per_byte = 8/w; + unsigned shift = w * (~i & (digits_per_byte-1)); /* Where in the byte */ + /* the coefficient is */ + unsigned mask = (1<> shift) & mask; +} + +/* This returns the Winternitz checksum to append to the hash */ +unsigned lm_ots_compute_checksum(const unsigned char *Q, unsigned Q_len, + unsigned w, unsigned ls) { + unsigned sum = 0; + unsigned i; + unsigned u = 8 * Q_len / w; + unsigned max_digit = (1< +#include "common_defs.h" + +bool lm_ots_look_up_parameter_set(param_set_t parameter_set, + unsigned *h, unsigned *n, unsigned *w, unsigned *p, unsigned *ls); +size_t lm_ots_get_public_key_len(param_set_t lm_ots_type); +size_t lm_ots_get_signature_len(param_set_t lm_ots_type); +unsigned lm_ots_hashes_per_public_key(param_set_t lm_ots_type); +unsigned lm_ots_compute_checksum(const unsigned char *Q, unsigned Q_len, + unsigned w, unsigned ls); +unsigned lm_ots_coef(const unsigned char *Q, unsigned i, unsigned w); + +#endif /* LM_OTS_COMMON_H_ */ diff --git a/src/sig_stateful/lms/external/lm_ots_sign.c b/src/sig_stateful/lms/external/lm_ots_sign.c new file mode 100644 index 0000000000..15decfcced --- /dev/null +++ b/src/sig_stateful/lms/external/lm_ots_sign.c @@ -0,0 +1,168 @@ +/* + * This is the code that implements the one-time-signature part of the LMS hash + * based signatures + */ +#include +#include "common_defs.h" +#include "lm_ots.h" +#include "lm_ots_common.h" +#include "hash.h" +#include "endian.h" +#include "hss_zeroize.h" +#include "hss_derive.h" +#include "hss_internal.h" + +bool lm_ots_generate_public_key( + param_set_t lm_ots_type, + const unsigned char *I, /* Public key identifier */ + merkle_index_t q, /* Diversification string, 4 bytes value */ + struct seed_derive *seed, + unsigned char *public_key, size_t public_key_len) { + + /* Look up the parameter set */ + unsigned h, n, w, p, ls; + LMS_UNUSED(public_key_len); + if (!lm_ots_look_up_parameter_set( lm_ots_type, &h, &n, &w, &p, &ls )) + return false; + + /* Start the hash that computes the final value */ + union hash_context public_ctx; + hss_init_hash_context(h, &public_ctx); + { + unsigned char prehash_prefix[ PBLC_PREFIX_LEN ]; + memcpy( prehash_prefix + PBLC_I, I, I_LEN ); + put_bigendian( prehash_prefix + PBLC_Q, q, 4 ); + SET_D( prehash_prefix + PBLC_D, D_PBLC ); + hss_update_hash_context(h, &public_ctx, prehash_prefix, + PBLC_PREFIX_LEN ); + } + + /* Now generate the public key */ + /* This is where we spend the majority of the time during key gen and */ + /* signing operations; it would make sense to attempt to try to take */ + /* advantage of parallel (SIMD) hardware; even if we use it nowhere */ + /* else, we'd get a significant speed up */ + unsigned i, j; + + unsigned char buf[ ITER_MAX_LEN ]; + memcpy( buf + ITER_I, I, I_LEN ); + put_bigendian( buf + ITER_Q, q, 4 ); + union hash_context ctx; + + hss_seed_derive_set_j( seed, 0 ); + + for (i=0; i +#include "lm_ots_verify.h" +#include "lm_ots_common.h" +#include "hash.h" +#include "endian.h" +#include "common_defs.h" + +/* + * This validate a OTS signature for a message. It doesn't actually use the + * public key explicitly; instead, it just produces the root key, based on the + * message; the caller is assumed to compare it to the expected value + * Parameters: + * - computed_public_key - where to place the reconstructed root. It is + * assumed that the caller has allocated enough space + * - I: the nonce value ("I") to use + * - q: diversification string + * - message - the message to verify + * - message_len - the length of the message + * - message_prehashed - true if the message has already undergone the initial + * (D_MESG) hash + * - signature - the signature + * - signature_len - the length of the signature + * - parameter_set - what we expect the parameter set to be + * + * This returns true on successfully recomputing a root value; whether it is + * the right one is something the caller would need to verify + */ +bool lm_ots_validate_signature_compute( + unsigned char *computed_public_key, + const unsigned char *I, merkle_index_t q, + const void *message, size_t message_len, bool message_prehashed, + const unsigned char *signature, size_t signature_len, + param_set_t expected_parameter_set) { + if (signature_len < 4) return false; /* Ha, ha, very funny... */ + + /* We don't trust the parameter set that's in the signature; verify it */ + param_set_t parameter_set = get_bigendian( signature, 4 ); + if (parameter_set != expected_parameter_set) { + return false; + } + + unsigned h, n, w, p, ls; + if (!lm_ots_look_up_parameter_set( parameter_set, &h, &n, &w, &p, &ls )) + return false; + + if (signature_len != 4 + n * (p+1)) return false; + + const unsigned char *C = signature + 4; + const unsigned char *y = C + n; + + unsigned char Q[MAX_HASH + 2]; + if (message_prehashed) { + memcpy( Q, message, n ); + } else { + union hash_context ctx; + /* Compute the initial hash */ + hss_init_hash_context(h, &ctx); + /* Hash the message prefix */ + { + unsigned char prefix[ MESG_PREFIX_MAXLEN ]; + memcpy( prefix + MESG_I, I, I_LEN ); + put_bigendian( prefix + MESG_Q, q, 4 ); + SET_D( prefix + MESG_D, D_MESG ); + memcpy( prefix + MESG_C, C, n ); + hss_update_hash_context(h, &ctx, prefix, MESG_PREFIX_LEN(n) ); + } + /* Then, the message */ + hss_update_hash_context(h, &ctx, message, message_len ); + + hss_finalize_hash_context( h, &ctx, Q ); + } + + /* Append the checksum to the randomized hash */ + put_bigendian( &Q[n], lm_ots_compute_checksum(Q, n, w, ls), 2 ); + + /* And, start building the parts for the final hash */ + union hash_context final_ctx; + hss_init_hash_context(h, &final_ctx); + { + unsigned char prehash_prefix[ PBLC_PREFIX_LEN ]; + memcpy( prehash_prefix + PBLC_I, I, I_LEN ); + put_bigendian( prehash_prefix + PBLC_Q, q, 4 ); + SET_D( prehash_prefix + PBLC_D, D_PBLC ); + hss_update_hash_context(h, &final_ctx, prehash_prefix, + PBLC_PREFIX_LEN ); + } + + unsigned i; + unsigned char tmp[ITER_MAX_LEN]; + + /* Preset the parts of tmp that don't change */ + memcpy( tmp + ITER_I, I, I_LEN ); + put_bigendian( tmp + ITER_Q, q, 4 ); + + unsigned max_digit = (1< +#include "common_defs.h" + +/* + * This validates an OTS signature, but instead of producing a SUCCESS/FAILURE + * return, it generates the root value (which the caller is expected to check). + * It can return false (failure), for things such as unrecognized parameter + * set It also makes sure that the parameter set of the signature is that + * value (as we need to make sure that the attacker didn't substitute a + * weaker one) + */ +bool lm_ots_validate_signature_compute( + unsigned char *computed_public_key, + const unsigned char *I, + merkle_index_t q, /* Diversification string, 4 bytes value */ + const void *message, size_t message_len, bool prehashed, + const unsigned char *signature, size_t signature_len, + param_set_t expected_parameter_set); + +#endif /* LM_OTS_VERIFY_H_ */ diff --git a/src/sig_stateful/lms/external/lm_verify.c b/src/sig_stateful/lms/external/lm_verify.c new file mode 100644 index 0000000000..46b3627885 --- /dev/null +++ b/src/sig_stateful/lms/external/lm_verify.c @@ -0,0 +1,107 @@ +/* + * This is the code that implements the tree part of the LMS hash + * based signatures + */ +#include +#include "lm_verify.h" +#include "lm_common.h" +#include "lm_ots_common.h" +#include "lm_ots_verify.h" +#include "hash.h" +#include "endian.h" +#include "common_defs.h" + +/* + * XDR requires us to pad the I value out to a multiple of 4 + * This computes how long the field will be after padding + * That is, it rounds len_I up to the next multiple of 4 + */ +#define padded_length(len_I) (((len_I) + 3) & ~3) + +/* + * This validate an LM signature for a message. It does take an XDR-encoded + * signature, and verify against it. + * Parameters: + * - public_key - the XDR-encoded public ley + * - message - the message to verify + * - message_len - the length of the message + * - signature - the signature + * - signature_len - the length of the signature + * + * This returns true if the signature verifies + */ +bool lm_validate_signature( + const unsigned char *public_key, + const void *message, size_t message_len, bool prehashed, + const unsigned char *signature, size_t signature_len) { + union hash_context ctx; + + param_set_t lm_type = get_bigendian( public_key + LM_PUB_PARM_SET, 4 ); + param_set_t ots_type = get_bigendian( public_key + LM_PUB_OTS_PARM_SET, 4 ); + + unsigned h, n, height; + if (!lm_look_up_parameter_set(lm_type, &h, &n, &height)) return false; + + unsigned char computed_public_key[MAX_HASH]; + + const unsigned char *I = public_key + LM_PUB_I; + + if (signature_len < 8) return false; + merkle_index_t count = get_bigendian( signature, 4 ); + signature += 4; signature_len -= 4; /* 4 bytes, rather then 8 */ + /* the OTS type is expected to be a part of the OTS signature, */ + /* which lm_ots_validate_signature_compute will expect */ + + /* Compute the OTS root */ + size_t ots_publen = lm_ots_get_public_key_len(ots_type); + size_t ots_siglen = lm_ots_get_signature_len(ots_type); + if (ots_publen == 0 || ots_siglen == 0) return false; + if (signature_len < ots_siglen) return false; + + unsigned char ots_sig[LEAF_MAX_LEN]; + if (!lm_ots_validate_signature_compute(ots_sig + LEAF_PK, I, count, + message, message_len, prehashed, + signature, ots_siglen, ots_type)) return false; + signature += ots_siglen; signature_len -= ots_siglen; + + /* Get the parameter set declared in the sigature; make sure it matches */ + /* what we expect */ + if (signature_len < 4) return false; + param_set_t parameter_set = get_bigendian( signature, 4 ); + if (parameter_set != lm_type) return false; + signature += 4; signature_len -= 4; + + merkle_index_t count_nodes = (merkle_index_t)1 << height; + + if (signature_len != n * height) return false; /* We expect the auth */ + /* path to be there as the last element */ + if (count >= count_nodes) return false; /* Index out of range */ + merkle_index_t node_num = count + count_nodes; + + memcpy( ots_sig + LEAF_I, I, I_LEN ); + put_bigendian( ots_sig + LEAF_R, node_num, 4 ); + SET_D( ots_sig + LEAF_D, D_LEAF ); + hss_hash_ctx( computed_public_key, h, &ctx, ots_sig, LEAF_LEN(n) ); + + unsigned char prehash[ INTR_MAX_LEN ]; + memcpy( prehash + INTR_I, I, I_LEN ); + SET_D( prehash + INTR_D, D_INTR ); + while (node_num > 1) { + if (node_num % 2) { + memcpy( prehash + INTR_PK + 0, signature, n ); + memcpy( prehash + INTR_PK + n, computed_public_key, n ); + } else { + memcpy( prehash + INTR_PK + 0, computed_public_key, n ); + memcpy( prehash + INTR_PK + n, signature, n ); + } + signature += n; + node_num /= 2; + put_bigendian( prehash + INTR_R, node_num, 4 ); + hss_hash_ctx( computed_public_key, h, &ctx, prehash, INTR_LEN(n) ); + } + + /* Now, check to see if the root we computed matches the root we should have */ + unsigned offset = LM_PUB_I + padded_length(I_LEN); + + return 0 == memcmp( computed_public_key, public_key + offset, n ); +} diff --git a/src/sig_stateful/lms/external/lm_verify.h b/src/sig_stateful/lms/external/lm_verify.h new file mode 100644 index 0000000000..7f48767fcb --- /dev/null +++ b/src/sig_stateful/lms/external/lm_verify.h @@ -0,0 +1,12 @@ +#if !defined(LM_VERIFY_H_) +#define LM_VERIFY_H_ + +#include +#include + +bool lm_validate_signature( + const unsigned char *public_key, + const void *message, size_t message_len, bool prehashed, + const unsigned char *signature, size_t signature_len); + +#endif /* LM_VERIFY_H_ */ diff --git a/src/sig_stateful/lms/external/programming.notes b/src/sig_stateful/lms/external/programming.notes new file mode 100644 index 0000000000..c9c7369b2c --- /dev/null +++ b/src/sig_stateful/lms/external/programming.notes @@ -0,0 +1,674 @@ +These are some notes about the internal programming style of the HSS +subsystem. These are of no interest to someone who just wants to use LMS +signatures; they might give some insight to someone spelunking through the +sources. + +- General philosophy + This subsystem implements the HSS siganture scheme, which is a moderately + complex data structure, involving multiple Merkle trees. To make things + even more fun, we implement restarting, that is, loading a private key + into memory, even if that private key has already generated some signatures + (and constructing the Merkle tree structures to reflect those signatures), + and multithreading (that is, we can spread many of the computations over + multiple processors). + Now, there is a rather annoying amount of complexity within the package; + we strive to isolate that from the user. When the user creates a private + key, he has to tell us the parameter set (we can't make that up ourselves); + however, after that, he just loads the keys, and signs messages, without + worrying about the internal structure of the trees. The parts that we + do leave to the user (such as the functions to load/store the private key) + are there in attempt to make it easier to use the system securely. + +- Merkle trees, subtrees + The core of this system is the signer, and the working key. To sign + a message, we generate the OTS signature for that message, and generate + the authentication path to the root; this authentication path are the + nodes adjacent to the actual path from the OTS public key to the Merkle + tree root; hence when we generate the signature, we need to have those + internal node values computed. We could compute them on the fly, but + that's be expensive. We could store the entire Merkle tree in memory + and retrieve them that way, but that'd take too much memory (for H-25, + that's 2Gigabytes) [1]. Instead, what we do is implement a Merkle tree + walk, which recomputes a few OTS public keys (and Merkle internal nodes) + per signature, and ensures that we have the data needed for the + authentication path when we need them. The algorithm we actually use for + this is inspired by the paper "Fractal Merkle Tree Representation and + Traversal", by Jakobsson et al. Now, we don't do the exact algorithm + found in the paper (Algorithm 3); we go for "constant OTS pubkey + computation", and not "constant hash evaluations" (the algorithm in the + paper assumes that generating a leaf node takes one hash; it's more + like a few thousand). In addition, we need to deal with the complexity + of dealing with multiple trees at once, their algorithm considers only + one. If you do go through the paper, their $Exist_i$ tree is our + ACTIVE_TREE, and their $Desire_i$ tree is our BUILDING_TREE (and they + don't have a NEXT_TREE). This algorithm allows a time/memory trade-off, + and even if we don't give it much memory, it's still decently efficient. + This algorithm is based on a "subtree"; a subtree is a triangular subsection + of the tree that consists of nodes from level A to level A+h-1 of the tree + which are rooted by a specific level A node; 'h is the height of the + subtree. If the Merkle tree is level H, then we divide up the tree into H/h + (rounded up) levels, and deal with the subtrees at each levels (if h doesn't + divide H cleanly, then the top level subtree will be shorter). For each + such level, we track an ACTIVE_TREE, a BUILDING_TREE and a NEXT_TREE + (exceptions: the top-most subtree doesn't get a BUILDING_TREE, and the + subtrees for the top level Merkle tree don't get NEXT_TREE's; they wouldn't + be used). + The ACTIVE_TREE lives on the current authentication path (that is, on the + path from the current (actually, next-to-be-used) leaf node to the Merkle + tree root. The ACTIVE_TREE is always fully populated (that is, it contains + the correct value for all the internal nodes within the subtree), and so we + can read the authentication path by looking at the nodes within the + ACTIVE_TREE. + The BUILDING_TREE is the subtree to the immediate right of the ACTIVE_TREE, + within the same Merkle tree. That is, the root of the BUILDING_TREE is the + node next to the root of the ACTIVE_TREE. As we generate signatures from + the ACTIVE_TREE, we also incrementally compute nodes in the BUILDING tree. + If the root of the two subtrees are height L from the leaf nodes, then we + generate 2**L signatures from the current ACTIVE_TREE, and we need 2**L + OTS pubkey generations to fully build the BUILDING tree; hence for each + signature generated, we compute one OTS pubkey (and do the other work + involved with constructing the tree); when we complete the ACTIVE_TREE, + the BUILDING_TREE is all constructed, and ready to become the new active. + We need to do this update 1 time for each building tree in the bottom + merkle tree; there are H/h-1 (rounded up) such levels (the topmost + subtree doesn't get a building tree, as there's no subtree adjacent + to it), and so the building subtrees involve H/h-1 OTS pubkey gens. + The NEXT_TREE is the first subtree that's in the next Merkle tree. Except + for the topmost Merkle level, when we exhaust one Merkle tree, we're + expected to roll over to the next. The NEXT_TREE is there so that when + we do, we'll have a fresh set of ACTIVE_TREEs are ready to go. If the + height of the Merkle tree is H, then the current Merkle tree can sign + 2**H signatures, and it takes 2**H OTS pubkey gens to construct the + next Merkle tree, hence we do one OTS pubkey gen for the next tree + per signature (in addition to the building tree pubkey gens listed + above). + Also, cryptographically, all levels of the HSS hierarchy are the same, but + from an implementation standpoint, they're not. Almost all the work + computing the next authentication path is done with the bottom level Merkle + tree, and so if the user allows us extra memory, we'll devote all that + memory to expanding the bottom subtrees (which reduces the number of OTS + pubkey computations per signature; by increasing h, we decrease H/h). For + Merkle trees other than the bottommost, we always use the subtree height + that gives us the minimal memory (which isn't always the smallest subtrees) + [2]. Because we hardly ever need to update the subtrees there, any extra + memory we use would be wasted. + Another difference between the bottom-most Merkle tree and higher trees + is the update strategy. For the bottom-most Merkle tree, we perform the + needed OTS pubkey gens as a part of the signature call (as that's the only + time we have to perform any operations). However, for higher level trees, + we don't gen those OTS pubkeys when we generate the signature (as that + would cause that signature to be unexpectedly expensive); instead we + spread the OTS pubkey gens over a number of previous signature operations. + Also, we deliberately do those when the bottom-most tree is doing fewer + operations than expected (e.g. one of its ACTIVE_TREEs is on the right + side, and so there's no need for us to update the BUILDING_TREE), and so + the caller doesn't see any unexpected expense at all. + When we load a private key in memory, the bulk of the work is initializing + the subtrees to be what we'd expect them to hold, based on what the current + count is. Actually, we advance the building and next trees to be slightly + in advance of what they'd be if we incremented them manually (that turns + out to be somewhat simpler). + + [1] Actually for the bottom level Merkle tree, if the user allows us enough + memory, we will explicitly represent the entire Merkle tree in memory; we + just have it implement one huge subtree, that is, H=h. However, we don't + mandate that the user allow us that much memory, if he allows us less, + we'll go with a more compact (and slower) representation. + [2] Except in those cases where the immediately below Merkle tree won't + give us enough updates, which can happen only in rather obscure parameter + sets, e.g. 25/5. + +- Aux data; what is it, really? + + When we first generate the public key, we must compute the entire Merkle + tree contents for the top level Merkle tree (in order to compute the public + key). When we load the private key into memory, we must compute the + authentication path for active Merkle trees (which includes the top level + one); this involves computing the entire Merkle tree contents. Obviously, + there is a lot of repeated computation going on; our answer to that it + auxiliary data. When we generate the Merkle tree the first time (when we + generate the public key), we save some of the contents of this tree; when we + load the key into memory, we use these saved contents (rather than + recomputing those nodes); this significantly decreases the amount of + recomputation we need. We actually save the values at the bottom of the + subtrees; that means that those bottom nodes for the subtrees we've saved + are free; we recompute the internal nodes for those subtrees ourselves (but + that's comparatively cheap). The higher level subtrees cost us the least + amount of disk space (there are fewer of them), and they save us the most + computation time, and so those get priority. We know (given the current + allocation algorithm) what subtree heights would be (and hence where their + bottom levels would be; based on the amount of aux data we're allowed, we'll + save as many bottom levels as can fit. + Also, we protect the aux data with an HMAC (and if that doesn't validate on + reload, we ignore it); this means that an attacker can't cause us to + generate invalide signatures by message with the aux data; they can make + key reloading take more time. + +- Extra info, what's up with that? + There are some information that the application may want to specify, but + most applications really don't care. Some examples are "how many threads + should I use", or "have we just signed the last signature?" or "why did + that call just fail?". Instead how having independant parameters for each + one (and having to modify the API every time we think of another one), we + bundled them all into a structure (struct hss_extra_info), and allow the + application to set it, pass in a pointer to it, and on return, check the + results. Of course, if the application is OK with the defaults, it can + just pass in a NULL. Currently, we have three things that can be passed: + - Number of threads that we can use + - Whether we just used up the last signature + - Most recent failure reason (hss_error_code, you'll see that there are + a lot of possible reaosns) + In the future, we'll likely to add support for partial trees, and possibly + other things we haven't thought of yet; one nice thing is that we can add + something here to add an obscure (== "not of interest to most applications") + parameter without modifying the API to existing applications. + +- Data structures; this is a review of some of the structures used within + this subsystem, and what they really mean + private key + This is what the raw private key looks like. It's not a formal C + structure; instead, it is a byte array, currently 48 bytes long, + split up into: + - 8 bytes of count; this is the state that gets updated with every + signature, and consists of a bigendian count of the number of + signatures so far. By convention, a setting of 0xffffffffffffffff + means 'we're used up all our signatures' + - 8 bytes of parameter set, in a compressed format. This is here so + that the application needn't tell us what the parmaeter set when + loading a key (and can't get it wrong) + - 32 bytes of random seed; this is where all the security comes from. + It is 32 bytes (256 bits) so Grover's algorthm can't recover it. + This is a flat set of bytes, because we hand it to read_private_key + and update_private_key routines, which are expected to read/write + them to long term storage. + Random musing: should we have included a version parameter (so we + could change the format without breaking things???) + struct hss_working_key + This structure holds all the current state of a loaded private key. + It contains a copy of the private key (so we can write it out as + required; we have the entire thing in memory, in case the write + routine isn't able to do a partial update), the current and reserve + counts (the reserve count is what we've last written to the private + key; we use it to implement the 'reserve' functionality), the + current signed public keys that we place into the signature, + and all the levels that make up this hierarchy. + One nonobvious member of this structure is 'stack'. Some of the + subtrees (the nonbottom building and next subtrees) require a + stack to hold intermediate hash values (as we compute them one + OTS pubkey at a time). On the other hand, other subtrees (the + active ones) have no such need; so, to save a bit of space, we + consolidate all the stacks into one contiguous region (and have + each subtree point into the part of the region that's theirs). + And, when we swap the active subtree with a building/next, we move + the stack pointer from the old building/next subtree to the new + (as the new active one doesn't need it). + struct merkle_level + Actually, this is not a Merkle tree (even though the code typically + names variables of this type 'tree'). Instead, it stands for a + specific tree level within the HSS hierarchy, and all the trees + that might live at that level. It contains the parameter set + that is used for this level, how the trees are implemented (subtree + sizes), and also two different trees at this level; the one the active + path is going through, and the next tree that will be used at this + level (once the current tree all the signatures it is allowed). This + structure has pointers to the various subtrees that hold the known node + values for the two trees. + struct subtree + This contains the node values for a particular subtree. The + height of this subtree is implicit (we use values in the merkle_level + to recompute it), it does contain the location of the subtree within + the larger tree. There are three different flavors of subtrees, + active, building and next; we tell which one this is based on + the pointer from the merkle_level. For building and next subtrees, + the subtree may not be complete; the current_index value tells + us where in the building process we are. For nonbottom subtrees, + this building process involves a stack (to combine nodes that + are lower than the bottom of this subtree); the stack member is + a pointer to a region dedicated for this rebuild for this subtree. + The actual node values are in the array nodes[] (and the structure + will be malloc'ed large enough to hold all the nodes); the root + of the subtree will be at location 0. + struct thread_collection + This is the abstract structure that stands for a collection of threads. + Its contents are specific to the actual threading implementation (the + trivial implementation doesn't actually bother defining it, as it never + allocates an object of this type). It holds information about the + threads, any necessary locks, and the tasks that have been asked for. + In addition, any such pointer to a (struct thread_collection) may be + NULL; this is never considered an error condition, rather it is an + indication that we're running in single-threaded mode (either because + that's how we're linked, or because we got an error spawning the + thread). + struct expanded_aux_data + This is an array of pointers to the various node values that occur + at various sublevels within the aux data; for any level that isn't + stored in the aux data, the pointer will be NULL. This will always + point into an application provided buffer, hence we don't need to + worry about memory allocation. + This is used in two slightly different ways: if we're building the + aux data (during key generation time), this will point to where the + aux data should go (that is, into the application-provided buffer). + If we're loading the aux data (during key load time), this will point + into where in the buffer the various levels actually are (and if the + buffer doesn't validate (wrong length or bad HMAC), we NULL out all + the pointers (and so the generation code treats it as if we had no + aux data). + +- Types + We try to use types in a stereotypical way; we do this not so much to allow + the compiler to do type checking (as most of the types of flavors of int, + which the compiler will allow to mix-and-match freely), intead, it's + intended as a hint to the maintainer what this variable is supposed to be. + Of course, it counts as a hint only if the reader actually knows what we use + which types for :-) + sequence_t + This is the internal sequence number across an entire HSS tree structure. + That is, it's used to represent the total number of signatures that an + HSS private key has signed so far. This is a 64 bit type, as we allow + parameter sets that can sign more than 2**32 signatures. + merkle_index_t + This is the 'index' within a single Merkle tree; however we do give it + four distinct meanings: + - This is the count of the number of signatures generated with a single + Merkle tree so far + - The 'address' of a Merkle node that we use when computing its hash; + that is, the four bytes we include in the hash. In the draft, the + variable 'r' is this type. + - The offset of a node from the left side of the Merkle tree. In the + draft, the variable 'q' in the OTS signatures is this type, however we + use it for internal nodes within the Merkle tree as well. + - The offset of a node from the left side of the subtree it is in. This + is different from the previous definition if the subtree we're in isn't + the leftmost. Obviously, this meaning is never referenced in the draft + (as the draft never discusses subtrees). + If we were doing a rewrite, we'd probably give different typedef's for + the distinct meanings. + size_t + This is the size of an object (buffer, signature, whatever). Two notes: + - If we know that the size cannot be greater than 65535 (e.g. the size + of a hash), we sometimes use 'unsigned' instead + - In a couple of places, we want the size, however we want to allow + negative values as well. In those places, we use the type + 'signed long' (and, yes, I know that 'signed' is redundant; I want + to emphesize the signedness). + bool + We use a bool in two different ways; the first is the obvious (a value + which is true or false); the other is a success value (did the operation + work or not?); we use the convention "true == it worked", + "false == it failed". It might make sense to use the opposite convention + (0 means it worked, nonzero means it failed; the exact nonzero value + might give an indication as to why it failed); however to me, having + 0 meaning success is sufficiently nonintuitive that I just can't do it; + we have added another way to communicating the failure reason to the + application, in case it cares. + param_set_t + This is either an LM or an OTS parameter set (32 bits). + aux_level_t + This is the 32 bit flag that goes in front of the auxilary data; bits + within this flag indicate which Merkle tree levels we actual have + auxilary data for (and whether we have any aux data at all). If the + msbit of the initial byte of the aux data (which corresponds to bit 31 + of the aux_level_t) is clear, then we assume we have no aux data (even + if the aux data consists of only that one byte; that means for any + real aux_level_t, bit 31 will be set. + hss_error_code + This is the code that stands for an error reason; for some structures, + we also use this as a 'is-this-structure-usable' flag (with something + other than "no error" (hss_error_none) meaning 'if someone tries to + use this structure, report this error'. + We've divided the various error codes into ranges, to stand for the + general types of errors (essentially, who we think was at fault): + - hss_range_normal_failures; these are the types of errors you get + when running the package normally (signature validation failure, + private key expired) + - hss_range_bad_parameters; these are the types of errors caused by + the application misusing the package (unsupported parameter set, + passed buffer not big enough, etc) + - hss_range_processing_error; these are errors caused by something in + the environment (rng failure, nvread/write failure, malloc + failure) + - hss_range_my_problem; these are errors caused by something internal + to this package; currently, they're all dubbed hss_error_internal, + and are caused by either something scribbling over our memory + or a bug somewhere + struct seed_derive + This is the structure we use to derive keys in a potentially side + channel resistant manner. There are two different versions of this + structure (with the same API, controlled by #define's) that map + the current seed, I value, q value (Merkle tree node index) and + j value (Winternitz digit index) into unpredictable values for the + LM-OTS private values (and the C values and the child tree I, seed + values). We make this into a structure because, when we're doing + a tree based derivation method (SECRET_METHOD==1), adjacent values + usually share most of the nodes of the tree, and this structure is + a convienent place to store those shared values (allowing us to + avoid recomputation). + This structure is used as follows: + - hss_seed_derive_init to set the I, seed values + - hss_seed_derive_set_q to set the q value (and if you call this again + to set it to a different q value, this tries to reuse as many nodes + as possible to minimize computation + - hss_seed_derive_set_j to set the initial j value. + - hss_seed_derive to get the next seed value. If increment_j is set, + this also sets up the structure for the next j value, and in a way + that minimized the number of hashes done. + - hss_seed_derive_done when we're done (to zeroize any internal state + information). + The programmer can modify the SECRET_METHOD/SECRET_MAX settings to + change the efficiency/side channel resistance mix; however any + such change modifies the mapping between seeds and private LM-OTS + values (that is, your private keys no longer work). + +- Side channel resistance and key derivation. + We are inherently resistant to timing and cache-based side channel attacks + (as those behaviors are uncorrelated to any secret information). However, + when we come to DPA-style attacks, we do try to have some protection. To + perform a DPA attack, the attacker would need to see us use the same secret + value in a number of distinct hashes (which would allow them to build up + the statistics). To prevent this from being a problem, we can be configured + so that no secure is ever used for more than a limited number of hashes + (and so the attacker cannot get enough information to reconstruct any + secret). This is controlled by the SECRET_METHOD and SECRET_MAX #defines + found in hss_derive.h. SECRET_METHOD==0 means that we don't worry that much + (and we use the LM-OTS key generation procedure found in Appendix A). + SECRET_METHOD==2 means that we use the same procedure that ACVP expects to + translate seed values to the random values used. It's a variation of the + SECRET_METHOD==0 method, and isn't designed to be side-channel resistant. + SECRET_METHOD==1 means that the number of times we use any secret is + bounded to a maximum of 2**SECRET_MAX times. In this method, we derive + keys using a tree-based process, with each node having up to 2**SECRET_MAX + children. Decreasing SECRET_MAX takes a bit more time (as the tree becomes + deeper), however even SECERET_MAX==1 (smallest allowed value) isn't that + expensive. + +- Threading architecture + We support doing operations on multiple threads, however we'd rather not + have the majority of the code know whether we're actually doing threading + or not. The compromise we came up with is the hss_thread.h API; this + compromise is not perfect (at least half of the complexity within + hss_generate.c is logic to try to work with multiple threads efficiently), + however it's better than embedding conditional pthread calls throughout + the code. This hss_thread API allows us to issue "tasks"; these tasks may + be run by the main thread or may be run in a spawned thread; the only + guarantee is that, by the time hss_thread_done returns, all the tasks have + completed. Now, we provide two different implementations of this API; a + trivial one (hss_thread_single.c), which doesn't try to spawn any threads, + and one that assumes the POSIX pthread API (hss_thread_pthread.c), which + makes calls to the pthread library to do this. You're expected to link + either one or the other when compiling the subsystem (and the Makefile we + provide generates two libraries, hss_lib.a and hss_lib_thread.a, which does + that for you). We do this, rather than attempting some internal switch, + because hss_thread_pthread.c makes direct calls to pthread (hence, would be + a link error if the OS didn't provide some implementation of those). Also, + if you're interesting in supporting some other threading library (such as + the one defined in C11), that shouldn't be that hard to add; we really use + only the basics of what a threading library ought to provide. + Also, as for avoiding race conditions (always a good idea), we have the + convention that, when a task is writing the result, and the area it's + writing to is malloc'ed or automatic region that might be shared by + other threads, that task must call hss_thread_before_write(col) before the + write (and hss_thread_after_write(col) afterwards); this makes sure that the + thread is the only one to write into that region. We do this even if two + threads aren't actually updating the exact same bytes; because we're not + careful to make sure our fields are aligned with the natural word size + of the CPU (whatever that is), then what an update of one field might + end up doing is a read/modify/write of a larger word, which might end + up overlapping the target of another thread (which might be doing a + read/modify/write of the same larger word simultaneously). This is a rather + unlikely event, however the race condition it would cause if it did happen + would be *really* hard to track down. Now, hss_thread_before_write does a + lock, which means that no other thread can write into the common region + until we release the lock, hence the time any thread holds the lock ought to + be short; the current code abides by that. + Now, we allow the application to specify the number of threads; if we're + using the pthread library, that's the number of child threads we'll + attempt to spawn (we don't count the parent thread, however while child + threads are active, the parent thread doesn't do much). Of course, it + is subject to a sane maximum. If the number of threads is specified as + 1, we'll fall back to single-threaded mode. If it specifies 0, it gets + a supposedly-reasonable default (DEFAULT_THREAD). On the other hand, if + we're linked with the nonthreaded library, this doesn't do anything (we're + always in single-threaded mode). + The obvious question is: how is the application supposed to know how + many threads are appropriate? I don't have a great answer to that. + The number of threads you want are dependent on the number of cores + you have on the system (spawning more than that just gives the OS a + bit of a workout without speeding anything; after all, all the threads + are CPU-bound), and how much of the system resources you want to devote to + this task. The pthread virtualization doesn't give us a hint on the first + one; the second one is something the application might have a clue about. + Also, while the key generation and loading can take advantage of as many + threads as they can get, the signature generation and verification logic + can't. The signature generation logic is limited by the number of subtree + levels in the bottom merkle tree (plus one); the signature verification + logic is limited to the number of merkle levels. + +- Use of malloc + If you go through the code, you'll see an occasional call to malloc. In + allocate_working_key (hss_alloc.c), we use malloc to build the working key + structure, and we'll fail (return 0) on a malloc failure. The working key + structure will contain everything we need to generate signatures (so we + never *have* to do a malloc later). Now, we will try to perform malloc's + elsewhere, however they are always strictly optional; we'll never fail + because malloc objected. Instead, the code will step to a "plan B" on a + failure, which will do the exact same job (but slower; if it wasn't slower, + it wouldn't be plan B). + The testing code (and demo.c) does use malloc, and will fail if the + malloc fails; these tools can be expected to run only when we have plenty + of memory, hence we feel we don't need a plan B there. + +- Use of Variable Length Arrays + Now, we used to use VLAs (a C99 language feature) at places. However, + someone's compiler couldn't handle them (even though they implemented the + rest of the C99 features we used), and so we went and reworked the code to + remove them. In any case, removing the VLAs might make this code a bit more + small-end-device friendly (as those small devices tend not to have huge + stacks). + +- Zeroization + Whenever we're done with a value whose leakage might allow someone to + generate a forgery, we zeroize it (hss_zeroize) before we release the + memory. Now, it turns out that most of the values we compute wouldn't + actually allow a forgery; the ones that do are: the seeds, the OTS private + keys and the OTS previous winternitz chain values. We also zeroize the + aux data HMAC values (those wouldn't allow a forgery; they would allow + someone to cause us to misbehave by modifying the aux data). + +- Use of globals + There are no globals (other than the optional debugging flag hss_verbose). + All memory is either a buffer provided by the calling application, + dynamically allocated (malloc), or automatic (stack). Globals are evil, + reentrancy is good. The regression code does have globals (for things like + coordinating with the randomness generator; no normal program has any need + for that); the regression code isn't intended for use for other programs... + +- Use of floating point + Crypto code hardly ever uses floating point. However, we're an exception; + in the hss_generate.c function, we do actually do some float point + computations; we do this to figure out a reasonable way to split the + building task between threads (and for this task, the imprecision inherent + in floating point is not a problem; if two ways of splitting the task are + so close in cost that the rounding error actually makes a difference, it + doesn't really matter which way we go). Now, we include a macro + (DO_FLOATING_POINT) which disables the use of floating point; a platform + that does not support floating point can set it to 0, and that code is + commented out. Now, if you use threading, you really want DO_FLOATING_POINT + If you don't, it doesn't matter for performance, and actually, turing it off + comments out quite a bit of code that you doesn't actually buy you anything; + it doesn't matter how we divide tasks between threads if the same thread + will end up performing them all anyways... + We also use floating point in the regression code; to figure out when to + update the displayed % completed. + +- Debugging + Good luck... + +- Regression tests + This package includes the test_hss executable, which is meant to be a set + of regression tests for this package. It ought to be run early and often + (with "test_hss all" being a good default), if not in -full mode, it's + relatively quick. + + The usage is: + test_hss [-f] [-q] [-full] test_1 test_2 test_3 + Without any parameters, it gives a usage message (and the list of the + supported tests) + The parameters are: + -f Normally, this test suite stops at the first failure; with this flag + it keeps on going + -q Normally, some of the longer tests some minimal progress messages + (percent complete); with this flag, it just lists the test being run + and a pass/fail message (and possibly a failure reason). + -full Normally, each test takes no more than 15 seconds to run (to + encourage you to run it early and often). With this flag, we allow + the tests to run much longer; warning, it'll take several hours to + run the full test suite in -full mode. I'm also not convinced that + -full mode gives you that much better coverage, on the other hand, + it really has found problems that the short tests haven't, and so it + should be run occasionally + all This is a shorthand to specify every test the test suite knows about. + On my test machine, 'test_hss all' currently takes about 70 seconds. + + Now, there are things that the regression tests currently don't test: + - Do we assume malloc gives us an initiallized buffer? + - Do we handle malloc failures as designed? + - How about thread spawn failures? We're supposed to handle those + transparently + - Do we have any memory leaks? + - We're supposed to be able to limit the number of times we hash any + specific secret; do we actually abide by that? + Testing those would require more infrastructure than we have right now. + Also, it might not be that bad of an idea to run a code-coverage tool to + check out how much of the code the regression tests actually tests. + +- Files; this is a listing of the files that make up this subsystem, and a + brief description of what's in them. Note that for many .c files, we have a + .h file with the prototypes; we list those together. + + common_defs.h This is a central spot to put definitions of general + interest of the entire subsystem + demo.c This is an example program that uses this subsystem; it + implements a simple file signer. Note: because it + doesn't get that great of randomness (due to the need + to restrict ourselves to standard C), it probably + shouldn't be used as is. It does try to use + /dev/urandom; that's of help only on OS's that + actually implement /dev/urandom + endian.[ch] Routines to read/write values in bigendian format + hash.[ch] Routines to implement the hashing API, as used by the + rest of the subsystem. This currently only implements + SHA-256, it'll support other hashes once LMS does. + Note that there really are three separate APIs to do + hashing (hash this entire string, hash this string + using this context variable as a temp, do on-line + hashing); those are all used at various times. + hss.c This used to be where all the code lived; however, we + have since migrated the vast majority of the routines + to more appropriate source files (so we don't have a + huge .c file). Now, it just has a handful of routines + that don't have a better home. + hss.h This is the public include file for the entire + subsystem; this is the file that we expect an + application that uses this subsystem to include. + hss_alloc.c This is the routine whose job it is to allocate a + working key (struct hss_working_key). Note that it + doesn't actually put anything in there, it just + allocates the memory (and initializes some + key-independent fields). + hss_aux.[ch] These are the routines that handle auxiliary data (that + is, data that holds part of the top level Merkle tree, + and is used to speed up the key load process) + hss_common.c These are routines that are of interest to both an + implementation that generates signatures, and one that + only does signature verification. + hss_common.h These are the prototypes for the above routines; we + list it separately to emphesize that this may be + included by an application. + hss_compute.[ch] These are routines that do some common computation; + these are shared between multiple source files. + hss_derive.[ch] This is the structure that does key derivation. It + allows a trade-off between efficiency, and side channel + resistance. + hss_generate.c This is the routine that takes an allocated working key + (hss_alloc.c), and loads a private key into it. Sound + simple? Well, if you go through this, you'll find out + that it isn't. + hss_internal.h These are the prototypes and structures that are common + to this subsystem, but shouldn't be used outside of it. + hss_keygen.c This is the routine that generates a public/private + keypair. + hss_param.c These are routines that deal with parameter sets. + hss_reserve.[ch] These are routines that deal with reservations, and + updating the sequence number in a private key. + hss_sign.c This is the routine that generates an HSS signature. + hss_sign_inc.c This is the routine that generates an HSS signature, + in an incremental fashion. + hss_sign_inc.h This is the public include file for the incremental + signature routines. It's in its own file because it + needs to pull in some internal files (e.g. hash.h)' + that we generally don't need to hand to people + hss_thread.h This is the internal prototype for our internal + threading abstraction. We have two implementations of + the abstraction, we expect to link with one of the two. + hss_thread_pthread.c This is the implementation of the threading API that + links with the POSIX pthread library, and uses that to + to multithreading. + hss_thread_single.c This is the implementation of the threading API that + assumes that we don't have any threading support at all + (and so the main thread does all the work). + hss_verify.c This is the routine that verifies an HSS signature. It + is in its own file so thar someone who wants to only + verify signatures doesn't need to pull in the signing + logic. + hss_verify.h This is the public API for the verifier. + hss_verify_inc.c This is the routine that verifies an HSS signature in + an incremental fashion; that is, you can hand it + pieces of the message in succession (so we don't need + to assume the entire message fits in memory). It + is in its own file so thar someone who wants to only + verify signatures doesn't need to pull in the signing + logic. This API is somewhat less efficient that the + hss_verify.c logic if you're multithreaded (we can't + paralleize the check of the bottom signature with + the upper ones), however it's not a huge delta. + hss_verify_inc.h This is the public API for the incremental verifier. + It's in its own file because it needs to pull in some + internal files (e.g. hash.h) that we generally don't + need to hand to people + hss_zeroize.[ch] This is a routine to clear out memory; it is used to + make sure we don't accidently leak any secrets by + free()ing them, or having them go out of scope. + lm_common.[ch] These are routines that support the (single level) LMS + routines that are of interest to both an implementation + that generates signatures, and one that only does + signature verification. + lm_ots.h Prototype for the OTS signature routines. + lm_ots_common.[ch] These are routines that support the OTS routines that + are of interest to both an implementation that + generates signatures, and one that only does + signature verification. + lm_ots_sign.c Routines that generate OTS public keys, and OTS + signatures + lm_ots_verify.c Routine that computes the public key given an OTS + signature and a message. + lm_verify.[ch] Routine that verifies an LMS signature + sha256.c Pure C implementation of SHA-256; it is included if + USE_OPENSSL is 0. This is provided in case you don't + have OpenSSL available. + sha256.h Routine that computes the SHA-256 hash. This is the + same interface that OpenSSL presents. We also + include a #define (USE_OPENSSL); if 1, these are + direct calls to OpenSSL; if 0, we use our own + implementation (in case you don't have OpenSSL + handy). If OpenSSL is available, use that - it has + an assembly language SHA-256 implementation, and that + performs better. + test_hss.c This is the main driver code for the regression tests. + It doesn't actually implement any tests itself; + instead, it deals with handling the test run + test_hss.h This has the prototypes for all the actual tests + test_*.c These are the actual tests. + +And, one final note: I would claim to be a competent C programmer, however my +skills at generating makefiles are laughable. I would ask that you keep the +taunting about my lack of ability there to reasonable bounds. diff --git a/src/sig_stateful/lms/external/read.me b/src/sig_stateful/lms/external/read.me new file mode 100644 index 0000000000..8ee8b5ffc9 --- /dev/null +++ b/src/sig_stateful/lms/external/read.me @@ -0,0 +1,597 @@ +This code attempts to be a usable implementation of of the LMS Hash Based +Signature Scheme from RFC 8554. + +This is a signature scheme (that is, someone with the private key can sign +messages, someone with the public key can verify sigantures, but cannot +generate signatures) that is based on a hash function (that is, the only +cryptographical assumption is that hash function is secure; in this case, +that you can't find preimages or second preimages). This particular +scheme is stateful (that is, the signer keeps state that must be updated +for each signature); there are stateless schemes, but they have much larger +signatures. + +Here's how to use it: general workflow for the signer: + +Step 1: generate a public/private keypair. + This is the hss_generate_private_key function; you give it the parameter + set you're interested in (see below for guidelines as to what's + appropriate here), a function to get randomness, a function to write + out the private key, a buffer to hold the public key, and the optional + aux data. + +The below step can be repeated as needed (e.g. because of a program reload): + +Step 2: load the private key into memory + This is the hss_load_working_key function; you give the private key, + a budget of the amount of memory to use, and optionally the aux data + generated during the hss_generate_private_key. This memory budget is + used as a guide for some space/time trade-offs; we'll never fail because + you haven't authorized enough memory (if you haven't, we'll reduce the + amount of memory as much as possible, hence passing a 0 will minimize the + amount of memory, which is appropriate if you'll use the private keys only + a handful of times). It turns out minimal space doesn't generate + signatures that slowly; if you have memory to burn, you could allow it to + allocate 100k and it would sign faster; if we allow enough space so that we + can place the entire bottom level LMS tree in memory, the speed up is + significant in nonthreaded mode (in threaded mode, we generate the + signature at the same time, and so we don't save as much wallclock time). + The aux data is optional (the load process will work if you don't provide + it); it'll be faster if you do (if you don't provide this, this'll + take at least as long as the original key generation). Also, the aux data + is integrity checked (and so if it was modified, this'll ignore it). + + If you just generated a key, and want to sign a message, you'll need to + load the private key (even if you're not interested in saving the private + key between reloads). If this is what you're doing, I suggest you have a + moderate (e.g. 1k) array that you use to hold the aux data; without that, + the load process would need to redo almost all the computations that were + done when initially generating the private key. + +Once you've expanded the key, you can use it to, say, actually sign messages. +Here are the steps you an do (and you don't need to reload the key after +every message you've signed): + +Step 3: actually generate the signatures + This is the hss_generate_signature function; you give it the working key, + a function to update the private key, and the message to sign, and it + generates the signature. + +Step 3a: reserve N signatures + This is the hss_reserve_signature function; this allows you to advance + the 'current counter' in private RAM by N (and so the update_private_key + function won't need to be called for the next N signature generations). + Of course, if the application exits without using all those N signatures, + those will still be considered used. + There are actually two variants of this; you could either has that N + signatures be reserved now (hss_reserve_signature), alternatively, you + can give a standing order that when we run out, we reserve extra + signatures (hss_set_autoreserve). The former works well if you have + some idle time periodically (and so this write-to-disk process mostly + happens when youre not busy); the latter works better if you don't have + idle time (and want to reduce the number of writes to disk). + + +The workflow for the verifier is easy: you pass the message, the public key +and the signature to hss_validate_signature; that returns 1 if the signature +validates, 0 if it doesn't. We provide APIs both to sign/validate the +signature in one shot (hss_generate_signature/hss_validate_signature), and +also init/update/finalize routines in case we have the message in multiple +pieces (see hss_sign_inc, hss_validate_inc for the details of those APIs). + +The makefile generates three .a files; hss_lib.a, which includes the above +routines; hss_lib_threaded.a, which is the same, but with threading enabled +(and so -lpthread is required to link), and hss_verify.a, which just includes +the verification logic (nonthreaded; threading speeds up verification only +somewhat; if you want a threaded verification code, it should be fairly +obvious how to make it yourself). + + + +Now, the practical problems that a (stateful) hash based signature has are: +- Statefulness; each signature effectively has a nonce, which must not be + used to sign two different messages. If we use a private key over reboots, + that means that we need to track the state in longterm storage somehow. +- Size of the signature; depending on the parameter set, the signatures might + be several k long +- There's an upper bound on the number of signatures that can be generated + with a specific public key. +This package tries to address all of them, as follows: + +Statefulness; we do several things to mitigate this issue: + - Everytime we need to update the state (either during initial key + generation, signature generation or reservation (see below)), we have + the application provide a function that is supposed to write the new + state to secure storage; the signature is not released to the application + (actually, not even generated) unless that function claims success. + - There's quite a lot of state involved with this package. However, instead + of writing all that to secure storage, what we do is write a summary. + On a program reload, we read ("load") that summary back into memory, + reconstruct the full state, and continue on. Because this summary is + short, this makes it easier to store, and we have less to worry about + partial updates (say, if we crashed in the middle of an update). + - If writing the state to secure storage is expensive, what we can do is + "reserve" a number of signatures; that will update the state on secure + storage; however, if we reserve N signatures, that means that for the + next N signature operations, we won't have to actually perform the + update. Hence, we can move much of the work to time where we would have + been idle. +Signature size; we can't actually do anything about the signature format +(that's fixed in the draft), however what we can do is try to make selecting +a parameter set with a short signature reasonable: + - Efficiency; we try to make it efficient; efficiency allows you to use + more aggressive parameter sets (chiefly, ones with W=8, and relatively few + Merkle levels), which makes the signature shorter. + - We support multithreading; that is, during the key generation and key + reload procedures, we can use multiple threads to speed things up. + Speeding these up allows up to use considerably larger trees (and hence + fewer of them). + - We support 'auxiliary data' for the top level Merkle tree. Our model is + that you don't mind spending quite a bit of time during key gemeration (as + that is a rate event); you are far more sensitive to the operation + operations (such as the key reload, which you might do whenever you + restart the program). What the aux data allows you program to do is + generate the top level Merkle tree once (and store some intermediate + nodes); this means that we can practically use a very large top level + Merkle tree, without costing much (other than ken gen time). In fact, + if you're happy with 1-30 million signatures maximum, you could go with + a single level Merkle tree, which yields relatively short (<2k) signatures + - After the initial key gen and load, we compute everything incrementally. + That means that, just because we have a level 20 tree, we never have to + sit down and compute 2**20 operations at once; we spread that work over + the time we spent generating the signatures from the previous tree. +Upper bound on signatures; again, we're stuck with the upper limit on any +parameter set; however we try to make it reasonable to select a parameter set +with a large number of signatures. We gave most of the efficiency features +above; we give a table below listing various parameter sets that are +reasonably efficient, and have large upper signature bounds. For example, +if 1 billion signatures are enough, then a 20/10 parameter set might be +reasonable. + + + + +Now, here is another listing of the features of this package (in a +feature-centric order, rather than a problem-centric one: + +- It tries to deal with state management. It does not assume that the + signing computer will be running without a reboot for the entire time that + the private key is valid, and it does not assume that we're able to do a + large scale update of secret state to disk (which would cause problems if we + rebooted in the middle of an update). It addresses this by dividing the + private key into three parts: + - A short section which is meant to be saved in secure storage (and this is + the part which is referred to as the 'private key'; this includes the + number of signatures generated so far. It's currently 48 bytes long + (with the part that needs to be actually updated only 8 bytes), we assume + that it is updated atomically. These 48 bytes consist of an 8 byte count + of the number of sigantures generated so far, 8 bytes of a compressed + version of the parameter set, and 32 bytes of 'seed' (a random value that + is used to derive every secret) + - A longer section (the "working key") that resides in memory; we assume + that this is private, but need not be saved into long term (nonvolatile) + storage. This section include the Merkle tree contents (actually, a + subsection, as below we implement a time/memory trade-off), the next + Merkle tree contents (incremental computation), I values, signatures for + internal public keys; for most parameter sets, this easily fits into + 10-30k (time/memory trade-offs will allow it to be larger to give better + signing performannce). For example, a level 20/10 tree (maximum of 2**30 + signatures), we can use as little as 16kbytes of ephemeral state (and the + time isn't all that bad after the working key has been regenerated). + +The idea by the above split is that we keep the short permament key somewhere +safe (and which would survive a restart should we need to); at any time (for +example, after a restart), we can regenerate the longer section, which we +would keep in memory. We use the version of memory to actually generate the +signatures (and update the short section as needed). If we restart, we +regenerate the large working section, and carry on. In addition, by using +reservations (see below), we don't actually need to update the secure storage +every time. + +By keeping the short section short, that means that we don't have to worry +about partial updates (which we would if that the private part was a +multikilobyte explicit tree); we can also use limited private space (e.g. HSM). + +We also have an optional third section to the private key: + + - Auxiliary data; this is an optional section that contains the node values + of parts of the top level Merkle tree; this is generated at keygen time, + and is read-only thereafter; the point of this is to speed up the process + of loading the private key into memory. This aux data is (usually) + persistent, but need not be secret. + - For a level 20 top level tree, 10916 bytes of aux data allows us to + regen the top level tree in circa a second. That means that, for a + 20/10 level tree, generating the private key is expensive (3 minutes + on my test machine, a 3.3GHz Xeon), but restarts after that can be done + in a second or two. + - We also authenticate the auxiliary data; that means that an adversary + who can corrupt the auxiliary data can increase the time it takes for + us to load the working key; however they don't get any other advantage. + - I did say that the auxiliary data is usually persistent. However, if + we can't store it permamently, it still can be used to speed up the + initial program load (that is, immediately after the private key + generation); we'd use a moderate (several k) temp array to both + the key generation and first reload; if we need to do a reload + afterwards, we would not pass the aux array (as we wouldn't save it). + This'll speed up the initial load process (as it wouldn't have to redo + most of the computations that the key generation did); it won't speed + up the later reloads, however at least we got some benefit from it. + +- We include a function that takes the short section, and recreate the working + portion, we refer to this as 'loading the private key into memory'. This is + meant to be called on a reload, and (assuming that you use the aux data) can + be significantly faster than generating the key in the first place. + +- We incrementally compute the next trees (so we don't hit a bump when we + generate the 1025th signature) + +- Time/memory trade-offs available (so we don't need so much memory for the + loaded private key) + - The 'load the private key' function (hss_load_private_key) takes a + parameter while means "try to limit the memory used to X bytes"; this + allows some flexibility in designing the trade-off (although less than I + expected when I first designed the API; the 'use as little memory as + possible' option isn't that slow, and the 'd*mn-the-memory- + full-speed-ahead' mode doesn't use that much memory (unless the bottom + level Merkle tree is huge). Yes, there's a delta, but not a big one. + +- For any function that can update the private key, we have the application + (the program that is using the package to sign messages) pass a function + ("update_private_key") and a context pointer. If this is non-NULL, then + when we update the private key, we call this function, what this function is + expected to do is write the private key into secure storage (and pass + nonzero on success); if you pass a NULL function pointer, then we assume + that passed context pointer is actually the pointer to the private key. + +- Explicit implementation of the reservation primitive. Right now, we have + an API that states "make sure that we have N signatures reserved (so that + we won't have to update the private key for the next N signature operations). + +- We can also perform operations in parallel (pthread's); do get this, you + use the hss_lib_thread.a library (and include -lpthread on the link); if + you use the hss_lib.a library, then everything is done in the main thread. + Using multiple threads speeds up the key generation and key loading process + significantly (15x in my experience, assuming you have enough CPU cores); + we also can use it during the signing and verification process, however the + speed up there is less radical (perhaps 2x). + +- I believe the code is fully compliant C99 (that is, it should work on any + C99 implementation that can handle the program/data size; even silly ones + with 13 bit chars and 26 bit int's and things like that); in fact, the + private keys should be portable to different architectures. I haven't + tested out either of these claims, though. + + +I've included a simple program (demo.c) that uses the above facility to +sign files, and verify them. I've also included a test harness (test_hss) to +do regression testing of HSS; however I would rather advise you not to look +at how those regression tests use the library as best practice; they are +designed to push the library's corner cases, and so sometimes do things that +real applications really ought not do. + + +General notes: + +- Advice about parameter sets: while this supports all the HSS parameter sets + currently allowed in draft-mcgrew-hash-sigs-08, some work better than others + The general recommendation is to use a few large trees (that is, with lots + of levels), rather than using a number of levels of smaller trees; this + package tries to make large trees not that costly (and reducing the number + of trees certainly cuts down on the signature size). If you have a + reasonable amount of auxiliary data available (and ideally, multithreading), + then making the top level tree H=20 (or even H=25) is doable; the main cost + is the time to do the initial key generation (which in most cases is a rare + event). On my test machine (a 3.3GHz Xeon), I can generate a key with a + H=20 tree on top (with W=8) in 3 minutes (multithreaded); loading that key + into memory (with 10k of aux data, with a single H=10 tree below it) takes + 1-2 seconds. An H=25 tree on top took 1.5 hours; loading that key into + memory (with 1Meg of aux data) took a second. Increasing the lower tree to + H=15 makes loading take 6 seconds (without affecting the key generation + time). Those parameter sets give reasonable speed, with 3.3-3.7k + signatures, and allow 1 billion to 1 trillion signatures per private key + (which I expect should be sufficient for most uses; at 1000 signatures per + second, that's enough to last you between 10 days (top H=20, bottom H=10) + to 3 decades (top H=25, bottom H=15). + Here's a table that goes through a list of various parameter sets. + They all have W=8 (which minimizes signature sizes, while increasing time), + and were measured on my test platform (with threading enabled; times would + be considerably larger without threading). These are here to give a + guideline as to what's possible; for the computational time, your mileage + may vary, depending on the computing resources you have. The machine I + have does not have the SHA-256 extensions; you could possibly do + significantly better. + + ParmSet KeyGenTime KeyLoadTime AuxSize SigSize KeyLifetime + 15 6 sec 3 sec 100 1616 30 seconds + 20 3 min 1 sec 10916 1776 16 minutes + 25 1.5 hour 18 sec 21860 1936 9 hours + 15/10 6 sec 1 sec 356 3172 9 hours + 15/15 6 sec 10 sec 356 3332 12 days + 20/10 3 min 2 sec 10916 3332 12 days + 20/15 3 min 10 sec 2724 3492 1 year + 25/10 1.5 hour 7 sec 87396 3492 1 year + 25/10 1.5 hour 1 sec 349540 3492 1 year + 25/15 1.5 hour 13 sec 87396 3652 34 years + + ParmSet: this is the height of the Merkle tree(s); parameter sets listed as + a single integer consists of a single Merkle tree of that height; + parameter sets that consist of two trees are listed as x/y, with + x being the height of the top level Merkle tree, and y being the + bottom level. + KeyGenTime: the measured key generation time; this is the operation that is + done once per private key. + KeyLoadTime: the measured key load time (assuming the auxsize of the + specified amount); this is the operation that is done on program reload + to continue using an existing private key. + AuxSize: the size of the aux data that was used to test load time, the value + listed for AuxSize is in bytes. You can use smaller sizes, however that + would increase the key load time (as can be seen by the two different + KeyLoadTimes given for ParmSet 25/10) + SigSize: the size of a signature (in bytes) + KeyLifetime: the lifetime of a key, assuming we generated 1000 signatures + per second. In practice, we're not likely to get anywhere close to + 1000 signatures per second sustained; if you have a more appropriate + figure for your scenario, this column is pretty easy to recompute. + + As for signature generation or verification times, those are moderately + insensitive to the above parameter settings (except for the Winternitz + setting, and the number of Merkle trees for verification). Tests on the same + machine (without multithreading) gave approximately 4msec to sign a short + message, 2.6msec to verify (for this test, we gave the working key lots of + memory; if you skimp on that, the parameter sets with the larger bottom + trees slow down somewhat more than the ones with less; we also used a two + level ParmSet; a single level would approximately halve the verification + time). All times can be significantly improved (by maybe a factor of 8) by + using a parameter set with W=4; however that also about doubles the + signature size. In addition, we allow you to adjust the amount of + threading on a per-call basis (byte the hss_extra_info parameter); + I suspect that typically you'll run with the defaults. + +- Portability of the private keys: I believe that the private keys are + portable to different CPU architectures (that is, picking up a private + key on one CPU and moving it to another will work) + However, I don't currently promise that private keys generated by this + version of the package will work on future versions; we reserve the right + to make changes that break current created private keys. Eventually, we'll + need to preserve this (possibly by including a version flag in the private + key); however, I don't believe that this package is mature enough yet. + After all, essentially everything involved with signing (with the sole + exception of the bottom C signature randomizer) must be derived in the exact + same way from the top level seed; once we start doing versioning, that means + we must forever support that way of deriving internal values from the secret + seed. + +- Same thing with the API; eventually, we'll need to stabilize the API; + however, I don't believe we're there yet (although with the extra_info + structure, we may be getting close; any new parameter where most + applications are happy with a reasonable default can be placed into the + extra_info structure); I don't want to lock us in to what we have right now. + +- This package will allow at most 18446744073709551615 signatures per private + key (that's 2**64-1), even if the parameter set would allow more. If you + think you need more signatures than that, well, dude, what are you smoking? + +- The API has an optional struct hss_extra_info* parameter; this is to allow + you to convey information that typically is not needed (but on occasion, + might be). You can always pass a NULL pointer (in which the package uses + reasonable defaults); the current parameters that are exchanged: + - num_threads; this allows the application to tell this packet about the + number of concurrent threads that it is allowed to use; 1 means not to + use threading at all; 2-16 means that many threads, and 0 means the + default. + - last_signature; if the signature generation routine detects that it has + just signed the last signature it is allowed to, it'll set this flag. + Hence, if the application cares about that, then it can pass an + extra info structure, and on return, test this flag. + - error_code: if some operation fails, the field is set to a value that + indicates why it failed (similar to errno, except not a global). + Hence, if the application cares about that, then it can pass an extra + info structure, and on failure return, test this flag. + + Just a word of warning; if you do pass in an hss_extra_info structure, + the fields must be initialized, passing in a structure containing random + stack garbage is begging Murphy to perform his magic. You can do this + by either: + struct hss_extra_info info = { 0 }; + or + struct hss_extra_info info; + hss_init_extra_info( &info ); + +Future ideas; I'm not against future ideas, but this package is getting +sufficiently complex that I'm not likely to do anything that adds even more +complexity unless someone has a real need. If you do have a real need (for +one of the below ideas, or something else), plesae tell me; otherwise, I'm +likely not to implement it: + +- Extend multithreading to generate the higher level Merkle signatures when we + step to the next lower level Merkle tree. We do multithreading for most of + the expensive operations, but not this one; when we need to generate a + nonbottom signature, that's done by the main thread (while none of the other + threads are doing anything). It's about half the cost of creating an OTS + public key, but it's still not free. We do this only rarely (once every + 1024 signatures of the bottom level tree is of height 10), however it does + increase the worse-case time (which can be significant for some + applications). One issue is that we can't sign the NEXT tree until we've + completed building all of the subtrees; if one of the tasks is finiahing + that up, well, we might be stuck. + +- Perhaps we can have support for SIMD hash implementations (e.g. AVX) that + can, in parallel, compute N independent hashes. I suspect that it wouldn't + be that difficult to add it to the Winternitz routines (which is where the + bulk of the time is spent anyways). However, it would appear that Intel's + SHA-256 instructions would be faster than AVX (and doesn't add any + complexity; recent versions of OpenSSL already include it); hence this would + be of interest only for CPUs with AVX but not SHA-256-NI; currently, that's + common, but I expect that'd change over time; it's not clear that this + amount of code would be warrented for a short term fix. On the other hand, + if we can get support for a GPU, well, that'd be a really big win (albeit + at a significantly higher complexity cost) + +- Q: restarting at some points (say, at the start of a Merkle tree) is + cheaper than restarting in the middle, or near the end. Should we do an + auto-reserve if we're getting close to the end (so that, if we do restart, + that's cheaper)? One the other hand, with parallel processing on the load + function, loading anywhere is already fairly cheap. + +- We might want to explicitly support a block-type interface (with read/writes) + to handle the aux data, rather than insist that it fits into a contiguous + memory segment; this may make it easier for an implementation to use a + significant sized aux data. My major complaints about this is "yet more + code complexity? Don't we have enough?" and "yet another nonobvious API to + document". On the other hand, except for H=25, we never really need more + than 10k or so of aux data; would this (code and documentation) complexity + actually be worth it? + +- The hss_load_private_key takes the 'memory budget' as a parameter, which + allows the user to select the time/memory trade-off. It might be considered + sporting to give the user some context as to what the trade-offs actually + are; perhaps a way of generating a table of memory-size vs. hash compression + operations (or OTS public key generations) expected per signature; it + depends on the parameter set, and so we can't just publish a table (although + a table for typical parameter sets on a typical compiler might not be that + hideous). + +- Should we make this package valid C++ as well? One issue is the malloc's + (which aren't casted), we could insert the "extern "C" {" markers (to say + these functions use the C ABI), are there other issues? + + - As for going the other way (making this valid C89), well the biggest issue + there is the 64 bit int's we use (sequence_t); we do declare automatics + after executable statements, but those wouldn't be hard to move. Yes, + we could program around the 64 bit int's, however I don't see the point + unless someone really needs this. + +- The logic to distribute updates to the upper level trees (which does the + work when we're close to the end of the current bottom level tree, when the + bottom level tree runs out of BUILDING_TREEs to update) rather assumes that + an upper level OTS is about the same cost as a bottom level OTS. This is + actually true if we use the same OTS parameter set (Winternitz parameter) + everywhere; however if the upper levels use a costlier one (e.g. the bottom + level tree uses W=4, the upper level trees use W=8), it's less true. It + might make sense to try to potentially split up the work of updating the + upper level tree into more signature operations; at least, in this case + (and it'd be an ideal way to add even more complexity into this package). + Of course, we'd have to ask: how common are these mixed-winternitz settings + anyways? + +- Another thing that I could see being useful in some scenarios is providing + a way to safely share the same private key between multiple signers. + Here are two ways to do this: + 1. There'd be a master holder of the private key (which would hold the real + private key); a secondary signer would ask for the permission to sign N + signatures; the master would pass it a 'subkey' that would give it + permission to sign using sequence numbers X and X+N-1 (where X is the + current private key value), and the master would increment its copy of X + by N. The client would load the subkey and we're good to go. It's not + clear how the master would advance its working key by N; a first cut may + be that the master does a fresh hss_generate_working_key; there are more + efficient ways to do it, but there are lots of corner cases that would + require testing to make sure we've covered everything. The subkey we + send to the client would be the private key plus one parameter (the + allowed maximum); we might want to modify our private key format to + include this maximum if we're serious about this (so that the secondary + signer could run the standard HSS code). There are likely a number of + gotcha's involved; however less than the below idea. + 2. We'd also have a master holder, but instead of giving the entire secret + key to the secondary signer (and tell him "please only use sequence + numbers from X to X+N-1"), we instead gave him an entire subtree (without + giving him enough information to sign outside that subtree). Note that we + can construct a subtree of size 2**n, for any n (given a tree-based + method to derive OTS keys; that would help with side channel resistance); + it need not be an entire Merkle tree; such a method would waste up to + 2**n - 1 sequence numbers of the main tree (as it assumes that the + initial sequence number we give to the secondary server is a multiple of + 2**n), unless the master tried something tricky and remember those + weren't used yet). This approach would not prevent an attacker who + obtains that subkey from signing anything he wants for as many times as + he wants; we assume that valid signers will not repeat sequence numbers, + but an attacker might not feel so constrained. It would allow us, given + such a breach, to figure out who leaked their subkey; would that be + considered enough of an advantage to make up for the extra complexity? + For this to be a reasonable alternative, we'd need a facility to log who + we gave what (otherwise, what's the point?) + +- Should we worry about implementations that don't support malloc? Or, have + a 'secure_malloc' that reserves memory that is never paged out (and we'd + prefer placing our keying data there)? The candidates for doing a secure + malloc would be the hss_working_key and the merkle_level (because they + both contain seeds); no other malloc'ed data structure contain any + secret data. On the other hand, there doesn't appear to be a great deal + of point in doing a secure malloc unless we know that the automatic + data is also secure (or we move all keying data out of automatic data; + could be problematic). + +- Should the verifier cache previous successful verifications (at least, the + top level trees)? I personally don't think it's worth the bother (or the + complexity). + +- We could design a signature format that's somewhat shorter; (although the + proof may need fiddling in some cases): + - Not having the intermediate root hashes in the signature + - A C randomizer of 32 bytes is overkill (with 16 bytes, we still make the + 'guess C, generate a collision based on that' attack as difficult as a + second preimage attack) + - We don't really need explicit intermediate C randomizers for the message + hashing (as an attacker can't select that message value in advance) + - We could have the lower level I values a determanistic function of the top + level I (rather than expressing them in the signature) + - We don't need to express the parameter types for the top level signature + in the signatures + - We can omit L from the signature, and shorten q + These ideas would (for a 2 level parm set) reduce the siganture size by + about 112 bytes (we could drop 3 bytes from the public key; probably not + worth doing). Should we have an API for this alternative format? + With a bit of cleverness, we can support both (all signatures would be in a + form that's compatible with the short format; we'd be able to express them + in either format as required). On the other hand, with the current format, + a 15/10 W=8 parm set has a pk+signature length of 3184 bytes total (pk is + 60 bytes, sig is 3124 bytes); this would reduce this total to 3072 bytes. + Is a <4% space reduction really worth the effort? This idea gave more + impressive savings back when we have 64 byte I values... + +- Some of our internal functions have fairly generic names (e.g. + get/put_bigendian). We should go through, and give them names that are less + likely to conflict with other functions we're linked with. This is one + place where C++ namespaces would be nice... + +- Some of the computation for a signing operation could be precomputed; we + could implement an API that says "call this during idle time, and your + next siggen will be cheaper". I personally wouldn't expect a lot of call + (pun intended!) for this sort of thing (and, in any case, we can't + precompute the OTS signature of the message, hence there are limits to how + much benefit we can give). And, maxing out the memory allowed in a working + key gives most of the same benefit... + +- We have starting building some regression tests; we should do more + +- We support incremental signing; there is a potential security problem. If + the caller plays games, he can cause bad things to happen; a) if he modifies + the signature between init and final, he could trick us into reusing an OTS + signature index, and b) if he copies the context, he could trick us in using + the same index to sign two different messages. The first would be fairly + easy to block (generate a MAC of the parts of the signature we care about, + and store that in the ctx); the second is harder. The only thing I can + think of to block it would be to have the working_key track those signature + indicies it has issued incremental signatures for, and are still pending + (this would block the first attack as well). That would likely require + dynamic memory allocation (unless we put a tight cap on the maximum number + of contexts that could be pending). My question is: should we go to that + extent? After all, we already need to trust the application already (as we + allow them to sign anything they want, using the API). + +- Should we have better protection against the application accidentally + passing in an uninitialized/freed buffer as a hss_working_key, or a + hss_*_inc context structure? Right now, the value we use to say 'this is a + good structure' is 0 (hss_error_none), which is far too likely a value to + be seen in an uninitialized buffer. Should we also include a magic value + as well, for some simple validity checking? Something like that certainly + wouldn't be fool-proof; however it might catch some simple mistakes. + +- Right now, we pass the update_private_key to every hss_generate_signature + operation. Would it make more sense to pass it instead when we + hss_load_private_key, and just store it in the working_key? Would it + make sense to use the same context for read_private_key and + update_private_key? + +- Right now, hss_extra_info has a last_signature flag. Would it be more + useful to have a 'signatures_remaining' count instead (and when it hits + 0, that'd mean we just wrote the last signature)? diff --git a/src/sig_stateful/lms/external/sha256.c b/src/sig_stateful/lms/external/sha256.c new file mode 100644 index 0000000000..fb18892a31 --- /dev/null +++ b/src/sig_stateful/lms/external/sha256.c @@ -0,0 +1,183 @@ +/* + * SHA-256 + * Implementation derived from LibTomCrypt (Tom St Denis) + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.org + */ + +#include +#include "sha256.h" +#include "endian.h" + +#if !USE_OPENSSL && !defined(EXT_SHA256_H) + +/* If we don't have OpenSSL, here's a SHA256 implementation */ +#define SHA256_FINALCOUNT_SIZE 8 +#define SHA256_K_SIZE 64 +static const unsigned long K[SHA256_K_SIZE] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Various logical functions */ + +/* Rotate x right by rot bits */ +static unsigned long RORc(unsigned long x, int rot) { + rot &= 31; if (rot == 0) return x; + unsigned long right = ((x&0xFFFFFFFFUL)>>rot ); + unsigned long left = ((x&0xFFFFFFFFUL)<<(32-rot) ); + return (right|left) & 0xFFFFFFFFUL; +} +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +static void sha256_compress (SHA256_CTX * ctx, const void *buf) +{ + unsigned long S0, S1, S2, S3, S4, S5, S6, S7, W[SHA256_K_SIZE], t0, t1, t; + int i; + const unsigned char *p; + + /* copy state into S */ + S0 = ctx->h[0]; + S1 = ctx->h[1]; + S2 = ctx->h[2]; + S3 = ctx->h[3]; + S4 = ctx->h[4]; + S5 = ctx->h[5]; + S6 = ctx->h[6]; + S7 = ctx->h[7]; + + /* + * We've been asked to perform the hash computation on this 512-bit string. + * SHA256 interprets that as an array of 16 bigendian 32 bit numbers; copy + * it, and convert it into 16 unsigned long's of the CPU's native format + */ + p = buf; + for (i=0; i<16; i++) { + W[i] = get_bigendian( p, 4 ); + p += 4; + } + + /* fill W[16..63] */ + for (i = 16; i < SHA256_K_SIZE; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < SHA256_K_SIZE; ++i) { + RND(S0,S1,S2,S3,S4,S5,S6,S7,i); + t = S7; S7 = S6; S6 = S5; S5 = S4; + S4 = S3; S3 = S2; S2 = S1; S1 = S0; S0 = t; + } +#undef RND + + /* feedback */ + ctx->h[0] += S0; + ctx->h[1] += S1; + ctx->h[2] += S2; + ctx->h[3] += S3; + ctx->h[4] += S4; + ctx->h[5] += S5; + ctx->h[6] += S6; + ctx->h[7] += S7; +} + +void SHA256_Init (SHA256_CTX *ctx) +{ + ctx->Nl = 0; + ctx->Nh = 0; + ctx->num = 0; + ctx->h[0] = 0x6A09E667UL; + ctx->h[1] = 0xBB67AE85UL; + ctx->h[2] = 0x3C6EF372UL; + ctx->h[3] = 0xA54FF53AUL; + ctx->h[4] = 0x510E527FUL; + ctx->h[5] = 0x9B05688CUL; + ctx->h[6] = 0x1F83D9ABUL; + ctx->h[7] = 0x5BE0CD19UL; +} + +void SHA256_Update (SHA256_CTX *ctx, const void *src, unsigned int count) +{ + unsigned new_count = (ctx->Nl + (count << 3)) & 0xffffffff; + if (new_count < ctx->Nl) { + ctx->Nh += 1; + } + ctx->Nl = new_count; + + while (count) { + unsigned int this_step = 64 - ctx->num; + if (this_step > count) this_step = count; + memcpy( ctx->data + ctx->num, src, this_step); + + if (this_step + ctx->num < 64) { + ctx->num += this_step; + break; + } + + src = (const unsigned char *)src + this_step; + count -= this_step; + ctx->num = 0; + + sha256_compress( ctx, ctx->data ); + } +} + +/* + * Add padding and return the message digest. + */ +void SHA256_Final (unsigned char *digest, SHA256_CTX *ctx) +{ + unsigned int i; + unsigned char finalcount[SHA256_FINALCOUNT_SIZE]; + + put_bigendian( &finalcount[0], ctx->Nh, 4 ); + put_bigendian( &finalcount[4], ctx->Nl, 4 ); + + SHA256_Update(ctx, "\200", 1); + + if (ctx->num > 56) { + SHA256_Update(ctx, "\0\0\0\0\0\0\0\0", 8); + } + memset( ctx->data + ctx->num, 0, 56 - ctx->num ); + ctx->num = 56; + SHA256_Update(ctx, finalcount, SHA256_FINALCOUNT_SIZE); /* Should cause a sha256_compress() */ + + /* + * The final state is an array of unsigned long's; place them as a series + * of bigendian 4-byte words onto the output + */ + for (i=0; i<8; i++) { + put_bigendian( digest + 4*i, ctx->h[i], 4 ); + } +} +#endif diff --git a/src/sig_stateful/lms/external/sha256.h b/src/sig_stateful/lms/external/sha256.h new file mode 100644 index 0000000000..a5de21c014 --- /dev/null +++ b/src/sig_stateful/lms/external/sha256.h @@ -0,0 +1,43 @@ +#if !defined(SHA256_H_) +#define SHA256_H_ + +#if defined( EXT_SHA256_H ) +#include EXT_SHA256_H +#else + +#define USE_OPENSSL 0 /* We use the OpenSSL implementation for SHA-256 */ + /* (which is quite a bit faster than our portable */ + /* C version) */ + +#if USE_OPENSSL + +#include + +#else + +/* SHA256 context. */ +typedef struct { + unsigned long int h[8]; /* state; this is in the CPU native format */ + unsigned long Nl, Nh; /* number of bits processed so far */ + unsigned num; /* number of bytes within the below */ + /* buffer */ + unsigned char data[64]; /* input buffer. This is in byte vector format */ +} SHA256_CTX; + +void SHA256_Init(SHA256_CTX *); /* context */ + +void SHA256_Update(SHA256_CTX *, /* context */ + const void *, /* input block */ + unsigned int);/* length of input block */ + +void SHA256_Final(unsigned char *, + SHA256_CTX *); +#endif + +#endif /* EXT_SHA256_H */ + +#if !defined( SHA256_LEN ) +#define SHA256_LEN 32 /* The length of a SHA256 hash output */ +#endif + +#endif /* ifdef(SHA256_H_) */ diff --git a/src/sig_stateful/sig_stfl.c b/src/sig_stateful/sig_stfl.c index 378f385db1..ecea3b4480 100644 --- a/src/sig_stateful/sig_stfl.c +++ b/src/sig_stateful/sig_stfl.c @@ -33,7 +33,33 @@ OQS_API const char *OQS_SIG_STFL_alg_identifier(size_t i) { OQS_SIG_STFL_alg_xmssmt_sha256_8_h40, OQS_SIG_STFL_alg_xmssmt_sha256_3_h60, OQS_SIG_STFL_alg_xmssmt_sha256_6_h60, - OQS_SIG_STFL_alg_xmssmt_sha256_12_h60 + OQS_SIG_STFL_alg_xmssmt_sha256_12_h60, + + OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1, + OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2, + OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4, + OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8, + + OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1, + OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2, + OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4, + OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8, + + OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1, + OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2, + OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4, + OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8, + + OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1, + OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2, + OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4, + OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8, + + OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1, + OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2, + OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4, + OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8, + OQS_SIG_STFL_alg_hss_sha256 }; if (i >= OQS_SIG_STFL_algs_length) { @@ -173,6 +199,132 @@ OQS_API int OQS_SIG_STFL_alg_is_enabled(const char *method_name) { return 1; #else return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8)) { +#ifdef OQS_ENABLE_SIG_STFL_LMS + return 1; +#else + return 0; +#endif + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_hss_sha256)) { +#ifdef OQS_ENABLE_SIG_STFL_HSS + return 1; +#else + return 0; #endif } // EDIT-WHEN ADDING MORE XMSS/XMSS^MT ALGS @@ -485,6 +637,7 @@ OQS_API OQS_SECRET_KEY *OQS_SECRET_KEY_new(const char *method_name) { OQS_API void OQS_SECRET_KEY_free(OQS_SECRET_KEY *sk) { + if (sk == NULL) return; OQS_MEM_secure_free(sk->secret_key, sk->length_secret_key); OQS_MEM_secure_free(sk, sizeof(sk)); } diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index 49f48726be..af6ff9b41e 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -61,7 +61,78 @@ extern "C" { /* Algorithm identifier for XMSSMT-SHA2_60/12_256 */ #define OQS_SIG_STFL_alg_xmssmt_sha256_12_h60 "XMSSMT-SHA2_60/12_256" -#define OQS_SIG_STFL_algs_length 20 +/* Algorithm identifier for LMS-SHA2_ */ +#define OQS_SIG_STFL_alg_hss_sha256 "HSS-SHA2" + +#define OQS_SIG_STFL_algs_length 41 + +/* Defined LM parameter sets */ +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1 "5/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2 "5/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4 "5/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8 "5/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1 "10/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2 "10/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4 "10/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8 "10/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1 "15/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2 "15/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4 "15/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8 "15/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1 "20/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2 "20/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4 "20/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8 "20/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1 "25/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2 "25/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4 "25/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8 "25/8" + +/* LM registry */ +#define OQS_SIG_STFL_alg_lm_sha256_n32_h5 "LM-SHA256_N32_H5" //0x00000005 +#define OQS_SIG_STFL_alg_lm_sha256_n32_h10 "LM-SHA256_N32_H10" //0x00000006 +#define OQS_SIG_STFL_alg_lm_sha256_n32_h15 "LM-SHA256_N32_H15" //0x00000007 +#define OQS_SIG_STFL_alg_lm_sha256_n32_h20 "LM-SHA256_N32_H20" //0x00000008 +#define OQS_SIG_STFL_alg_lm_sha256_n32_h25 "LM-SHA256_N32_H25" //0x00000009 + +/* LM-OTS registry */ +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w1 "LMOTS-SHA256_N32_W1" //0x00000001 +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w2 "LMOTS-SHA256_N32_W2" //0x00000002 +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w3 "LMOTS-SHA256_N32_W4" //0x00000003 +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w4 "LMOTS-SHA256_N32_W8" //0x00000004 + +/* Defined LM parameter sets */ +/* Algorithm identifier for LMS-SHA256_N32_H5 */ +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5 "LMS-SHA256_N32_H5" //0x00000005 + +/* Algorithm identifier for LMS-SHA256_N32_H10 */ +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10 "LMS-SHA256_N32_H10" //0x00000006 + +/* Algorithm identifier for LMS-SHA256_N32_H15 */ +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15 "LMS-SHA256_N32_H15" //0x00000007 + +/* Algorithm identifier for LMS-SHA256_N32_H20 */ +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20 "LMS-SHA256_N32_H20" //0x00000008 + +/* Algorithm identifier for LMS-SHA256_N32_H25 */ +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25 "LMS-SHA256_N32_H25" //0x00000009 + +/* LM-OTS registry */ +/* Algorithm identifier for LMOTS-SHA256_N32_W1 */ +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w1 "LMOTS-SHA256_N32_W1" //0x00000001 + +/* Algorithm identifier for LMOTS-SHA256_N32_W2 */ +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w2 "LMOTS-SHA256_N32_W2" //0x00000002 + +/* Algorithm identifier for LMOTS-SHA256_N32_W4 */ +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w3 "LMOTS-SHA256_N32_W4" //0x00000003 + +/* Algorithm identifier for LMOTS-SHA256_N32_W8 */ +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w4 "LMOTS-SHA256_N32_W8" //0x00000004 /** * Returns identifiers for available signature schemes in liboqs. Used with OQS_SIG_STFL_new. @@ -320,4 +391,7 @@ OQS_API void OQS_SIG_STFL_free(OQS_SIG_STFL *sig); #include #endif // OQS_ENABLE_SIG_STFL_XMSS +#if defined(OQS_ENABLE_SIG_STFL_LMS) || defined(OQS_ENABLE_SIG_STFL_HSS) +//#include +#endif //OQS_ENABLE_SIG_STFL_LMS | OQS_ENABLE_SIG_STFL_HSS #endif /* OQS_SIG_STATEFUL_H */ From c490ee408680ca499ccc2984228e2d8e650afa03 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Tue, 11 Apr 2023 03:39:19 -0400 Subject: [PATCH 02/13] Add support for basis single level LMS parameters --- .CMake/alg_support.cmake | 3 + CMakeLists.txt | 3 + src/sig_stateful/lms/CMakeLists.txt | 4 +- src/sig_stateful/lms/sig_stfl_lms.c | 271 ++++++++++++++++++ src/sig_stateful/lms/sig_stfl_lms.h | 233 +++++++++++++++ src/sig_stateful/lms/sig_stfl_lms_functions.c | 183 ++++++++++++ src/sig_stateful/sig_stfl.c | 103 ++----- src/sig_stateful/sig_stfl.h | 70 ++--- 8 files changed, 742 insertions(+), 128 deletions(-) create mode 100644 src/sig_stateful/lms/sig_stfl_lms.c create mode 100644 src/sig_stateful/lms/sig_stfl_lms.h create mode 100644 src/sig_stateful/lms/sig_stfl_lms_functions.c diff --git a/.CMake/alg_support.cmake b/.CMake/alg_support.cmake index 26b2cc1aae..ebd625b654 100644 --- a/.CMake/alg_support.cmake +++ b/.CMake/alg_support.cmake @@ -33,3 +33,6 @@ cmake_dependent_option(OQS_ENABLE_SIG_STFL_XMSSMT_SHA256_8_H40 "" ON "OQS_ENABLE cmake_dependent_option(OQS_ENABLE_SIG_STFL_XMSSMT_SHA256_3_H60 "" ON "OQS_ENABLE_SIG_STFL_XMSS" OFF) cmake_dependent_option(OQS_ENABLE_SIG_STFL_XMSSMT_SHA256_6_H60 "" ON "OQS_ENABLE_SIG_STFL_XMSS" OFF) cmake_dependent_option(OQS_ENABLE_SIG_STFL_XMSSMT_SHA256_12_H60 "" ON "OQS_ENABLE_SIG_STFL_XMSS" OFF) + +option(OQS_ENABLE_SIG_STFL_LMS "Enable LMS algorithm family" ON) +option(OQS_ENABLE_SIG_STFL_HSS "Enable Multi level LMS algorithm" ON) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 337c264648..c5dee117e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,6 +180,9 @@ endif() if(OQS_ENABLE_SIG_STFL_XMSS) set(PUBLIC_HEADERS ${PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/src/sig_stateful/xmss/sig_stfl_xmss_xmssmt.h) endif() +if(OQS_ENABLE_SIG_STFL_LMS) + set(PUBLIC_HEADERS ${PUBLIC_HEADERS} ${PROJECT_SOURCE_DIR}/src/sig_stateful/lms/sig_stfl_lms.h) +endif() ##### OQS_COPY_FROM_UPSTREAM_FRAGMENT_INCLUDE_HEADERS_END execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/include/oqs) execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${PUBLIC_HEADERS} ${PROJECT_BINARY_DIR}/include/oqs) diff --git a/src/sig_stateful/lms/CMakeLists.txt b/src/sig_stateful/lms/CMakeLists.txt index 4f41445339..e7131a9e7d 100644 --- a/src/sig_stateful/lms/CMakeLists.txt +++ b/src/sig_stateful/lms/CMakeLists.txt @@ -23,7 +23,9 @@ set(SRCS external/lm_ots_sign.c external/lm_ots_verify.c external/lm_verify.c - external/sha256.c) + external/sha256.c + sig_stfl_lms.c + sig_stfl_lms_functions.c) add_library(lms OBJECT ${SRCS}) target_include_directories(lms PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/external) diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c new file mode 100644 index 0000000000..fa5c9a496b --- /dev/null +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include +#include "./external/config.h" +//#include "./external/xmss.h" +//#include "./external/params.h" +#include "sig_stfl_lms.h" + +//#include "./external/xmss_namespace.h" + + +// ======================== LMS5-SHA2 ======================== // + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL || public_key == NULL) { + return OQS_ERROR; + } + + uint32_t oid = 0x00000001; + LMS_UNUSED(oid); + if (oqs_sig_stfl_lms_keypair(public_key, secret_key, oid) != 0) { + return OQS_ERROR; + } + return OQS_SUCCESS; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->method_name = "LMS-SHA2_H5_W1"; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +// ================================================================ // + +// ======================== LMS-SHA256 H10======================== // +// +//OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { +// if (secret_key == NULL || public_key == NULL) { +// return OQS_ERROR; +// } +// +// uint32_t oid = 0x00000001; +// if (xmss_keypair(public_key, secret_key, oid) != 0) { +// return OQS_ERROR; +// } +// return OQS_SUCCESS; +//} +// +//OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { +// if (secret_key == NULL || public_key == NULL) { +// return OQS_ERROR; +// } +// +// uint32_t oid = 0x00000001; +// if (xmss_keypair(public_key, secret_key, oid) != 0) { +// return OQS_ERROR; +// } +// return OQS_SUCCESS; +//} +// +//OQS_SIG_STFL *OQS_SIG_STFL_alg_xmss_sha256_h10_new(void) { +// +// OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); +// if (sig == NULL) { +// return NULL; +// } +// memset(sig, 0, sizeof(OQS_SIG_STFL)); +// +// sig->method_name = "XMSS-SHA2_10_256"; +// sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; +// sig->euf_cma = true; +// +// sig->length_public_key = OQS_SIG_STFL_alg_xmss_sha256_h10_length_pk; +// sig->length_signature = OQS_SIG_STFL_alg_xmss_sha256_h10_length_signature; +// +// sig->keypair = OQS_SIG_STFL_alg_xmss_sha256_h10_keypair; +// sig->sign = OQS_SIG_STFL_alg_xmss_sign; +// sig->verify = OQS_SIG_STFL_alg_xmss_verify; +// sig->derive_subkey = OQS_SIG_STFL_alg_xmss_derive_subkey; +// +// return sig; +//} +// +//OQS_SECRET_KEY *OQS_SECRET_KEY_XMSS_SHA256_H10_new(void) { +// +// // Initialize the secret key in the heap with adequate memory +// OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); +// if (sk == NULL) { +// return NULL; +// } +// memset(sk, 0, sizeof(OQS_SECRET_KEY)); +// +// sk->length_secret_key = OQS_SIG_STFL_alg_xmss_sha256_h10_length_sk; +// +// // Assign the sigs_left and sigs_max functions +// sk->sigs_left = OQS_SECRET_KEY_xmss_sigs_left; +// sk->sigs_total = OQS_SECRET_KEY_xmss_sigs_total; +// +// // Initialize the key with length_secret_key amount of bytes. +// sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); +// memset(sk->secret_key, 0, sk->length_secret_key); +// +// return sk; +//} +// +//// ================================================================ // +// +//// ======================== XMSS16-SHA256 ======================== // +// +//OQS_API OQS_STATUS OQS_SIG_STFL_alg_xmss_sha256_h16_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { +// if (secret_key == NULL || public_key == NULL) { +// return OQS_ERROR; +// } +// +// uint32_t oid = 0x00000002; +// if (xmss_keypair(public_key, secret_key, oid) != 0) { +// return OQS_ERROR; +// } +// return OQS_SUCCESS; +//} +// +// +//OQS_SIG_STFL *OQS_SIG_STFL_alg_xmss_sha256_h16_new(void) { +// +// OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); +// if (sig == NULL) { +// return NULL; +// } +// memset(sig, 0, sizeof(OQS_SIG_STFL)); +// +// sig->method_name = "XMSS-SHA2_16_256"; +// sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; +// sig->euf_cma = true; +// +// sig->length_public_key = OQS_SIG_STFL_alg_xmss_sha256_h16_length_pk; +// sig->length_signature = OQS_SIG_STFL_alg_xmss_sha256_h16_length_signature; +// +// sig->keypair = OQS_SIG_STFL_alg_xmss_sha256_h16_keypair; +// sig->sign = OQS_SIG_STFL_alg_xmss_sign; +// sig->verify = OQS_SIG_STFL_alg_xmss_verify; +// sig->derive_subkey = OQS_SIG_STFL_alg_xmss_derive_subkey; +// +// return sig; +//} +// +//OQS_SECRET_KEY *OQS_SECRET_KEY_XMSS_SHA256_H16_new(void) { +// +// // Initialize the secret key in the heap with adequate memory +// OQS_SECRET_KEY *sk = (OQS_SECRET_KEY *)malloc(sizeof(OQS_SECRET_KEY)); +// if (sk == NULL) { +// return NULL; +// } +// memset(sk, 0, sizeof(OQS_SECRET_KEY)); +// +// sk->length_secret_key = OQS_SIG_STFL_alg_xmss_sha256_h16_length_sk; +// +// // Assign the sigs_left and sigs_max functions +// sk->sigs_left = OQS_SECRET_KEY_xmss_sigs_left; +// sk->sigs_total = OQS_SECRET_KEY_xmss_sigs_total; +// +// // Initialize the key with length_secret_key amount of bytes. +// sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); +// memset(sk->secret_key, 0, sk->length_secret_key); +// return sk; +//} +// +//// ================================================================ // +// +// +//// ======================== XMSS20-SHA256 ======================== // +// +//OQS_API OQS_STATUS OQS_SIG_STFL_alg_xmss_sha256_h20_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { +// if (secret_key == NULL || public_key == NULL) { +// return OQS_ERROR; +// } +// +// uint32_t oid = 0x00000003; +// if (xmss_keypair(public_key, secret_key, oid) != 0) { +// return OQS_ERROR; +// } +// return OQS_SUCCESS; +//} +// +//OQS_SIG_STFL *OQS_SIG_STFL_alg_xmss_sha256_h20_new(void) { +// +// OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); +// if (sig == NULL) { +// return NULL; +// } +// memset(sig, 0, sizeof(OQS_SIG_STFL)); +// +// sig->method_name = "XMSS-SHA2_20_256"; +// sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; +// sig->euf_cma = true; +// +// sig->length_public_key = OQS_SIG_STFL_alg_xmss_sha256_h20_length_pk; +// sig->length_signature = OQS_SIG_STFL_alg_xmss_sha256_h20_length_signature; +// +// sig->keypair = OQS_SIG_STFL_alg_xmss_sha256_h20_keypair; +// sig->sign = OQS_SIG_STFL_alg_xmss_sign; +// sig->verify = OQS_SIG_STFL_alg_xmss_verify; +// sig->derive_subkey = OQS_SIG_STFL_alg_xmss_derive_subkey; +// +// return sig; +//} +// +//OQS_SECRET_KEY *OQS_SECRET_KEY_XMSS_SHA256_H20_new(void) { +// +// // Initialize the secret key in the heap with adequate memory +// OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); +// if (sk == NULL) { +// return NULL; +// } +// memset(sk, 0, sizeof(OQS_SECRET_KEY)); +// +// sk->length_secret_key = OQS_SIG_STFL_alg_xmss_sha256_h20_length_sk ; +// // Assign the sigs_left and sigs_max functions +// sk->sigs_left = OQS_SECRET_KEY_xmss_sigs_left; +// sk->sigs_total = OQS_SECRET_KEY_xmss_sigs_total; +// +// // Initialize the key with length_secret_key amount of bytes. +// sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); +// memset(sk->secret_key, 0, sk->length_secret_key); +// +// return sk; +//} + +// ================================================================ // diff --git a/src/sig_stateful/lms/sig_stfl_lms.h b/src/sig_stateful/lms/sig_stfl_lms.h new file mode 100644 index 0000000000..fbef130424 --- /dev/null +++ b/src/sig_stateful/lms/sig_stfl_lms.h @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: MIT + +#ifndef OQS_SIG_STFL_LMS_LMSMT_H +#define OQS_SIG_STFL_LMS_LMSMT_H + +#include + +#define LMS_OID_LEN 4 + +//#ifdef OQS_ENABLE_SIG_STFL_LMS + +//H5 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_signature 8688 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_signature 4464 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_w2_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w2_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_signature 2352 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_w4_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w4_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_signature 1296 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_w8_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w8_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +//H10 +// H10 W1 60 8848 64 +// H10 W2 60 4624 64 +// H10 W4 60 2512 64 +// H10 W8 60 1456 64 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_signature 8848 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w1_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w1_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_signature 4624 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w2_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w2_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_signature 2512 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w4_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w4_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_signature 1456 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w8_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w8_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +//H15 +// H15 W1 60 9008 64 +// H15 W2 60 4784 64 +// H15 W4 60 2672 64 +// H15 W8 60 1616 64 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_signature 9008 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w1_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w1_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_signature 4784 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w2_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w2_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_signature 2672 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w4_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w4_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_signature 1616 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w8_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w8_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +//H20 +// H20 W1 60 9168 64 +// H20 W2 60 4944 64 +// H20 W4 60 2832 64 +// H20 W8 60 1776 64 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_signature 9168 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w1_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w1_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_signature 4944 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w2_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w2_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_signature 2832 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w4_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w4_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_signature 1776 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w8_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w8_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +//H25 +// H25 W1 60 9328 64 +// H25 W2 60 5104 64 +// H25 W4 60 2992 64 +// H25 W8 60 1936 64 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_signature 9328 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w1_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w1_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_signature 5104 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w2_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w2_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_signature 2992 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w4_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w4_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + +#define OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_signature 1936 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_pk 60 +#define OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_sk 64 + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w8_new(void); + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w8_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); + + +// ----------------------------------- WRAPPER FUNCTIONS ------------------------------------------------ +int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid); + + +// ---------------------------- FUNCTIONS INDEPENDENT OF VARIANT ----------------------------------------- + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key); + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key); + +unsigned long long OQS_SECRET_KEY_lms_sigs_left(const OQS_SECRET_KEY *secret_key); + +unsigned long long OQS_SECRET_KEY_lms_sigs_total(const OQS_SECRET_KEY *secret_key); + + +OQS_SECRET_KEY *OQS_SIG_STFL_alg_lms_derive_subkey(OQS_SECRET_KEY *master_key, const unsigned long long number_of_sigs); + +// -------------------------------------------------------------------------------------------------------- +//#endif //OQS_ENABLE_SIG_STFL_LMS +#endif /* OQS_SIG_STFL_LMS_H */ diff --git a/src/sig_stateful/lms/sig_stfl_lms_functions.c b/src/sig_stateful/lms/sig_stfl_lms_functions.c new file mode 100644 index 0000000000..718127d394 --- /dev/null +++ b/src/sig_stateful/lms/sig_stfl_lms_functions.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT + +#include +#include "sig_stfl_lms.h" +#include "external/config.h" + + + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL || message == NULL || signature == NULL) { + return OQS_ERROR; + } + + LMS_UNUSED(signature_length); + LMS_UNUSED(message_len); + +// return OQS_SUCCESS; + return OQS_ERROR; +} + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + if (message == NULL || signature == NULL || public_key == NULL) { + return OQS_ERROR; + } + + LMS_UNUSED(signature_len); + LMS_UNUSED(message_len); + +// return OQS_SUCCESS; + return OQS_ERROR; +} + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_hss_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL || message == NULL || signature == NULL) { + return OQS_ERROR; + } + + LMS_UNUSED(signature_length); + LMS_UNUSED(message_len); + + +// return OQS_SUCCESS; + return OQS_ERROR; +} + +OQS_API OQS_STATUS OQS_SIG_STFL_alg_hss_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { + if (message == NULL || signature == NULL || public_key == NULL) { + return OQS_ERROR; + } + LMS_UNUSED(signature_len); + LMS_UNUSED(message_len); + +// return OQS_SUCCESS; + return OQS_ERROR; +} + +unsigned long long OQS_SECRET_KEY_lms_sigs_left(const OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL) { + return -1; + } + + LMS_UNUSED(secret_key); + + return 0; +} + +unsigned long long OQS_SECRET_KEY_lms_sigs_total(const OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL) { + return -1; + } + + return 0; +} + +unsigned long long OQS_SECRET_KEY_hss_sigs_left(const OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL) { + return -1; + } + + return 0; +} + +unsigned long long OQS_SECRET_KEY_hss_sigs_total(const OQS_SECRET_KEY *secret_key) { + if (secret_key == NULL) { + return -1; + } + return 0; +} + +OQS_SECRET_KEY *OQS_SIG_STFL_alg_lms_derive_subkey(OQS_SECRET_KEY *master_key, const unsigned long long number_of_sigs) { + + LMS_UNUSED(master_key); + LMS_UNUSED(number_of_sigs); + + return NULL; +} + +OQS_SECRET_KEY *OQS_SIG_STFL_alg_hss_derive_subkey(OQS_SECRET_KEY *master_key, const unsigned long long number_of_sigs) { + LMS_UNUSED(master_key); + LMS_UNUSED(number_of_sigs); + + return NULL; +} + + +/* LMS wrapper functions use internal OIDs to + * identify the parameter set to be used + */ + +int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid) +{ + int ret = -1; + LMS_UNUSED(sk); + LMS_UNUSED(oid); + LMS_UNUSED(pk); + + // Set lms param set + // gen key pair + // store key pair + + return ret; +} + +int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, + uint8_t *sm, unsigned long long *smlen, + const uint8_t *m, unsigned long long mlen) +{ + LMS_UNUSED(sk); + LMS_UNUSED(sm); + LMS_UNUSED(smlen); + LMS_UNUSED(m); + LMS_UNUSED(mlen); + + return -1; +} + +int oqs_sig_stfl_lms_verify(uint8_t *m, unsigned long long *mlen, + const uint8_t *sm, unsigned long long smlen, + const uint8_t *pk) +{ + LMS_UNUSED(m); + LMS_UNUSED(mlen); + LMS_UNUSED(sm); + LMS_UNUSED(smlen); + LMS_UNUSED(pk); + + return -1; +} + +int oqs_sig_stfl_hss_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid) +{ + LMS_UNUSED(sk); + LMS_UNUSED(oid); + LMS_UNUSED(pk); + + return -1; +} + +int oqs_sig_stfl_hss_sign(OQS_SECRET_KEY *sk, + uint8_t *sm, unsigned long long *smlen, + const uint8_t *m, unsigned long long mlen) +{ + LMS_UNUSED(sk); + LMS_UNUSED(sm); + LMS_UNUSED(smlen); + LMS_UNUSED(m); + LMS_UNUSED(mlen); + + return -1; +} + +int oqs_sig_stfl_hss_verify(uint8_t *m, unsigned long long *mlen, + const uint8_t *sm, unsigned long long smlen, + const uint8_t *pk) +{ + LMS_UNUSED(m); + LMS_UNUSED(mlen); + LMS_UNUSED(sm); + LMS_UNUSED(smlen); + LMS_UNUSED(pk); + + return -1; +} diff --git a/src/sig_stateful/sig_stfl.c b/src/sig_stateful/sig_stfl.c index ecea3b4480..71bdafd5fe 100644 --- a/src/sig_stateful/sig_stfl.c +++ b/src/sig_stateful/sig_stfl.c @@ -200,131 +200,51 @@ OQS_API int OQS_SIG_STFL_alg_is_enabled(const char *method_name) { #else return 0; #endif - } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { #ifdef OQS_ENABLE_SIG_STFL_LMS + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; -#endif } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8)) { -#ifdef OQS_ENABLE_SIG_STFL_LMS return 1; -#else - return 0; #endif - } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_hss_sha256)) { #ifdef OQS_ENABLE_SIG_STFL_HSS + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_hss_sha256)) { return 1; -#else - return 0; #endif } // EDIT-WHEN ADDING MORE XMSS/XMSS^MT ALGS @@ -458,6 +378,11 @@ OQS_API OQS_SIG_STFL *OQS_SIG_STFL_new(const char *method_name) { return OQS_SIG_STFL_alg_xmssmt_sha256_12_h60_new(); #else return NULL; +#endif +#ifdef OQS_ENABLE_SIG_STFL_LMS + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { + return OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(); + #endif } else { return NULL; @@ -466,7 +391,7 @@ OQS_API OQS_SIG_STFL *OQS_SIG_STFL_new(const char *method_name) { OQS_API OQS_STATUS OQS_SIG_STFL_keypair(const OQS_SIG_STFL *sig, uint8_t *public_key, OQS_SECRET_KEY *secret_key) { - if (sig == NULL || sig->keypair(public_key, secret_key) != 0) { + if (sig == NULL || sig->keypair == NULL || sig->keypair(public_key, secret_key) != 0) { return OQS_ERROR; } else { return OQS_SUCCESS; @@ -474,7 +399,7 @@ OQS_API OQS_STATUS OQS_SIG_STFL_keypair(const OQS_SIG_STFL *sig, uint8_t *public } OQS_API OQS_STATUS OQS_SIG_STFL_sign(const OQS_SIG_STFL *sig, uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { - if (sig == NULL || sig->sign(signature, signature_len, message, message_len, secret_key) != 0) { + if (sig == NULL || sig->sign == NULL || sig->sign(signature, signature_len, message, message_len, secret_key) != 0) { return OQS_ERROR; } else { return OQS_SUCCESS; @@ -482,7 +407,7 @@ OQS_API OQS_STATUS OQS_SIG_STFL_sign(const OQS_SIG_STFL *sig, uint8_t *signature } OQS_API OQS_STATUS OQS_SIG_STFL_verify(const OQS_SIG_STFL *sig, const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { - if (sig == NULL || sig->verify(message, message_len, signature, signature_len, public_key) != 0) { + if (sig == NULL || sig->verify == NULL || sig->verify(message, message_len, signature, signature_len, public_key) != 0) { return OQS_ERROR; } else { return OQS_SUCCESS; @@ -629,8 +554,14 @@ OQS_API OQS_SECRET_KEY *OQS_SECRET_KEY_new(const char *method_name) { return OQS_SECRET_KEY_XMSSMT_SHA256_12_H60_new(); #else return NULL; + #endif - } else { +#ifdef OQS_ENABLE_SIG_STFL_LMS + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { + return OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(); +#endif + } + else { return NULL; } } diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index af6ff9b41e..4d79f38562 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -67,43 +67,30 @@ extern "C" { #define OQS_SIG_STFL_algs_length 41 /* Defined LM parameter sets */ -#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1 "5/1" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2 "5/2" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4 "5/4" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8 "5/8" - -#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1 "10/1" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2 "10/2" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4 "10/4" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8 "10/8" - -#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1 "15/1" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2 "15/2" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4 "15/4" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8 "15/8" - -#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1 "20/1" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2 "20/2" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4 "20/4" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8 "20/8" - -#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1 "25/1" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2 "25/2" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4 "25/4" -#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8 "25/8" - -/* LM registry */ -#define OQS_SIG_STFL_alg_lm_sha256_n32_h5 "LM-SHA256_N32_H5" //0x00000005 -#define OQS_SIG_STFL_alg_lm_sha256_n32_h10 "LM-SHA256_N32_H10" //0x00000006 -#define OQS_SIG_STFL_alg_lm_sha256_n32_h15 "LM-SHA256_N32_H15" //0x00000007 -#define OQS_SIG_STFL_alg_lm_sha256_n32_h20 "LM-SHA256_N32_H20" //0x00000008 -#define OQS_SIG_STFL_alg_lm_sha256_n32_h25 "LM-SHA256_N32_H25" //0x00000009 - -/* LM-OTS registry */ -#define OQS_SIG_STFL_alg_lmots_sha256_n32_w1 "LMOTS-SHA256_N32_W1" //0x00000001 -#define OQS_SIG_STFL_alg_lmots_sha256_n32_w2 "LMOTS-SHA256_N32_W2" //0x00000002 -#define OQS_SIG_STFL_alg_lmots_sha256_n32_w3 "LMOTS-SHA256_N32_W4" //0x00000003 -#define OQS_SIG_STFL_alg_lmots_sha256_n32_w4 "LMOTS-SHA256_N32_W8" //0x00000004 +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1 "LMS-SHA256-H5_W1" //"5/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2 "LMS-SHA256-H5_W2" //"5/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4 "LMS-SHA256-H5_W4" //"5/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8 "LMS-SHA256-H5_W8" //"5/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1 "LMS-SHA256-H10_W1" //"10/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2 "LMS-SHA256-H10_W2" //"10/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4 "LMS-SHA256-H10_W4" //"10/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8 "LMS-SHA256-H10_W8" //"10/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1 "LMS-SHA256-H15_W1" //"15/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2 "LMS-SHA256-H15_W2" //"15/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4 "LMS-SHA256-H15_W4" //"15/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8 "LMS-SHA256-H15_W8" //"15/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1 "LMS-SHA256-H20_W1" //"20/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2 "LMS-SHA256-H20_W2" //"20/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4 "LMS-SHA256-H20_W4" //"20/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8 "LMS-SHA256-H20_W8" //"20/8" + +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1 "LMS-SHA256-H25_W1" //"25/1" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2 "LMS-SHA256-H25_W2" //"25/2" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4 "LMS-SHA256-H25_W4" //"25/4" +#define OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8 "LMS-SHA256-H25_W8" //"25/8" /* Defined LM parameter sets */ /* Algorithm identifier for LMS-SHA256_N32_H5 */ @@ -129,10 +116,10 @@ extern "C" { #define OQS_SIG_STFL_alg_lmots_sha256_n32_w2 "LMOTS-SHA256_N32_W2" //0x00000002 /* Algorithm identifier for LMOTS-SHA256_N32_W4 */ -#define OQS_SIG_STFL_alg_lmots_sha256_n32_w3 "LMOTS-SHA256_N32_W4" //0x00000003 +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w4 "LMOTS-SHA256_N32_W4" //0x00000003 /* Algorithm identifier for LMOTS-SHA256_N32_W8 */ -#define OQS_SIG_STFL_alg_lmots_sha256_n32_w4 "LMOTS-SHA256_N32_W8" //0x00000004 +#define OQS_SIG_STFL_alg_lmots_sha256_n32_w8 "LMOTS-SHA256_N32_W8" //0x00000004 /** * Returns identifiers for available signature schemes in liboqs. Used with OQS_SIG_STFL_new. @@ -391,7 +378,8 @@ OQS_API void OQS_SIG_STFL_free(OQS_SIG_STFL *sig); #include #endif // OQS_ENABLE_SIG_STFL_XMSS -#if defined(OQS_ENABLE_SIG_STFL_LMS) || defined(OQS_ENABLE_SIG_STFL_HSS) -//#include +//#if defined(OQS_ENABLE_SIG_STFL_LMS) || defined(OQS_ENABLE_SIG_STFL_HSS) +#ifdef OQS_ENABLE_SIG_STFL_LMS +#include #endif //OQS_ENABLE_SIG_STFL_LMS | OQS_ENABLE_SIG_STFL_HSS #endif /* OQS_SIG_STATEFUL_H */ From 4f5449134e1ea7f94adcd529542fac5b49ee4764 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Sat, 15 Apr 2023 00:43:10 -0400 Subject: [PATCH 03/13] Support single level trees --- src/sig_stateful/lms/external/hss_generate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sig_stateful/lms/external/hss_generate.c b/src/sig_stateful/lms/external/hss_generate.c index be2c991081..e57570e6d2 100644 --- a/src/sig_stateful/lms/external/hss_generate.c +++ b/src/sig_stateful/lms/external/hss_generate.c @@ -317,6 +317,7 @@ bool hss_generate_working_key( unsigned index = count & tree->max_index; count >>= tree->level; tree->current_index = index; + if (i == 0) break; // This is a single level tree } /* Initialize the I values */ @@ -563,7 +564,9 @@ bool hss_generate_working_key( } // bot_level_subtree -= h_subtree; + if (j == 0) break; //This is a single level tree } + if (i == 0) break; //This is a single level tree } #if DO_FLOATING_POINT From aa5942c1242c642ae2ab2f9a04e87f2b0e2aaf57 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Sat, 15 Apr 2023 00:44:28 -0400 Subject: [PATCH 04/13] Add support for LMS gen key, sign and verify for single level LMS trees --- src/sig_stateful/lms/sig_stfl_lms.c | 200 +---------- src/sig_stateful/lms/sig_stfl_lms.h | 60 +++- src/sig_stateful/lms/sig_stfl_lms_functions.c | 336 ++++++++++++++++-- 3 files changed, 363 insertions(+), 233 deletions(-) diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c index fa5c9a496b..1e9d3d1bb8 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.c +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -1,19 +1,13 @@ // SPDX-License-Identifier: MIT #include -#include -#include - #include #include "./external/config.h" -//#include "./external/xmss.h" -//#include "./external/params.h" #include "sig_stfl_lms.h" -//#include "./external/xmss_namespace.h" -// ======================== LMS5-SHA2 ======================== // +// ======================== LMS-SHA256 H5/W1 ======================== // OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { if (secret_key == NULL || public_key == NULL) { @@ -75,197 +69,5 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { // ================================================================ // -// ======================== LMS-SHA256 H10======================== // -// -//OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { -// if (secret_key == NULL || public_key == NULL) { -// return OQS_ERROR; -// } -// -// uint32_t oid = 0x00000001; -// if (xmss_keypair(public_key, secret_key, oid) != 0) { -// return OQS_ERROR; -// } -// return OQS_SUCCESS; -//} -// -//OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { -// if (secret_key == NULL || public_key == NULL) { -// return OQS_ERROR; -// } -// -// uint32_t oid = 0x00000001; -// if (xmss_keypair(public_key, secret_key, oid) != 0) { -// return OQS_ERROR; -// } -// return OQS_SUCCESS; -//} -// -//OQS_SIG_STFL *OQS_SIG_STFL_alg_xmss_sha256_h10_new(void) { -// -// OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); -// if (sig == NULL) { -// return NULL; -// } -// memset(sig, 0, sizeof(OQS_SIG_STFL)); -// -// sig->method_name = "XMSS-SHA2_10_256"; -// sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; -// sig->euf_cma = true; -// -// sig->length_public_key = OQS_SIG_STFL_alg_xmss_sha256_h10_length_pk; -// sig->length_signature = OQS_SIG_STFL_alg_xmss_sha256_h10_length_signature; -// -// sig->keypair = OQS_SIG_STFL_alg_xmss_sha256_h10_keypair; -// sig->sign = OQS_SIG_STFL_alg_xmss_sign; -// sig->verify = OQS_SIG_STFL_alg_xmss_verify; -// sig->derive_subkey = OQS_SIG_STFL_alg_xmss_derive_subkey; -// -// return sig; -//} -// -//OQS_SECRET_KEY *OQS_SECRET_KEY_XMSS_SHA256_H10_new(void) { -// -// // Initialize the secret key in the heap with adequate memory -// OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); -// if (sk == NULL) { -// return NULL; -// } -// memset(sk, 0, sizeof(OQS_SECRET_KEY)); -// -// sk->length_secret_key = OQS_SIG_STFL_alg_xmss_sha256_h10_length_sk; -// -// // Assign the sigs_left and sigs_max functions -// sk->sigs_left = OQS_SECRET_KEY_xmss_sigs_left; -// sk->sigs_total = OQS_SECRET_KEY_xmss_sigs_total; -// -// // Initialize the key with length_secret_key amount of bytes. -// sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); -// memset(sk->secret_key, 0, sk->length_secret_key); -// -// return sk; -//} -// -//// ================================================================ // -// -//// ======================== XMSS16-SHA256 ======================== // -// -//OQS_API OQS_STATUS OQS_SIG_STFL_alg_xmss_sha256_h16_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { -// if (secret_key == NULL || public_key == NULL) { -// return OQS_ERROR; -// } -// -// uint32_t oid = 0x00000002; -// if (xmss_keypair(public_key, secret_key, oid) != 0) { -// return OQS_ERROR; -// } -// return OQS_SUCCESS; -//} -// -// -//OQS_SIG_STFL *OQS_SIG_STFL_alg_xmss_sha256_h16_new(void) { -// -// OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); -// if (sig == NULL) { -// return NULL; -// } -// memset(sig, 0, sizeof(OQS_SIG_STFL)); -// -// sig->method_name = "XMSS-SHA2_16_256"; -// sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; -// sig->euf_cma = true; -// -// sig->length_public_key = OQS_SIG_STFL_alg_xmss_sha256_h16_length_pk; -// sig->length_signature = OQS_SIG_STFL_alg_xmss_sha256_h16_length_signature; -// -// sig->keypair = OQS_SIG_STFL_alg_xmss_sha256_h16_keypair; -// sig->sign = OQS_SIG_STFL_alg_xmss_sign; -// sig->verify = OQS_SIG_STFL_alg_xmss_verify; -// sig->derive_subkey = OQS_SIG_STFL_alg_xmss_derive_subkey; -// -// return sig; -//} -// -//OQS_SECRET_KEY *OQS_SECRET_KEY_XMSS_SHA256_H16_new(void) { -// -// // Initialize the secret key in the heap with adequate memory -// OQS_SECRET_KEY *sk = (OQS_SECRET_KEY *)malloc(sizeof(OQS_SECRET_KEY)); -// if (sk == NULL) { -// return NULL; -// } -// memset(sk, 0, sizeof(OQS_SECRET_KEY)); -// -// sk->length_secret_key = OQS_SIG_STFL_alg_xmss_sha256_h16_length_sk; -// -// // Assign the sigs_left and sigs_max functions -// sk->sigs_left = OQS_SECRET_KEY_xmss_sigs_left; -// sk->sigs_total = OQS_SECRET_KEY_xmss_sigs_total; -// -// // Initialize the key with length_secret_key amount of bytes. -// sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); -// memset(sk->secret_key, 0, sk->length_secret_key); -// return sk; -//} -// -//// ================================================================ // -// -// -//// ======================== XMSS20-SHA256 ======================== // -// -//OQS_API OQS_STATUS OQS_SIG_STFL_alg_xmss_sha256_h20_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { -// if (secret_key == NULL || public_key == NULL) { -// return OQS_ERROR; -// } -// -// uint32_t oid = 0x00000003; -// if (xmss_keypair(public_key, secret_key, oid) != 0) { -// return OQS_ERROR; -// } -// return OQS_SUCCESS; -//} -// -//OQS_SIG_STFL *OQS_SIG_STFL_alg_xmss_sha256_h20_new(void) { -// -// OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); -// if (sig == NULL) { -// return NULL; -// } -// memset(sig, 0, sizeof(OQS_SIG_STFL)); -// -// sig->method_name = "XMSS-SHA2_20_256"; -// sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; -// sig->euf_cma = true; -// -// sig->length_public_key = OQS_SIG_STFL_alg_xmss_sha256_h20_length_pk; -// sig->length_signature = OQS_SIG_STFL_alg_xmss_sha256_h20_length_signature; -// -// sig->keypair = OQS_SIG_STFL_alg_xmss_sha256_h20_keypair; -// sig->sign = OQS_SIG_STFL_alg_xmss_sign; -// sig->verify = OQS_SIG_STFL_alg_xmss_verify; -// sig->derive_subkey = OQS_SIG_STFL_alg_xmss_derive_subkey; -// -// return sig; -//} -// -//OQS_SECRET_KEY *OQS_SECRET_KEY_XMSS_SHA256_H20_new(void) { -// -// // Initialize the secret key in the heap with adequate memory -// OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); -// if (sk == NULL) { -// return NULL; -// } -// memset(sk, 0, sizeof(OQS_SECRET_KEY)); -// -// sk->length_secret_key = OQS_SIG_STFL_alg_xmss_sha256_h20_length_sk ; -// // Assign the sigs_left and sigs_max functions -// sk->sigs_left = OQS_SECRET_KEY_xmss_sigs_left; -// sk->sigs_total = OQS_SECRET_KEY_xmss_sigs_total; -// -// // Initialize the key with length_secret_key amount of bytes. -// sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); -// memset(sk->secret_key, 0, sk->length_secret_key); -// -// return sk; -//} // ================================================================ // diff --git a/src/sig_stateful/lms/sig_stfl_lms.h b/src/sig_stateful/lms/sig_stfl_lms.h index fbef130424..05672dd0c0 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.h +++ b/src/sig_stateful/lms/sig_stfl_lms.h @@ -4,7 +4,8 @@ #define OQS_SIG_STFL_LMS_LMSMT_H #include - +#include "external/hss.h" +#include "external/hss_sign_inc.h" #define LMS_OID_LEN 4 //#ifdef OQS_ENABLE_SIG_STFL_LMS @@ -214,6 +215,11 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w8_keypair(uint8_t *public_ke // ----------------------------------- WRAPPER FUNCTIONS ------------------------------------------------ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid); +int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, uint8_t *sm, unsigned long long *smlen, + const uint8_t *m, unsigned long long mlen); + +int oqs_sig_stfl_lms_verify(const uint8_t *m, size_t mlen, const uint8_t *sm, size_t smlen, + const uint8_t *pk); // ---------------------------- FUNCTIONS INDEPENDENT OF VARIANT ----------------------------------------- @@ -229,5 +235,57 @@ unsigned long long OQS_SECRET_KEY_lms_sigs_total(const OQS_SECRET_KEY *secret_ke OQS_SECRET_KEY *OQS_SIG_STFL_alg_lms_derive_subkey(OQS_SECRET_KEY *master_key, const unsigned long long number_of_sigs); // -------------------------------------------------------------------------------------------------------- + + +/** + * @brief OQS_LMS_KEY object for HSS key pair + */ +typedef struct OQS_LMS_KEY_DATA oqs_lms_key_data; + +typedef struct OQS_LMS_KEY_DATA { + + /* Tree levels. */ + unsigned levels; + + /* Array, 8 levels max, of LMS types */ + param_set_t lm_type[8]; + + /* Array, 8 levels max, of LM OTS types */ + param_set_t lm_ots_type[8]; + + /* LMS public key */ + unsigned char public_key[60]; + + /* internal nodes info of the Merkle tree */ + unsigned char *aux_data; + + /* Length of aux data */ + size_t len_aux_data; + + /* User defined data that may be used for the SAFETY functions */ + void *data; + +} oqs_lms_key_data; + + +typedef struct OQS_LMS_SIG_DATA oqs_lms_sig_data; + +typedef struct OQS_LMS_SIG_DATA { + + + /* message buffer */ + unsigned char *message; + + /* Length of msg buffer */ + size_t len_msg_buf; + + /* signature buffer */ + unsigned char *signature; + + /* Length of sig buffer */ + size_t len_sig_buf; + +} oqs_lms_sig_data; + //#endif //OQS_ENABLE_SIG_STFL_LMS #endif /* OQS_SIG_STFL_LMS_H */ diff --git a/src/sig_stateful/lms/sig_stfl_lms_functions.c b/src/sig_stateful/lms/sig_stfl_lms_functions.c index 718127d394..f53086a334 100644 --- a/src/sig_stateful/lms/sig_stfl_lms_functions.c +++ b/src/sig_stateful/lms/sig_stfl_lms_functions.c @@ -3,31 +3,45 @@ #include #include "sig_stfl_lms.h" #include "external/config.h" - - +#include "external/hss.h" +#include "external/hss_verify_inc.h" +#include OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { if (secret_key == NULL || message == NULL || signature == NULL) { return OQS_ERROR; } - LMS_UNUSED(signature_length); - LMS_UNUSED(message_len); + if (oqs_sig_stfl_lms_sign(secret_key, signature, + (unsigned long long *)signature_length, + message, (unsigned long long) message_len) !=0) { + return OQS_ERROR; + } + return OQS_SUCCESS; -// return OQS_SUCCESS; - return OQS_ERROR; } OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { - if (message == NULL || signature == NULL || public_key == NULL) { - return OQS_ERROR; - } + if (message == NULL || signature == NULL || public_key == NULL) { + return OQS_ERROR; + } - LMS_UNUSED(signature_len); - LMS_UNUSED(message_len); + LMS_UNUSED(signature_len); + LMS_UNUSED(message_len); -// return OQS_SUCCESS; - return OQS_ERROR; + if (oqs_sig_stfl_lms_verify(message, message_len, + signature, signature_len, + public_key) !=0 ) { + return OQS_ERROR; + } + // if (oqs_sig_stfl_lms_verify(secret_key, signature, + // (unsigned long long *)signature_length, + // message, (unsigned long long) message_len) !=0) { + // return OQS_ERROR; + // } + + return OQS_SUCCESS; + // return OQS_ERROR; } OQS_API OQS_STATUS OQS_SIG_STFL_alg_hss_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { @@ -107,17 +121,200 @@ OQS_SECRET_KEY *OQS_SIG_STFL_alg_hss_derive_subkey(OQS_SECRET_KEY *master_key, c * identify the parameter set to be used */ +bool LMS_randombytes(void *buffer, size_t length); +bool LMS_randombytes(void *buffer, size_t length) +{ + OQS_randombytes((uint8_t *)buffer, length); + return true; +} + +/* + * This saves the private key to secure storage; in this case, a file on the + * filesystem. The context pointer we use here is the filename + */ +/*static*/ bool oqs_update_private_key( unsigned char *private_key, + size_t len_private_key, void *filename) { + FILE *f = fopen( filename, "r+" ); + if (!f) { + /* Open failed, possibly because the file didn't exist */ + f = fopen( filename, "w" ); + if (!f) { + /* Unable to open file */ + return false; + } + } + if (1 != fwrite( private_key, len_private_key, 1, f )) { + /* Write failed */ + fclose(f); + return false; + } + if (0 != fclose(f)) { + /* Close failed (possibly because pending write failed) */ + return false; + } + + /* Everything succeeded */ + return true; +} + int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid) { int ret = -1; - LMS_UNUSED(sk); - LMS_UNUSED(oid); - LMS_UNUSED(pk); + bool b_ret; + int parse_err = 0; + unsigned levels = 1; + + unsigned char public_key[60];// = NULL; + size_t len_public_key = 60; + unsigned char aux_data[10000]; + size_t len_aux_data = 10000; + + param_set_t lm_type[1]; + param_set_t lm_ots_type[1]; + + if (!pk || !sk || !oid) return -1; + + switch (oid) + { + case 1: + lm_type[0] = LMS_SHA256_N32_H5; + lm_ots_type[0] = LMOTS_SHA256_N32_W1; + break; + case 2: + lm_type[0] = LMS_SHA256_N32_H5; + lm_ots_type[0] = LMOTS_SHA256_N32_W2; + break; + case 3: + lm_type[0] = LMS_SHA256_N32_H5; + lm_ots_type[0] = LMOTS_SHA256_N32_W4; + break; + case 4: + lm_type[0] = LMS_SHA256_N32_H5; + lm_ots_type[0] = LMOTS_SHA256_N32_W8; + break; + case 5: + lm_type[0] = LMS_SHA256_N32_H10; + lm_ots_type[0] = LMOTS_SHA256_N32_W1; + break; + case 6: + lm_type[0] = LMS_SHA256_N32_H10; + lm_ots_type[0] = LMOTS_SHA256_N32_W2; + break; + case 7: + lm_type[0] = LMS_SHA256_N32_H10; + lm_ots_type[0] = LMOTS_SHA256_N32_W4; + break; + case 8: + lm_type[0] = LMS_SHA256_N32_H10; + lm_ots_type[0] = LMOTS_SHA256_N32_W8; + break; + case 9: + lm_type[0] = LMS_SHA256_N32_H15; + lm_ots_type[0] = LMOTS_SHA256_N32_W1; + break; + case 10: + lm_type[0] = LMS_SHA256_N32_H15; + lm_ots_type[0] = LMOTS_SHA256_N32_W2; + break; + case 11: + lm_type[0] = LMS_SHA256_N32_H15; + lm_ots_type[0] = LMOTS_SHA256_N32_W4; + break; + case 12: + lm_type[0] = LMS_SHA256_N32_H15; + lm_ots_type[0] = LMOTS_SHA256_N32_W8; + break; + case 13: + lm_type[0] = LMS_SHA256_N32_H20; + lm_ots_type[0] = LMOTS_SHA256_N32_W1; + break; + case 14: + lm_type[0] = LMS_SHA256_N32_H20; + lm_ots_type[0] = LMOTS_SHA256_N32_W2; + break; + case 15: + lm_type[0] = LMS_SHA256_N32_H20; + lm_ots_type[0] = LMOTS_SHA256_N32_W4; + break; + case 16: + lm_type[0] = LMS_SHA256_N32_H20; + lm_ots_type[0] = LMOTS_SHA256_N32_W8; + break; + case 17: + lm_type[0] = LMS_SHA256_N32_H25; + lm_ots_type[0] = LMOTS_SHA256_N32_W1; + break; + case 18: + lm_type[0] = LMS_SHA256_N32_H25; + lm_ots_type[0] = LMOTS_SHA256_N32_W2; + break; + case 19: + lm_type[0] = LMS_SHA256_N32_H25; + lm_ots_type[0] = LMOTS_SHA256_N32_W4; + break; + case 20: + lm_type[0] = LMS_SHA256_N32_H25; + lm_ots_type[0] = LMOTS_SHA256_N32_W8; + break; + default: + lm_type[0] = 0; + lm_ots_type[0] = 0; + parse_err = 1; + break; + } + + if (parse_err) return -1; + + + /* + * This creates a private key (and the correspond public key, and optionally + * the aux data for that key) + * Parameters: + * generate_random - the function to be called to generate randomness. This + * is assumed to be a pointer to a cryptographically secure rng, + * otherwise all security is lost. This function is expected to fill + * output with 'length' uniformly distributed bits, and return 1 on + * success, 0 if something went wrong + * levels - the number of levels for the key pair (2-8) + * lm_type - an array of the LM registry entries for the various levels; + * entry 0 is the topmost + * lm_ots_type - an array of the LM-OTS registry entries for the various + * levels; again, entry 0 is the topmost + * update_private_key, context - the function that is called when the + * private key is generated; it is expected to store it to secure NVRAM + * If this is NULL, then the context pointer is reinterpretted to mean + * where in RAM the private key is expected to be placed + * public_key - where to store the public key + * len_public_key - length of the above buffer; see hss_get_public_key_len + * if you need a hint. + * aux_data - where to store the optional aux data. This is not required, but + * if provided, can be used to speed up the hss_generate_working_key + * process; + * len_aux_data - the length of the above buffer. This is not fixed length; + * the function will run different time/memory trade-offs based on the + * length provided + * + * This returns true on success, false on failure + */ + b_ret = hss_generate_private_key( + LMS_randombytes, + levels, + lm_type, + lm_ots_type, + NULL, //File handler function? + (void*)sk->secret_key, + public_key, len_public_key, + aux_data, len_aux_data, + NULL); + if (b_ret) + memcpy(pk, public_key, len_public_key); // Set lms param set // gen key pair - // store key pair + // store key pair, file handler + // Store aux data in oqs key struc, new struct to combine aux data and length + ret = 0; return ret; } @@ -125,26 +322,99 @@ int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, uint8_t *sm, unsigned long long *smlen, const uint8_t *m, unsigned long long mlen) { - LMS_UNUSED(sk); - LMS_UNUSED(sm); - LMS_UNUSED(smlen); - LMS_UNUSED(m); - LMS_UNUSED(mlen); - - return -1; + size_t sig_len; + bool status; + unsigned char *sig = NULL; + struct hss_working_key *w = NULL; + struct hss_sign_inc ctx; + w = hss_load_private_key(NULL, sk->secret_key, + 0, + NULL, + 0, + 0); + if (!w) { + printf( "Error loading private key\n" ); + hss_free_working_key(w); + return 0; + } + + /* Now, go through the file list, and generate the signatures for each */ + + /* Look up the signature length */ + + sig_len = hss_get_signature_len_from_working_key(w); + if (sig_len == 0) { + printf( "Error getting signature len\n" ); + hss_free_working_key(w); + return 0; + } + + sig = malloc(sig_len); + if (!sig) { + printf( "Error during malloc\n" ); + hss_free_working_key(w); + } + + (void)hss_sign_init( + &ctx, /* Incremental signing context */ + w, /* Working key */ + NULL, /* Routine to update the */ + sk->secret_key, /* private key */ + sig, sig_len, /* Where to place the signature */ + 0); + + (void)hss_sign_update( + &ctx, /* Incremental signing context */ + m, /* Next piece of the message */ + mlen); /* Length of this piece */ + + status = hss_sign_finalize( + &ctx, /* Incremental signing context */ + w, /* Working key */ + sig, /* Signature */ + 0); + + if (!status) { + hss_free_working_key(w); + free(sig); + return -1; + } + + *smlen = sig_len; + memcpy(sm, sig, sig_len); + + return 0; } -int oqs_sig_stfl_lms_verify(uint8_t *m, unsigned long long *mlen, - const uint8_t *sm, unsigned long long smlen, +int oqs_sig_stfl_lms_verify(const uint8_t *m, size_t mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { - LMS_UNUSED(m); - LMS_UNUSED(mlen); - LMS_UNUSED(sm); - LMS_UNUSED(smlen); - LMS_UNUSED(pk); - - return -1; + struct hss_validate_inc ctx; + (void)hss_validate_signature_init( + &ctx, /* Incremental validate context */ + (const unsigned char *)pk, /* Public key */ + (const unsigned char *)sm, + (size_t)smlen, /* Signature */ + 0); /* Use the defaults for extra info */ + + (void)hss_validate_signature_update( + &ctx, /* Incremental validate context */ + (const void*) m, /* Next piece of the message */ + (size_t)mlen); /* Length of this piece */ + + bool status = hss_validate_signature_finalize( + &ctx, /* Incremental validate context */ + (const unsigned char *)sm, /* Signature */ + 0); /* Use the defaults for extra info */ + + if (status) { + /* Signature verified */ + return 0; + } else { + /* signature NOT verified */ + return -1; + } } int oqs_sig_stfl_hss_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid) From c2cf884b18c4e1e61573b87a28b92c6598c54cdc Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Tue, 18 Apr 2023 21:02:54 -0400 Subject: [PATCH 05/13] Save auxiliary data when keypair is generated --- src/sig_stateful/lms/CMakeLists.txt | 2 +- src/sig_stateful/lms/sig_stfl_lms.c | 37 ++++- src/sig_stateful/lms/sig_stfl_lms.h | 61 +-------- src/sig_stateful/lms/sig_stfl_lms_functions.c | 129 +++++++++--------- src/sig_stateful/lms/sig_stfl_lms_wrap.h | 61 +++++++++ src/sig_stateful/sig_stfl.h | 7 + tests/test_sig_stfl_mem.c | 13 ++ 7 files changed, 181 insertions(+), 129 deletions(-) create mode 100644 src/sig_stateful/lms/sig_stfl_lms_wrap.h diff --git a/src/sig_stateful/lms/CMakeLists.txt b/src/sig_stateful/lms/CMakeLists.txt index e7131a9e7d..1dc8dfbeff 100644 --- a/src/sig_stateful/lms/CMakeLists.txt +++ b/src/sig_stateful/lms/CMakeLists.txt @@ -14,7 +14,7 @@ set(SRCS external/hss_reserve.c external/hss_sign.c external/hss_sign_inc.c - external/hss_thread_single.c + external/hss_thread_pthread.c external/hss_verify.c external/hss_verify_inc.c external/hss_zeroize.c diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c index 1e9d3d1bb8..9f3b1f6746 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.c +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -3,9 +3,44 @@ #include #include #include "./external/config.h" +#include "sig_stfl_lms_wrap.h" #include "sig_stfl_lms.h" +static OQS_STATUS OQS_SIG_STFL_alg_lms_aux_data(OQS_SECRET_KEY *secret_key, uint8_t **aux_data, size_t *aux_len) { + size_t aux_data_len = 0; + uint8_t *buffer = NULL; + unsigned char *lms_aux_data = NULL; + if (secret_key == NULL || aux_data == NULL || aux_len == NULL) { + return OQS_ERROR; + } + *aux_len = 0; + *aux_data = NULL; + + if (secret_key->data) { + aux_data_len = ((oqs_lms_key_data*)secret_key->data)->len_aux_data; + if (aux_data_len == 0) { + goto done; + } + lms_aux_data = ((oqs_lms_key_data*)secret_key->data)->aux_data; + if(!lms_aux_data) { + goto done; + } + + buffer = malloc(aux_data_len); + if (!buffer) { + goto done; + } + + memset(buffer, 0, aux_data_len); + memcpy(buffer, lms_aux_data, aux_data_len); + + *aux_len = aux_data_len; + *aux_data = (void*)buffer; + } +done: + return OQS_SUCCESS; +} // ======================== LMS-SHA256 H5/W1 ======================== // @@ -15,7 +50,6 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key } uint32_t oid = 0x00000001; - LMS_UNUSED(oid); if (oqs_sig_stfl_lms_keypair(public_key, secret_key, oid) != 0) { return OQS_ERROR; } @@ -59,6 +93,7 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { // Assign the sigs_left and sigs_max functions sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); diff --git a/src/sig_stateful/lms/sig_stfl_lms.h b/src/sig_stateful/lms/sig_stfl_lms.h index 05672dd0c0..4fe884099c 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.h +++ b/src/sig_stateful/lms/sig_stfl_lms.h @@ -1,14 +1,9 @@ // SPDX-License-Identifier: MIT -#ifndef OQS_SIG_STFL_LMS_LMSMT_H -#define OQS_SIG_STFL_LMS_LMSMT_H +#ifndef OQS_SIG_STFL_LMS_H +#define OQS_SIG_STFL_LMS_H #include -#include "external/hss.h" -#include "external/hss_sign_inc.h" -#define LMS_OID_LEN 4 - -//#ifdef OQS_ENABLE_SIG_STFL_LMS //H5 #define OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_signature 8688 @@ -236,56 +231,4 @@ OQS_SECRET_KEY *OQS_SIG_STFL_alg_lms_derive_subkey(OQS_SECRET_KEY *master_key, c // -------------------------------------------------------------------------------------------------------- - -/** - * @brief OQS_LMS_KEY object for HSS key pair - */ -typedef struct OQS_LMS_KEY_DATA oqs_lms_key_data; - -typedef struct OQS_LMS_KEY_DATA { - - /* Tree levels. */ - unsigned levels; - - /* Array, 8 levels max, of LMS types */ - param_set_t lm_type[8]; - - /* Array, 8 levels max, of LM OTS types */ - param_set_t lm_ots_type[8]; - - /* LMS public key */ - unsigned char public_key[60]; - - /* internal nodes info of the Merkle tree */ - unsigned char *aux_data; - - /* Length of aux data */ - size_t len_aux_data; - - /* User defined data that may be used for the SAFETY functions */ - void *data; - -} oqs_lms_key_data; - - -typedef struct OQS_LMS_SIG_DATA oqs_lms_sig_data; - -typedef struct OQS_LMS_SIG_DATA { - - - /* message buffer */ - unsigned char *message; - - /* Length of msg buffer */ - size_t len_msg_buf; - - /* signature buffer */ - unsigned char *signature; - - /* Length of sig buffer */ - size_t len_sig_buf; - -} oqs_lms_sig_data; - -//#endif //OQS_ENABLE_SIG_STFL_LMS #endif /* OQS_SIG_STFL_LMS_H */ diff --git a/src/sig_stateful/lms/sig_stfl_lms_functions.c b/src/sig_stateful/lms/sig_stfl_lms_functions.c index f53086a334..700deee4bb 100644 --- a/src/sig_stateful/lms/sig_stfl_lms_functions.c +++ b/src/sig_stateful/lms/sig_stfl_lms_functions.c @@ -3,21 +3,29 @@ #include #include "sig_stfl_lms.h" #include "external/config.h" -#include "external/hss.h" #include "external/hss_verify_inc.h" +#include "external/hss_sign_inc.h" +#include "external/hss.h" +#include "sig_stfl_lms_wrap.h" #include OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { - if (secret_key == NULL || message == NULL || signature == NULL) { - return OQS_ERROR; - } + if (secret_key == NULL || message == NULL || signature == NULL) { + return OQS_ERROR; + } - if (oqs_sig_stfl_lms_sign(secret_key, signature, - (unsigned long long *)signature_length, - message, (unsigned long long) message_len) !=0) { - return OQS_ERROR; - } - return OQS_SUCCESS; + /* Make sure we have a way to update the private key */ + if (!secret_key->save_secret_key) { + return OQS_ERROR; + } + if (oqs_sig_stfl_lms_sign(secret_key, signature, + (unsigned long long *)signature_length, + message, (unsigned long long) message_len) !=0) { + return OQS_ERROR; + } + + /* Update private key */ + return(secret_key->save_secret_key(secret_key)); } @@ -26,44 +34,33 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_verify(const uint8_t *message, size_t me return OQS_ERROR; } - LMS_UNUSED(signature_len); - LMS_UNUSED(message_len); - if (oqs_sig_stfl_lms_verify(message, message_len, signature, signature_len, public_key) !=0 ) { return OQS_ERROR; } - // if (oqs_sig_stfl_lms_verify(secret_key, signature, - // (unsigned long long *)signature_length, - // message, (unsigned long long) message_len) !=0) { - // return OQS_ERROR; - // } return OQS_SUCCESS; - // return OQS_ERROR; } OQS_API OQS_STATUS OQS_SIG_STFL_alg_hss_sign(uint8_t *signature, size_t *signature_length, const uint8_t *message, size_t message_len, OQS_SECRET_KEY *secret_key) { - if (secret_key == NULL || message == NULL || signature == NULL) { + if (secret_key == NULL || message == NULL || signature == NULL + || signature_length == NULL || message_len == 0) { return OQS_ERROR; } - LMS_UNUSED(signature_length); - LMS_UNUSED(message_len); - - +// Currently Unsupported // return OQS_SUCCESS; return OQS_ERROR; } OQS_API OQS_STATUS OQS_SIG_STFL_alg_hss_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { - if (message == NULL || signature == NULL || public_key == NULL) { + if (message == NULL || signature == NULL || public_key == NULL || + signature_len == 0 || message_len == 0 ) { return OQS_ERROR; } - LMS_UNUSED(signature_len); - LMS_UNUSED(message_len); +// Currently Unsupported // return OQS_SUCCESS; return OQS_ERROR; } @@ -73,8 +70,6 @@ unsigned long long OQS_SECRET_KEY_lms_sigs_left(const OQS_SECRET_KEY *secret_key return -1; } - LMS_UNUSED(secret_key); - return 0; } @@ -121,42 +116,12 @@ OQS_SECRET_KEY *OQS_SIG_STFL_alg_hss_derive_subkey(OQS_SECRET_KEY *master_key, c * identify the parameter set to be used */ -bool LMS_randombytes(void *buffer, size_t length); bool LMS_randombytes(void *buffer, size_t length) { OQS_randombytes((uint8_t *)buffer, length); return true; } -/* - * This saves the private key to secure storage; in this case, a file on the - * filesystem. The context pointer we use here is the filename - */ -/*static*/ bool oqs_update_private_key( unsigned char *private_key, - size_t len_private_key, void *filename) { - FILE *f = fopen( filename, "r+" ); - if (!f) { - /* Open failed, possibly because the file didn't exist */ - f = fopen( filename, "w" ); - if (!f) { - /* Unable to open file */ - return false; - } - } - if (1 != fwrite( private_key, len_private_key, 1, f )) { - /* Write failed */ - fclose(f); - return false; - } - if (0 != fclose(f)) { - /* Close failed (possibly because pending write failed) */ - return false; - } - - /* Everything succeeded */ - return true; -} - int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid) { int ret = -1; @@ -164,16 +129,19 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid int parse_err = 0; unsigned levels = 1; - unsigned char public_key[60];// = NULL; + unsigned char public_key[60]; size_t len_public_key = 60; - unsigned char aux_data[10000]; - size_t len_aux_data = 10000; + unsigned char *aux_data = NULL; + size_t max_aux_data = 10916; + int aux_len; + oqs_lms_key_data *oqs_data = NULL; param_set_t lm_type[1]; param_set_t lm_ots_type[1]; if (!pk || !sk || !oid) return -1; + /* Set lms param set */ switch (oid) { case 1: @@ -265,6 +233,20 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid if (parse_err) return -1; + /* gen key pair */ + + aux_len = (int)hss_get_aux_data_len( max_aux_data, levels, + lm_type, lm_ots_type); + printf( "aux_len = %d\n", aux_len ); + + if (aux_len) { + aux_data = malloc(aux_len); + if (aux_data){ + memset(aux_data, 0, aux_len); + } else { + aux_len = 0; + } + } /* * This creates a private key (and the correspond public key, and optionally @@ -304,17 +286,27 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid NULL, //File handler function? (void*)sk->secret_key, public_key, len_public_key, - aux_data, len_aux_data, + aux_data, aux_len, NULL); if (b_ret) memcpy(pk, public_key, len_public_key); - // Set lms param set - // gen key pair - // store key pair, file handler - // Store aux data in oqs key struc, new struct to combine aux data and length + oqs_data = malloc(sizeof(oqs_lms_key_data)); + if (oqs_data) { + memset(oqs_data, 0, sizeof(oqs_lms_key_data)); + oqs_data->levels = levels; + oqs_data->lm_type[0] = lm_type[0]; + oqs_data->lm_ots_type[0] = lm_ots_type[0]; + oqs_data->data = (void*)aux_data; + oqs_data->len_aux_data = aux_len; + sk->data = oqs_data; + } - ret = 0; + /* store key pair, file handler */ + if (sk->save_secret_key) { + (void)sk->save_secret_key(sk); + } + ret = 0; return ret; } @@ -451,3 +443,4 @@ int oqs_sig_stfl_hss_verify(uint8_t *m, unsigned long long *mlen, return -1; } + diff --git a/src/sig_stateful/lms/sig_stfl_lms_wrap.h b/src/sig_stateful/lms/sig_stfl_lms_wrap.h new file mode 100644 index 0000000000..445da13aa4 --- /dev/null +++ b/src/sig_stateful/lms/sig_stfl_lms_wrap.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +#ifndef OQS_SIG_STFL_LMS_WRAP_H +#define OQS_SIG_STFL_LMS_WRAP_H + +//#include +#include "external/hss.h" +#include "external/hss_sign_inc.h" + + +/** + * @brief OQS_LMS_KEY object for HSS key pair + */ +typedef struct OQS_LMS_KEY_DATA oqs_lms_key_data; + +typedef struct OQS_LMS_KEY_DATA { + + /* Tree levels. */ + unsigned levels; + + /* Array, 8 levels max, of LMS types */ + param_set_t lm_type[8]; + + /* Array, 8 levels max, of LM OTS types */ + param_set_t lm_ots_type[8]; + + /* LMS public key */ + unsigned char public_key[60]; + + /* internal nodes info of the Merkle tree */ + unsigned char *aux_data; + + /* Length of aux data */ + size_t len_aux_data; + + /* User defined data that may be used for the SAFETY functions */ + void *data; + +} oqs_lms_key_data; + + +typedef struct OQS_LMS_SIG_DATA oqs_lms_sig_data; + +typedef struct OQS_LMS_SIG_DATA { + + + /* message buffer */ + unsigned char *message; + + /* Length of msg buffer */ + size_t len_msg_buf; + + /* signature buffer */ + unsigned char *signature; + + /* Length of sig buffer */ + size_t len_sig_buf; + +} oqs_lms_sig_data; + +#endif //OQS_SIG_STFL_LMS_H diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index 4d79f38562..afe45a30ef 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -197,6 +197,13 @@ typedef struct OQS_SECRET_KEY { */ OQS_STATUS (*release_key)(OQS_SECRET_KEY *sk); + /** + * Secret Key data / parse data if present + * + * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @return void pointer to stored data + */ + OQS_STATUS (*get_key_data)(OQS_SECRET_KEY *sk, uint8_t **data, size_t *data_len); } OQS_SECRET_KEY; /** diff --git a/tests/test_sig_stfl_mem.c b/tests/test_sig_stfl_mem.c index 4af2b636df..28f0752d66 100644 --- a/tests/test_sig_stfl_mem.c +++ b/tests/test_sig_stfl_mem.c @@ -42,6 +42,8 @@ static OQS_STATUS sig_test_correctness(const char *method_name, SIG_OPS op) { OQS_SIG_STFL *sig = NULL; uint8_t *public_key = NULL; OQS_SECRET_KEY *secret_key = NULL; + uint8_t *secret_key_data = NULL; + size_t aux_data_len = 0; uint8_t *message = NULL; size_t message_len = 100; uint8_t *signature = NULL; @@ -82,6 +84,17 @@ static OQS_STATUS sig_test_correctness(const char *method_name, SIG_OPS op) { if (oqs_fstore("sk", method_name, secret_key->secret_key, secret_key->length_secret_key) != OQS_SUCCESS) { goto err; } + + if(secret_key->get_key_data) { + rc = secret_key->get_key_data(secret_key, &secret_key_data, &aux_data_len); + if (rc == OQS_SUCCESS && aux_data_len != 0) { + if (oqs_fstore("aux", method_name, secret_key_data, aux_data_len) != OQS_SUCCESS) { + goto err; + } + free(secret_key_data); + } + } + ret = OQS_SUCCESS; goto cleanup; From c4602153a389453f0b3250e7eead56de4da74d33 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Tue, 18 Apr 2023 22:50:11 -0400 Subject: [PATCH 06/13] Update private key after sign and test --- src/sig_stateful/lms/sig_stfl_lms.c | 16 +++++ src/sig_stateful/lms/sig_stfl_lms_functions.c | 2 - src/sig_stateful/sig_stfl.h | 67 ++++++++++--------- tests/test_sig_stfl_mem.c | 8 +++ 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c index 9f3b1f6746..12e2ddb801 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.c +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT #include +#include #include #include "./external/config.h" #include "sig_stfl_lms_wrap.h" @@ -81,6 +82,10 @@ OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(void) { OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { + char name[] = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1; + char *method_name = NULL; + + // Initialize the secret key in the heap with adequate memory OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); if (sk == NULL) { @@ -88,6 +93,17 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { } memset(sk, 0, sizeof(OQS_SECRET_KEY)); + /* set method name */ + method_name = malloc(sizeof(name)); + if (method_name == NULL) { + OQS_SECRET_KEY_free(sk); + sk = NULL; + return NULL; + } + memset(method_name, 0, sizeof(name)); + snprintf(method_name, sizeof(name), "%s", name); + sk->method_name = method_name; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk; // Assign the sigs_left and sigs_max functions diff --git a/src/sig_stateful/lms/sig_stfl_lms_functions.c b/src/sig_stateful/lms/sig_stfl_lms_functions.c index 700deee4bb..8e87632fc9 100644 --- a/src/sig_stateful/lms/sig_stfl_lms_functions.c +++ b/src/sig_stateful/lms/sig_stfl_lms_functions.c @@ -26,7 +26,6 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sign(uint8_t *signature, size_t *signatu /* Update private key */ return(secret_key->save_secret_key(secret_key)); - } OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_verify(const uint8_t *message, size_t message_len, const uint8_t *signature, size_t signature_len, const uint8_t *public_key) { @@ -237,7 +236,6 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid aux_len = (int)hss_get_aux_data_len( max_aux_data, levels, lm_type, lm_ots_type); - printf( "aux_len = %d\n", aux_len ); if (aux_len) { aux_data = malloc(aux_len); diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index afe45a30ef..1bc0331b6c 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -158,44 +158,47 @@ typedef struct OQS_SECRET_KEY OQS_SECRET_KEY; typedef struct OQS_SECRET_KEY { - /* The (maximum) length, in bytes, of secret keys for this signature scheme. */ - size_t length_secret_key; + /* The secret key stored in memory as an array of bytes*/ + char *method_name; - /* The secret key stored in memory as an array of bytes*/ - uint8_t *secret_key; + /* The (maximum) length, in bytes, of secret keys for this signature scheme. */ + size_t length_secret_key; - /* User defined data that may be used for the SAFETY functions */ - void *data; + /* The secret key stored in memory as an array of bytes*/ + uint8_t *secret_key; - /* Function that returns the total number of signatures for the secret key */ - unsigned long long (*sigs_total)(const OQS_SECRET_KEY *secret_key); + /* User defined data that may be used for the SAFETY functions */ + void *data; - /* Function that returns the number of signatures left for the secret key */ - unsigned long long (*sigs_left)(const OQS_SECRET_KEY *secret_key); + /* Function that returns the total number of signatures for the secret key */ + unsigned long long (*sigs_total)(const OQS_SECRET_KEY *secret_key); - /** - * Secret Key Locking Function - * - * @param[in] sk The secret key represented as OQS_SECRET_KEY object - * @return OQS_SUCCESS or OQS_ERROR - */ - OQS_STATUS (*lock_key)(OQS_SECRET_KEY *sk); + /* Function that returns the number of signatures left for the secret key */ + unsigned long long (*sigs_left)(const OQS_SECRET_KEY *secret_key); - /** - * Secret Key Saving Function - * - * @param[in] sk The secret key represented as OQS_SECRET_KEY object - * @return OQS_SUCCESS or OQS_ERROR - */ - OQS_STATUS (*save_secret_key)(const OQS_SECRET_KEY *sk); + /** + * Secret Key Locking Function + * + * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @return OQS_SUCCESS or OQS_ERROR + */ + OQS_STATUS (*lock_key)(OQS_SECRET_KEY *sk); - /** - * Secret Key Unlocking / Releasing Function - * - * @param[in] sk The secret key represented as OQS_SECRET_KEY object - * @return OQS_SUCCESS or OQS_ERROR - */ - OQS_STATUS (*release_key)(OQS_SECRET_KEY *sk); + /** + * Secret Key Saving Function + * + * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @return OQS_SUCCESS or OQS_ERROR + */ + OQS_STATUS (*save_secret_key)(const OQS_SECRET_KEY *sk); + + /** + * Secret Key Unlocking / Releasing Function + * + * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @return OQS_SUCCESS or OQS_ERROR + */ + OQS_STATUS (*release_key)(OQS_SECRET_KEY *sk); /** * Secret Key data / parse data if present @@ -203,7 +206,7 @@ typedef struct OQS_SECRET_KEY { * @param[in] sk The secret key represented as OQS_SECRET_KEY object * @return void pointer to stored data */ - OQS_STATUS (*get_key_data)(OQS_SECRET_KEY *sk, uint8_t **data, size_t *data_len); + OQS_STATUS (*get_key_data)(OQS_SECRET_KEY *sk, uint8_t **data, size_t *data_len); } OQS_SECRET_KEY; /** diff --git a/tests/test_sig_stfl_mem.c b/tests/test_sig_stfl_mem.c index 28f0752d66..9a7a472c6c 100644 --- a/tests/test_sig_stfl_mem.c +++ b/tests/test_sig_stfl_mem.c @@ -34,6 +34,14 @@ OQS_STATUS release_sk_key(OQS_SECRET_KEY *sk) { } OQS_STATUS do_nothing_save(const OQS_SECRET_KEY *sk) { + + if (sk && sk->method_name) { + if (oqs_fstore("sk", sk->method_name, sk->secret_key, sk->length_secret_key) == OQS_SUCCESS) { + return OQS_SUCCESS; + } else { + return OQS_ERROR; + } + } return sk != NULL ? OQS_SUCCESS : OQS_ERROR; } From e391ecf1e0e2689abcc313766609f7abff08ce34 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Thu, 20 Apr 2023 10:41:18 -0400 Subject: [PATCH 07/13] Fix compiler warnings for some platforms. --- src/sig_stateful/lms/external/hss_alloc.c | 8 +++--- src/sig_stateful/lms/external/hss_generate.c | 15 +++++------ src/sig_stateful/lms/external/hss_keygen.c | 2 +- src/sig_stateful/lms/external/hss_sign.c | 26 +++++++++---------- src/sig_stateful/lms/external/lm_ots_sign.c | 2 +- src/sig_stateful/lms/sig_stfl_lms.c | 8 +----- src/sig_stateful/lms/sig_stfl_lms_functions.c | 3 ++- 7 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/sig_stateful/lms/external/hss_alloc.c b/src/sig_stateful/lms/external/hss_alloc.c index ea435ffa7f..6edbd42512 100644 --- a/src/sig_stateful/lms/external/hss_alloc.c +++ b/src/sig_stateful/lms/external/hss_alloc.c @@ -163,7 +163,7 @@ struct hss_working_key *allocate_working_key( /* Assign the memory target to a *signed* variable; signed so that it */ /* can take on negative values meaningfully (to account for cases where */ /* we are "overbudget") */ - signed long mem_target; + unsigned long mem_target; if (memory_target > LONG_MAX) { mem_target = LONG_MAX; } else { @@ -327,8 +327,8 @@ signed long initial_mem_target = mem_target; /* DEBUG HACK */ found_plenty_memory, /* We found something that fits within */ /* out budget */ } search_status = nothing_yet; - signed long best_mem = 0; - signed long best_levels = 0; + unsigned long best_mem = 0; + unsigned long best_levels = 0; unsigned best_j = 0; size_t best_stack_used = 0; unsigned j; @@ -361,7 +361,7 @@ signed long initial_mem_target = mem_target; /* DEBUG HACK */ /* This is a signed type so that the comparison works as */ /* expected if mem_target is negative */ size_t stack_used; - signed long mem = compute_level_memory_usage(i, j, + unsigned long mem = compute_level_memory_usage(i, j, level_height[i], hash_size[i], &subtree_levels[i], &stack_used ); /* # of sublevels this would have */ diff --git a/src/sig_stateful/lms/external/hss_generate.c b/src/sig_stateful/lms/external/hss_generate.c index e57570e6d2..d4a6a96cc6 100644 --- a/src/sig_stateful/lms/external/hss_generate.c +++ b/src/sig_stateful/lms/external/hss_generate.c @@ -109,7 +109,7 @@ static void hash_subtree( unsigned char *dest, for (; num_level > 1; num_level--) { unsigned i; merkle_index_t this_level_node_index = node_index << (num_level-1); - for (i = 0; i < (1<<(num_level-1)); i++) { + for (i = 0; i < ((unsigned)1<<(num_level-1)); i++) { hss_combine_internal_nodes( &hashes[ hash_size * i ], &hashes[ hash_size * (2*i) ], @@ -312,12 +312,11 @@ bool hss_generate_working_key( /* Initialize the current count for each level (from the bottom-up) */ sequence_t i; sequence_t count = current_count; - for (i = w->levels - 1; i >= 0 ; i--) { - struct merkle_level *tree = w->tree[i]; + for (i = w->levels; i >= 1 ; i--) { + struct merkle_level *tree = w->tree[i-1]; unsigned index = count & tree->max_index; count >>= tree->level; tree->current_index = index; - if (i == 0) break; // This is a single level tree } /* Initialize the I values */ @@ -378,8 +377,8 @@ bool hss_generate_working_key( /* Step through the levels, and for each Merkle tree, compile a list of */ /* the orders to initialize the bottoms of the subtrees that we'll need */ - for (i = w->levels - 1; i >= 0 ; i--) { - struct merkle_level *tree = w->tree[i]; + for (i = w->levels; i >= 1 ; i--) { + struct merkle_level *tree = w->tree[i-1]; unsigned hash_size = tree->hash_size; /* The current count within this tree */ merkle_index_t tree_count = tree->current_index; @@ -501,7 +500,7 @@ bool hss_generate_working_key( } /* And the NEXT_TREE (which is always left-aligned) */ - if (i > 0) { + if ((i-1) > 0) { struct subtree *next = tree->subtree[j][NEXT_TREE]; next->left_leaf = 0; merkle_index_t leaf_size = @@ -512,7 +511,7 @@ bool hss_generate_working_key( /* update process will miss the very first update before we */ /* need to sign. To account for that, potetially generate */ /* one more node than what our current count would suggest */ - if (i != w->levels - 1) { + if ((i-1) != w->levels - 1) { next_index++; } diff --git a/src/sig_stateful/lms/external/hss_keygen.c b/src/sig_stateful/lms/external/hss_keygen.c index e47025a56b..cad193f511 100644 --- a/src/sig_stateful/lms/external/hss_keygen.c +++ b/src/sig_stateful/lms/external/hss_keygen.c @@ -182,7 +182,7 @@ bool hss_generate_private_key( /* If going to a higher levels would mean that we wouldn't */ /* effectively use all the cores we have, use this level */ - if ((1<levels; struct merkle_level *tree; - for (i = w->levels-1; i>=0; i--, merkle_levels_below += tree->level) { - tree = w->tree[i]; + for (i = w->levels; i>=1; i--, merkle_levels_below += tree->level) { + tree = w->tree[i-1]; if (0 == (cur_count & (((sequence_t)1 << (merkle_levels_below + tree->level))-1))) { /* We exhausted this tree */ - if (i == 0) { + if ((i-1) == 0) { /* We've run out of signatures; we've already caught this */ /* above; just make *sure* we've marked the key as */ /* unusable, and give up */ @@ -607,7 +607,7 @@ bool hss_generate_signature( } /* Remember we'll need to switch to the NEXT_TREE */ - switch_merkle = i; + switch_merkle = i-1; continue; } @@ -639,23 +639,23 @@ bool hss_generate_signature( /* Check if we used up any Merkle trees; if we have, switch to the */ /* NEXT_TREE (which we've built in our spare time) */ for (i = switch_merkle; i < w->levels; i++) { - struct merkle_level *tree = w->tree[i]; + struct merkle_level *tree_l = w->tree[i]; struct merkle_level *parent = w->tree[i-1]; unsigned j; /* Rearrange the subtrees */ for (j=0; jsublevels; j++) { /* Make the NEXT_TREE active; replace it with the current active */ - struct subtree *active = tree->subtree[j][NEXT_TREE]; - struct subtree *next = tree->subtree[j][ACTIVE_TREE]; + struct subtree *active = tree_l->subtree[j][NEXT_TREE]; + struct subtree *next = tree_l->subtree[j][ACTIVE_TREE]; unsigned char *stack = active->stack; /* Stack stays with */ /* next tree */ active->left_leaf = 0; next->current_index = 0; next->left_leaf = 0; - tree->subtree[j][ACTIVE_TREE] = active; - tree->subtree[j][NEXT_TREE] = next; + tree_l->subtree[j][ACTIVE_TREE] = active; + tree_l->subtree[j][NEXT_TREE] = next; active->stack = NULL; next->stack = stack; if (j > 0) { @@ -669,8 +669,8 @@ bool hss_generate_signature( } /* Copy in the value of seed, I we'll use for the new tree */ - memcpy( tree->seed, tree->seed_next, SEED_LEN ); - memcpy( tree->I, tree->I_next, I_LEN ); + memcpy( tree_l->seed, tree->seed_next, SEED_LEN ); + memcpy( tree_l->I, tree->I_next, I_LEN ); /* Compute the new next I, which is derived from either the parent's */ /* I or the parent's I_next value */ @@ -687,11 +687,11 @@ bool hss_generate_signature( parent->lm_ots_type); } - tree->current_index = 0; /* We're starting this from scratch */ + tree_l->current_index = 0; /* We're starting this from scratch */ /* Generate the signature of the new level */ if (!hss_create_signed_public_key( w->signed_pk[i], w->siglen[i-1], - tree, parent, w )) { + tree_l, parent, w )) { info->error_code = hss_error_internal; goto failed; } diff --git a/src/sig_stateful/lms/external/lm_ots_sign.c b/src/sig_stateful/lms/external/lm_ots_sign.c index 15decfcced..ee8f56b0a2 100644 --- a/src/sig_stateful/lms/external/lm_ots_sign.c +++ b/src/sig_stateful/lms/external/lm_ots_sign.c @@ -55,7 +55,7 @@ bool lm_ots_generate_public_key( hss_seed_derive( buf + ITER_PREV, seed, i < p-1 ); put_bigendian( buf + ITER_K, i, 2 ); /* We'll place j in the buffer below */ - for (j=0; j < (1<method_name = method_name; + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1; sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk; // Assign the sigs_left and sigs_max functions diff --git a/src/sig_stateful/lms/sig_stfl_lms_functions.c b/src/sig_stateful/lms/sig_stfl_lms_functions.c index 8e87632fc9..122db59639 100644 --- a/src/sig_stateful/lms/sig_stfl_lms_functions.c +++ b/src/sig_stateful/lms/sig_stfl_lms_functions.c @@ -295,7 +295,7 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid oqs_data->levels = levels; oqs_data->lm_type[0] = lm_type[0]; oqs_data->lm_ots_type[0] = lm_ots_type[0]; - oqs_data->data = (void*)aux_data; + oqs_data->aux_data = (void*)aux_data; oqs_data->len_aux_data = aux_len; sk->data = oqs_data; } @@ -372,6 +372,7 @@ int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, *smlen = sig_len; memcpy(sm, sig, sig_len); + free(sig); return 0; } From f22c6bd61542a1e99101bff36ca5ca7dcf8550f9 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Thu, 20 Apr 2023 10:47:12 -0400 Subject: [PATCH 08/13] Fix build error --- src/sig_stateful/lms/sig_stfl_lms.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c index 3909ae1027..487d63a3b4 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.c +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -90,13 +90,6 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { memset(sk, 0, sizeof(OQS_SECRET_KEY)); /* set method name */ - method_name = malloc(sizeof(name)); - if (method_name == NULL) { - OQS_SECRET_KEY_free(sk); - sk = NULL; - return NULL; - } - sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1; sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk; From f0f3448bdd12374b720748e5148a903bc24468d3 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Thu, 20 Apr 2023 10:53:15 -0400 Subject: [PATCH 09/13] Fix warning. --- src/sig_stateful/sig_stfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index 1bc0331b6c..bc95543603 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -159,7 +159,7 @@ typedef struct OQS_SECRET_KEY OQS_SECRET_KEY; typedef struct OQS_SECRET_KEY { /* The secret key stored in memory as an array of bytes*/ - char *method_name; + const char *method_name; /* The (maximum) length, in bytes, of secret keys for this signature scheme. */ size_t length_secret_key; From b1e4434421ab334d733b6927374d8ba07a6ea9c0 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Thu, 20 Apr 2023 12:11:05 -0400 Subject: [PATCH 10/13] Address temp build failure --- src/sig_stateful/lms/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sig_stateful/lms/CMakeLists.txt b/src/sig_stateful/lms/CMakeLists.txt index 1dc8dfbeff..e7131a9e7d 100644 --- a/src/sig_stateful/lms/CMakeLists.txt +++ b/src/sig_stateful/lms/CMakeLists.txt @@ -14,7 +14,7 @@ set(SRCS external/hss_reserve.c external/hss_sign.c external/hss_sign_inc.c - external/hss_thread_pthread.c + external/hss_thread_single.c external/hss_verify.c external/hss_verify_inc.c external/hss_zeroize.c From b3eee7518a1571d086111d0984c096528787d652 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Thu, 20 Apr 2023 18:33:45 -0400 Subject: [PATCH 11/13] Fix XMSS crash --- src/sig_stateful/xmss/sig_stfl_xmss_xmssmt_functions.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sig_stateful/xmss/sig_stfl_xmss_xmssmt_functions.c b/src/sig_stateful/xmss/sig_stfl_xmss_xmssmt_functions.c index af55221432..5445d5e547 100644 --- a/src/sig_stateful/xmss/sig_stfl_xmss_xmssmt_functions.c +++ b/src/sig_stateful/xmss/sig_stfl_xmss_xmssmt_functions.c @@ -13,9 +13,11 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_xmss_sign(uint8_t *signature, size_t *signat return OQS_ERROR; } - if (xmss_sign(secret_key, signature, (unsigned long long *)signature_length, message, (unsigned long long)message_len) != 0) { + unsigned long long sig_len = 0; + if (xmss_sign(secret_key, signature, &sig_len, message, (unsigned long long)message_len) != 0) { return OQS_ERROR; } + *signature_length = (size_t)sig_len; return OQS_SUCCESS; } From 91906e8bfee03ad852be8e6644cb708736bf80d8 Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Fri, 21 Apr 2023 02:02:15 -0400 Subject: [PATCH 12/13] Refactor to support all LMS types. --- src/sig_stateful/lms/sig_stfl_lms.c | 964 +++++++++++++++++- src/sig_stateful/lms/sig_stfl_lms.h | 84 +- src/sig_stateful/lms/sig_stfl_lms_functions.c | 56 +- src/sig_stateful/sig_stfl.c | 91 +- src/sig_stateful/sig_stfl.h | 6 + tests/test_sig_stfl.c | 91 +- 6 files changed, 1193 insertions(+), 99 deletions(-) diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c index 487d63a3b4..0781fac732 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.c +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -45,13 +45,15 @@ static OQS_STATUS OQS_SIG_STFL_alg_lms_aux_data(OQS_SECRET_KEY *secret_key, uint // ======================== LMS-SHA256 H5/W1 ======================== // -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { if (secret_key == NULL || public_key == NULL) { return OQS_ERROR; } + if (secret_key->oid == 0) { + return OQS_ERROR; + } - uint32_t oid = 0x00000001; - if (oqs_sig_stfl_lms_keypair(public_key, secret_key, oid) != 0) { + if (oqs_sig_stfl_lms_keypair(public_key, secret_key, secret_key->oid) != 0) { return OQS_ERROR; } return OQS_SUCCESS; @@ -65,14 +67,15 @@ OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(void) { } memset(sig, 0, sizeof(OQS_SIG_STFL)); - sig->method_name = "LMS-SHA2_H5_W1"; + sig->oid = 0x00000001; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1; sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; sig->euf_cma = true; sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_pk; sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_signature; - sig->keypair = OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair; + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; sig->sign = OQS_SIG_STFL_alg_lms_sign; sig->verify = OQS_SIG_STFL_alg_lms_verify; sig->derive_subkey = NULL; @@ -89,6 +92,7 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { } memset(sk, 0, sizeof(OQS_SECRET_KEY)); + sk->oid = 0x00000001; /* set method name */ sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1; sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk; @@ -105,6 +109,956 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { return sk; } +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w2_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000002; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W2_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000002; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w4_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000003; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W4_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000003; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w8_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000004; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W8_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000004; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w1_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000005; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W1_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000005; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w2_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000006; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W2_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000006; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w4_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000007; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W4_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000007; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w8_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000008; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W8_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000008; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w1_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000009; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W1_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000009; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w2_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x0000000a; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W2_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x0000000a; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w4_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x0000000b; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W4_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x0000000b; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w8_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x0000000c; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W8_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x0000000c; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w1_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x0000000d; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W1_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x0000000d; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w2_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x0000000e; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W2_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x0000000e; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w4_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x0000000f; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W4_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x0000000f; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w8_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000010; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W8_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000010; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w1_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000011; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W1_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000011; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w2_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000012; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W2_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000012; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w4_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000013; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W4_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000013; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + +OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w8_new(void) { + + OQS_SIG_STFL *sig = (OQS_SIG_STFL *)malloc(sizeof(OQS_SIG_STFL)); + if (sig == NULL) { + return NULL; + } + memset(sig, 0, sizeof(OQS_SIG_STFL)); + + sig->oid = 0x00000014; + sig->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8; + sig->alg_version = "https://datatracker.ietf.org/doc/html/rfc8554"; + sig->euf_cma = true; + + sig->length_public_key = OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_pk; + sig->length_signature = OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_signature; + + sig->keypair = OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair; + sig->sign = OQS_SIG_STFL_alg_lms_sign; + sig->verify = OQS_SIG_STFL_alg_lms_verify; + sig->derive_subkey = NULL; + + return sig; +} + +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W8_new(void) { + + // Initialize the secret key in the heap with adequate memory + OQS_SECRET_KEY *sk = malloc(sizeof(OQS_SECRET_KEY)); + if (sk == NULL) { + return NULL; + } + memset(sk, 0, sizeof(OQS_SECRET_KEY)); + + sk->oid = 0x00000014; + /* set method name */ + sk->method_name = OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8; + sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_sk; + + // Assign the sigs_left and sigs_max functions + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + + // Initialize the key with length_secret_key amount of bytes. + sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); + memset(sk->secret_key, 0, sk->length_secret_key); + + return sk; +} + // ================================================================ // diff --git a/src/sig_stateful/lms/sig_stfl_lms.h b/src/sig_stateful/lms/sig_stfl_lms.h index 4fe884099c..8ede256f05 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.h +++ b/src/sig_stateful/lms/sig_stfl_lms.h @@ -10,37 +10,31 @@ #define OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void); +OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_signature 4464 #define OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_w2_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W2_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w2_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_signature 2352 #define OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_w4_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W4_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w4_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_signature 1296 #define OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_w8_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W8_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h5_w8_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); //H10 // H10 W1 60 8848 64 @@ -51,37 +45,29 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h5_w8_keypair(uint8_t *public_key #define OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w1_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W1_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w1_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_signature 4624 #define OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w2_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W2_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w2_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_signature 2512 #define OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w4_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W4_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w4_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_signature 1456 #define OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_w8_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W8_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h10_w8_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); //H15 // H15 W1 60 9008 64 @@ -92,37 +78,29 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h10_w8_keypair(uint8_t *public_ke #define OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w1_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W1_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w1_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_signature 4784 #define OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w2_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W2_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w2_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_signature 2672 #define OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w4_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W4_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w4_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_signature 1616 #define OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_w8_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W8_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h15_w8_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); //H20 // H20 W1 60 9168 64 @@ -133,37 +111,29 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h15_w8_keypair(uint8_t *public_ke #define OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w1_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W1_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w1_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_signature 4944 #define OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w2_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W2_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w2_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_signature 2832 #define OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w4_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W4_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w4_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_signature 1776 #define OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_w8_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W8_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h20_w8_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); //H25 // H25 W1 60 9328 64 @@ -174,44 +144,36 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h20_w8_keypair(uint8_t *public_ke #define OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w1_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W1_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w1_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w1_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_signature 5104 #define OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w2_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W2_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w2_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w2_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_signature 2992 #define OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w4_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W4_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w4_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w4_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); #define OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_signature 1936 #define OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_pk 60 #define OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_sk 64 -OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_w8_new(void); - +OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W8_new(void); OQS_SIG_STFL *OQS_SIG_STFL_alg_lms_sha256_h25_w8_new(void); -OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_h25_w8_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key); // ----------------------------------- WRAPPER FUNCTIONS ------------------------------------------------ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid); -int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, uint8_t *sm, unsigned long long *smlen, - const uint8_t *m, unsigned long long mlen); +int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen); int oqs_sig_stfl_lms_verify(const uint8_t *m, size_t mlen, const uint8_t *sm, size_t smlen, const uint8_t *pk); diff --git a/src/sig_stateful/lms/sig_stfl_lms_functions.c b/src/sig_stateful/lms/sig_stfl_lms_functions.c index 122db59639..6f2208caf1 100644 --- a/src/sig_stateful/lms/sig_stfl_lms_functions.c +++ b/src/sig_stateful/lms/sig_stfl_lms_functions.c @@ -19,8 +19,8 @@ OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sign(uint8_t *signature, size_t *signatu return OQS_ERROR; } if (oqs_sig_stfl_lms_sign(secret_key, signature, - (unsigned long long *)signature_length, - message, (unsigned long long) message_len) !=0) { + signature_length, + message, message_len) !=0) { return OQS_ERROR; } @@ -143,83 +143,83 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid /* Set lms param set */ switch (oid) { - case 1: + case 0x1: lm_type[0] = LMS_SHA256_N32_H5; lm_ots_type[0] = LMOTS_SHA256_N32_W1; break; - case 2: + case 0x2: lm_type[0] = LMS_SHA256_N32_H5; lm_ots_type[0] = LMOTS_SHA256_N32_W2; break; - case 3: + case 0x3: lm_type[0] = LMS_SHA256_N32_H5; lm_ots_type[0] = LMOTS_SHA256_N32_W4; break; - case 4: + case 0x4: lm_type[0] = LMS_SHA256_N32_H5; lm_ots_type[0] = LMOTS_SHA256_N32_W8; break; - case 5: + case 0x5: lm_type[0] = LMS_SHA256_N32_H10; lm_ots_type[0] = LMOTS_SHA256_N32_W1; break; - case 6: + case 0x6: lm_type[0] = LMS_SHA256_N32_H10; lm_ots_type[0] = LMOTS_SHA256_N32_W2; break; - case 7: + case 0x7: lm_type[0] = LMS_SHA256_N32_H10; lm_ots_type[0] = LMOTS_SHA256_N32_W4; break; - case 8: + case 0x8: lm_type[0] = LMS_SHA256_N32_H10; lm_ots_type[0] = LMOTS_SHA256_N32_W8; break; - case 9: + case 0x9: lm_type[0] = LMS_SHA256_N32_H15; lm_ots_type[0] = LMOTS_SHA256_N32_W1; break; - case 10: + case 0xa: lm_type[0] = LMS_SHA256_N32_H15; lm_ots_type[0] = LMOTS_SHA256_N32_W2; break; - case 11: + case 0xb: lm_type[0] = LMS_SHA256_N32_H15; lm_ots_type[0] = LMOTS_SHA256_N32_W4; break; - case 12: + case 0xc: lm_type[0] = LMS_SHA256_N32_H15; lm_ots_type[0] = LMOTS_SHA256_N32_W8; break; - case 13: + case 0xd: lm_type[0] = LMS_SHA256_N32_H20; lm_ots_type[0] = LMOTS_SHA256_N32_W1; break; - case 14: + case 0xe: lm_type[0] = LMS_SHA256_N32_H20; lm_ots_type[0] = LMOTS_SHA256_N32_W2; break; - case 15: + case 0xf: lm_type[0] = LMS_SHA256_N32_H20; lm_ots_type[0] = LMOTS_SHA256_N32_W4; break; - case 16: + case 0x10: lm_type[0] = LMS_SHA256_N32_H20; lm_ots_type[0] = LMOTS_SHA256_N32_W8; break; - case 17: + case 0x11: lm_type[0] = LMS_SHA256_N32_H25; lm_ots_type[0] = LMOTS_SHA256_N32_W1; break; - case 18: + case 0x12: lm_type[0] = LMS_SHA256_N32_H25; lm_ots_type[0] = LMOTS_SHA256_N32_W2; break; - case 19: + case 0x13: lm_type[0] = LMS_SHA256_N32_H25; lm_ots_type[0] = LMOTS_SHA256_N32_W4; break; - case 20: + case 0x14: lm_type[0] = LMS_SHA256_N32_H25; lm_ots_type[0] = LMOTS_SHA256_N32_W8; break; @@ -309,8 +309,8 @@ int oqs_sig_stfl_lms_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid } int oqs_sig_stfl_lms_sign(OQS_SECRET_KEY *sk, - uint8_t *sm, unsigned long long *smlen, - const uint8_t *m, unsigned long long mlen) + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen) { size_t sig_len; bool status; @@ -418,8 +418,8 @@ int oqs_sig_stfl_hss_keypair(uint8_t *pk, OQS_SECRET_KEY *sk, const uint32_t oid } int oqs_sig_stfl_hss_sign(OQS_SECRET_KEY *sk, - uint8_t *sm, unsigned long long *smlen, - const uint8_t *m, unsigned long long mlen) + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen) { LMS_UNUSED(sk); LMS_UNUSED(sm); @@ -430,8 +430,8 @@ int oqs_sig_stfl_hss_sign(OQS_SECRET_KEY *sk, return -1; } -int oqs_sig_stfl_hss_verify(uint8_t *m, unsigned long long *mlen, - const uint8_t *sm, unsigned long long smlen, +int oqs_sig_stfl_hss_verify(uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { LMS_UNUSED(m); diff --git a/src/sig_stateful/sig_stfl.c b/src/sig_stateful/sig_stfl.c index 71bdafd5fe..3d7d259ae5 100644 --- a/src/sig_stateful/sig_stfl.c +++ b/src/sig_stateful/sig_stfl.c @@ -207,7 +207,7 @@ OQS_API int OQS_SIG_STFL_alg_is_enabled(const char *method_name) { return 1; } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { return 1; - } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8)) { return 1; } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1)) { return 1; @@ -382,7 +382,48 @@ OQS_API OQS_SIG_STFL *OQS_SIG_STFL_new(const char *method_name) { #ifdef OQS_ENABLE_SIG_STFL_LMS } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { return OQS_SIG_STFL_alg_lms_sha256_h5_w1_new(); - + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2)) { + return OQS_SIG_STFL_alg_lms_sha256_h5_w2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { + return OQS_SIG_STFL_alg_lms_sha256_h5_w4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8)) { + return OQS_SIG_STFL_alg_lms_sha256_h5_w8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1)) { + return OQS_SIG_STFL_alg_lms_sha256_h10_w1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2)) { + return OQS_SIG_STFL_alg_lms_sha256_h10_w2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4)) { + return OQS_SIG_STFL_alg_lms_sha256_h10_w4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8)) { + return OQS_SIG_STFL_alg_lms_sha256_h10_w8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1)) { + return OQS_SIG_STFL_alg_lms_sha256_h15_w1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2)) { + return OQS_SIG_STFL_alg_lms_sha256_h15_w2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4)) { + return OQS_SIG_STFL_alg_lms_sha256_h15_w4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8)) { + return OQS_SIG_STFL_alg_lms_sha256_h15_w8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1)) { + return OQS_SIG_STFL_alg_lms_sha256_h20_w1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2)) { + return OQS_SIG_STFL_alg_lms_sha256_h20_w2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4)) { + return OQS_SIG_STFL_alg_lms_sha256_h20_w4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8)) { + return OQS_SIG_STFL_alg_lms_sha256_h20_w8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1)) { + return OQS_SIG_STFL_alg_lms_sha256_h25_w1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2)) { + return OQS_SIG_STFL_alg_lms_sha256_h25_w2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4)) { + return OQS_SIG_STFL_alg_lms_sha256_h25_w4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8)) { + return OQS_SIG_STFL_alg_lms_sha256_h25_w8_new(); +#endif //OQS_ENABLE_SIG_STFL_LMS +#ifdef OQS_ENABLE_SIG_STFL_HSS + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_hss_sha256)) { + return NULL; #endif } else { return NULL; @@ -557,8 +598,50 @@ OQS_API OQS_SECRET_KEY *OQS_SECRET_KEY_new(const char *method_name) { #endif #ifdef OQS_ENABLE_SIG_STFL_LMS - } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { - return OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1)) { + return OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w2)) { + return OQS_SECRET_KEY_LMS_SHA256_H5_W2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w4)) { + return OQS_SECRET_KEY_LMS_SHA256_H5_W4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h5_w8)) { + return OQS_SECRET_KEY_LMS_SHA256_H5_W8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w1)) { + return OQS_SECRET_KEY_LMS_SHA256_H10_W1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w2)) { + return OQS_SECRET_KEY_LMS_SHA256_H10_W2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w4)) { + return OQS_SECRET_KEY_LMS_SHA256_H10_W4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h10_w8)) { + return OQS_SECRET_KEY_LMS_SHA256_H10_W8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w1)) { + return OQS_SECRET_KEY_LMS_SHA256_H15_W1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w2)) { + return OQS_SECRET_KEY_LMS_SHA256_H15_W2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w4)) { + return OQS_SECRET_KEY_LMS_SHA256_H15_W4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h15_w8)) { + return OQS_SECRET_KEY_LMS_SHA256_H15_W8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w1)) { + return OQS_SECRET_KEY_LMS_SHA256_H20_W1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w2)) { + return OQS_SECRET_KEY_LMS_SHA256_H20_W2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w4)) { + return OQS_SECRET_KEY_LMS_SHA256_H20_W4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h20_w8)) { + return OQS_SECRET_KEY_LMS_SHA256_H20_W8_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w1)) { + return OQS_SECRET_KEY_LMS_SHA256_H25_W1_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w2)) { + return OQS_SECRET_KEY_LMS_SHA256_H25_W2_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w4)) { + return OQS_SECRET_KEY_LMS_SHA256_H25_W4_new(); + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_lms_sha256_n32_h25_w8)) { + return OQS_SECRET_KEY_LMS_SHA256_H25_W8_new(); +#endif //OQS_ENABLE_SIG_STFL_LMS +#ifdef OQS_ENABLE_SIG_STFL_HSS + } else if (0 == strcasecmp(method_name, OQS_SIG_STFL_alg_hss_sha256)) { + return NULL; #endif } else { diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index bc95543603..6a8e2c3a93 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -158,6 +158,9 @@ typedef struct OQS_SECRET_KEY OQS_SECRET_KEY; typedef struct OQS_SECRET_KEY { + /** A local ordinal representing the LMS parameter of the signature scheme. */ + uint32_t oid; + /* The secret key stored in memory as an array of bytes*/ const char *method_name; @@ -233,6 +236,9 @@ OQS_API void OQS_SECRET_KEY_free(OQS_SECRET_KEY *sk); */ typedef struct OQS_SIG_STFL { + /** A local ordinal representing the LMS parameter of the signature scheme. */ + uint32_t oid; + /** Printable string representing the name of the signature scheme. */ const char *method_name; diff --git a/tests/test_sig_stfl.c b/tests/test_sig_stfl.c index a3e17d3c33..8616e81f3d 100644 --- a/tests/test_sig_stfl.c +++ b/tests/test_sig_stfl.c @@ -120,11 +120,100 @@ static OQS_STATUS sig_stfl_test_correctness(void) { return rc; } +static OQS_STATUS sig_stfl_lms_test_correctness(void) { + + OQS_SIG_STFL *sig = NULL; + uint8_t *public_key = NULL; + OQS_SECRET_KEY *secret_key = NULL; + + uint8_t message[MESSAGE_LEN] = {0}; + + uint8_t *signature = NULL; + size_t signature_len = 0; + + OQS_STATUS rc; + + const char *alg_name = OQS_SIG_STFL_alg_lms_sha256_n32_h5_w1; + + sig = OQS_SIG_STFL_new(alg_name); + if (sig == NULL) { + fprintf(stderr, "ERROR: LMS OQS_SIG_STFL_new failed\n"); + goto error; + } + + secret_key = OQS_SECRET_KEY_new(alg_name); + if (secret_key == NULL) { + fprintf(stderr, "ERROR: LMS OQS_SECRET_KEY_new failed\n"); + goto error; + } + secret_key->lock_key = &lock_sk_key; + secret_key->release_key = &release_sk_key; + secret_key->save_secret_key = &do_nothing_save; + + public_key = malloc(sig->length_public_key); + if (public_key == NULL) { + fprintf(stderr, "ERROR: LMS malloc failed!\n"); + goto error; + } + + signature = malloc(sig->length_signature); + if (signature == NULL) { + fprintf(stderr, "ERROR: LMS malloc failed!\n"); + goto error; + } + + rc = OQS_SIG_STFL_keypair(sig, public_key, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: LMS OQS_SIG_STFL_keypair failed\n"); + goto error; + } + + OQS_randombytes(message, MESSAGE_LEN); + + rc = OQS_SIG_STFL_sign(sig, signature, &signature_len, message, MESSAGE_LEN, secret_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: OQS_SIG_STFL_sign failed\n"); + goto error; + } + + rc = OQS_SIG_STFL_verify(sig, message, MESSAGE_LEN, signature, signature_len, public_key); + if (rc != OQS_SUCCESS) { + fprintf(stderr, "ERROR: Signature verification error.\n"); + goto error; + } + + printf("SUCCESS!\n"); + rc = OQS_SUCCESS; + goto cleanup; + +error: + rc = OQS_ERROR; + +cleanup: + if (public_key) { + free(public_key); + } + if (signature) { + free(signature); + } + if (sig) { + OQS_SIG_STFL_free(sig); + } + if(secret_key) { + OQS_SECRET_KEY_free(secret_key); + } + + return rc; +} + int main(void) { OQS_STATUS rc = sig_stfl_test_correctness(); if (rc != OQS_SUCCESS) { return EXIT_FAILURE; } + rc = sig_stfl_lms_test_correctness(); + if (rc != OQS_SUCCESS) { + return EXIT_FAILURE; + } return EXIT_SUCCESS; } - From 48172bde2e283cabad519e313273f97a9b5d193e Mon Sep 17 00:00:00 2001 From: Norman Ashley Date: Fri, 21 Apr 2023 17:57:52 -0400 Subject: [PATCH 13/13] Clean up memory allocated for aux lms data --- src/sig_stateful/lms/sig_stfl_lms.c | 178 ++++++++++++++++++---------- src/sig_stateful/lms/sig_stfl_lms.h | 1 - src/sig_stateful/sig_stfl.c | 5 + src/sig_stateful/sig_stfl.h | 20 +++- 4 files changed, 142 insertions(+), 62 deletions(-) diff --git a/src/sig_stateful/lms/sig_stfl_lms.c b/src/sig_stateful/lms/sig_stfl_lms.c index 0781fac732..44bf5289a6 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.c +++ b/src/sig_stateful/lms/sig_stfl_lms.c @@ -43,6 +43,23 @@ static OQS_STATUS OQS_SIG_STFL_alg_lms_aux_data(OQS_SECRET_KEY *secret_key, uint return OQS_SUCCESS; } + +static void OQS_SIG_STFL_alg_lms_set_aux_data(OQS_SECRET_KEY *sk, void *data) { + sk->data = data; +} + +static void OQS_SIG_STFL_alg_lms_free_aux_data(OQS_SECRET_KEY *sk) { + if (!sk || !sk->data) { + return; + } + oqs_lms_key_data *key_data = NULL; + key_data = (oqs_lms_key_data*)sk->data; + OQS_MEM_secure_free(key_data->aux_data, key_data->len_aux_data); + key_data->aux_data = NULL; + key_data->len_aux_data = 0; + sk->data = NULL; +} + // ======================== LMS-SHA256 H5/W1 ======================== // OQS_API OQS_STATUS OQS_SIG_STFL_alg_lms_sha256_hx_wx_keypair(uint8_t *public_key, OQS_SECRET_KEY *secret_key) { @@ -98,9 +115,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W1_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w1_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -148,9 +167,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W2_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w2_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -198,9 +219,12 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W4_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w4_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; + // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -248,9 +272,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H5_W8_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h5_w8_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -298,9 +324,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W1_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w1_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -348,9 +376,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W2_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w2_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -398,9 +428,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W4_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w4_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -448,9 +480,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H10_W8_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h10_w8_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -498,9 +532,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W1_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w1_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -548,9 +584,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W2_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w2_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -598,9 +636,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W4_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w4_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -648,9 +688,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H15_W8_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h15_w8_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -698,9 +740,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W1_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w1_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -748,9 +792,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W2_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w2_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -798,9 +844,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W4_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w4_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -848,9 +896,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H20_W8_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h20_w8_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -898,9 +948,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W1_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w1_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -948,9 +1000,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W2_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w2_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -998,9 +1052,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W4_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w4_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); @@ -1048,9 +1104,11 @@ OQS_SECRET_KEY *OQS_SECRET_KEY_LMS_SHA256_H25_W8_new(void) { sk->length_secret_key = OQS_SIG_STFL_alg_lms_sha256_h25_w8_length_sk; // Assign the sigs_left and sigs_max functions - sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; - sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; - sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->sigs_left = OQS_SECRET_KEY_lms_sigs_left; + sk->sigs_total = OQS_SECRET_KEY_lms_sigs_total; + sk->get_key_data = OQS_SIG_STFL_alg_lms_aux_data; + sk->free_key_data = OQS_SIG_STFL_alg_lms_free_aux_data; + sk->set_key_data = OQS_SIG_STFL_alg_lms_set_aux_data; // Initialize the key with length_secret_key amount of bytes. sk->secret_key = (uint8_t *)malloc(sk->length_secret_key * sizeof(uint8_t)); diff --git a/src/sig_stateful/lms/sig_stfl_lms.h b/src/sig_stateful/lms/sig_stfl_lms.h index 8ede256f05..fe885f8f26 100644 --- a/src/sig_stateful/lms/sig_stfl_lms.h +++ b/src/sig_stateful/lms/sig_stfl_lms.h @@ -188,7 +188,6 @@ unsigned long long OQS_SECRET_KEY_lms_sigs_left(const OQS_SECRET_KEY *secret_key unsigned long long OQS_SECRET_KEY_lms_sigs_total(const OQS_SECRET_KEY *secret_key); - OQS_SECRET_KEY *OQS_SIG_STFL_alg_lms_derive_subkey(OQS_SECRET_KEY *master_key, const unsigned long long number_of_sigs); // -------------------------------------------------------------------------------------------------------- diff --git a/src/sig_stateful/sig_stfl.c b/src/sig_stateful/sig_stfl.c index 3d7d259ae5..4f40a5eeeb 100644 --- a/src/sig_stateful/sig_stfl.c +++ b/src/sig_stateful/sig_stfl.c @@ -653,5 +653,10 @@ OQS_API OQS_SECRET_KEY *OQS_SECRET_KEY_new(const char *method_name) { OQS_API void OQS_SECRET_KEY_free(OQS_SECRET_KEY *sk) { if (sk == NULL) return; OQS_MEM_secure_free(sk->secret_key, sk->length_secret_key); + + /* Call object specif free */ + if (sk->free_key_data) { + sk->free_key_data(sk); + } OQS_MEM_secure_free(sk, sizeof(sk)); } diff --git a/src/sig_stateful/sig_stfl.h b/src/sig_stateful/sig_stfl.h index 6a8e2c3a93..98dc65af5a 100644 --- a/src/sig_stateful/sig_stfl.h +++ b/src/sig_stateful/sig_stfl.h @@ -204,12 +204,30 @@ typedef struct OQS_SECRET_KEY { OQS_STATUS (*release_key)(OQS_SECRET_KEY *sk); /** - * Secret Key data / parse data if present + * Set secret Key data * * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @param[in] data void pointer to stored data + * @return none + */ + void (*set_key_data)(OQS_SECRET_KEY *sk, void *data); + + /** + * Get secret Key data pointer if present + * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @return OQS_SUCCESS or OQS_ERROR * @return void pointer to stored data + * @return data length */ OQS_STATUS (*get_key_data)(OQS_SECRET_KEY *sk, uint8_t **data, size_t *data_len); + + /** + * Secret Key free internal data + * + * @param[in] sk The secret key represented as OQS_SECRET_KEY object + * @return none + */ + void (*free_key_data)(OQS_SECRET_KEY *sk); } OQS_SECRET_KEY; /**