From 31df0b23556b119e91ca0193c77298cd71680ab4 Mon Sep 17 00:00:00 2001 From: Frans van Dorsselaer <17404029+dorssel@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:43:44 +0200 Subject: [PATCH] Release 2.0.0 --- .reuse/dep5 | 2 +- CMakeLists.txt | 29 +- README.md | 3 +- include/CMakeLists.txt | 5 + include/compat.h | 10 +- include/errors.h | 50 ++ include/generic_digest.h | 20 +- include/opaque_structures.h | 26 +- include/override_sha256_generic.h | 6 +- include/override_sha256_internal.h | 5 +- include/override_shake256_256_generic.h | 6 +- include/override_shake256_256_internal.h | 44 +- include/signing.h | 478 +++++++------ include/structures.h | 138 ++-- include/types.h | 85 ++- include/verification.h | 96 ++- include/version.h | 158 +++++ include/xmss_config.in.h | 43 +- src/CMakeLists.txt | 207 ++---- src/compat_stdatomic.h | 8 +- src/config.in.h | 24 + src/endianness.h | 12 +- src/errors.c | 97 +++ src/fault_detection_helpers.h | 66 ++ src/generic_xmss_hashes.h | 171 +++-- src/index_permutation.c | 22 +- src/index_permutation.h | 11 +- src/libxmss.c | 79 +++ src/libxmss.def | 35 + src/libxmss.h | 45 ++ src/libxmss_no_signing.def | 13 + src/rand_hash.c | 50 ++ src/rand_hash.h | 40 ++ src/sha256_generic_xmss_hashes.h | 91 ++- src/sha256_internal_H0.c | 36 + src/sha256_internal_H0.h | 54 ++ src/sha256_internal_default.c | 27 +- src/sha256_internal_xmss_hashes.c | 176 +++-- src/sha256_internal_xmss_hashes.h | 172 +++-- src/sha256_xmss_hashes.c | 26 +- src/sha256_xmss_hashes.h | 13 + src/shake256_256_generic_xmss_hashes.h | 99 ++- src/shake256_256_internal_default.c | 68 +- src/shake256_256_internal_xmss_hashes.c | 221 +++--- src/shake256_256_internal_xmss_hashes.h | 76 +- src/shake256_256_xmss_hashes.c | 25 +- src/shake256_256_xmss_hashes.h | 13 + src/signing.c | 656 +++++++----------- src/{private.h => signing_private.h} | 125 ++-- src/utils.c | 88 ++- src/utils.h | 75 +- src/verification.c | 369 +++++++--- src/version.c | 19 + src/version.in.rc | 78 +++ src/wotsp.c | 308 +------- src/wotsp.h | 108 +-- src/wotsp_private.h | 104 +++ src/wotsp_signing.c | 211 ++++++ src/wotsp_signing.h | 76 ++ src/wotsp_verification.c | 126 ++++ src/wotsp_verification.h | 57 ++ src/xmss_hashes.c | 9 +- src/xmss_hashes.h | 277 +++++--- src/xmss_hashes_base.h | 200 ++++-- src/xmss_ltree.c | 57 ++ src/xmss_ltree.h | 43 ++ src/xmss_tree.c | 109 +-- src/xmss_tree.h | 43 +- src/zeroize.c | 75 ++ src/zeroize.h | 35 + tests/CMakeLists.txt | 196 +++--- tests/cmake/test_static_assert.c | 2 +- tests/nist-test-vectors.c | 2 +- tests/reference-digest.inc | 81 +++ tests/sha256-kat.c | 14 +- tests/shake256_256-kat.c | 13 +- tests/test-aarch64-override_sha256_internal.c | 80 +++ tests/test-blob-confusion.c | 2 +- tests/test-chain.c | 28 +- tests/test-completeness.c | 52 ++ tests/test-endianness.c | 8 +- tests/test-ltree.c | 27 +- tests/test-override_sha256_generic.c | 12 +- tests/test-override_sha256_internal.c | 30 + tests/test-override_shake256_256_generic.c | 12 +- tests/test-rand_hash.c | 32 +- tests/test-sha256_xmss_hashes.c | 14 +- tests/test-shake256_256_xmss_hashes.c | 14 +- tests/test-static_assert.c | 2 +- tests/test-structures.c | 2 +- tests/test-version.c | 32 + tests/test-wotsp.c | 15 +- tests/test-xmss_context_initialize.c | 49 +- tests/test-xmss_generate_private_key.c | 11 +- tests/test-xmss_generate_public_key.c | 25 +- tests/test-xmss_hashes.h | 24 - ...est-xmss_hashes.c => test-xmss_hashes.inc} | 152 ++-- tests/test-xmss_sign_verify.c | 177 +++++ tests/test-xmss_tree_hash.c | 19 +- tests/test-xmss_zeroize.c | 4 +- 100 files changed, 4948 insertions(+), 2612 deletions(-) create mode 100644 include/errors.h create mode 100644 include/version.h create mode 100644 src/errors.c create mode 100644 src/fault_detection_helpers.h create mode 100644 src/libxmss.c create mode 100644 src/libxmss.def create mode 100644 src/libxmss.h create mode 100644 src/libxmss_no_signing.def create mode 100644 src/rand_hash.c create mode 100644 src/rand_hash.h create mode 100644 src/sha256_internal_H0.c create mode 100644 src/sha256_internal_H0.h rename src/{private.h => signing_private.h} (84%) create mode 100644 src/version.c create mode 100644 src/version.in.rc create mode 100644 src/wotsp_private.h create mode 100644 src/wotsp_signing.c create mode 100644 src/wotsp_signing.h create mode 100644 src/wotsp_verification.c create mode 100644 src/wotsp_verification.h create mode 100644 src/xmss_ltree.c create mode 100644 src/xmss_ltree.h create mode 100644 src/zeroize.c create mode 100644 src/zeroize.h create mode 100644 tests/reference-digest.inc create mode 100644 tests/test-aarch64-override_sha256_internal.c create mode 100644 tests/test-completeness.c create mode 100644 tests/test-override_sha256_internal.c create mode 100644 tests/test-version.c delete mode 100644 tests/test-xmss_hashes.h rename tests/{test-xmss_hashes.c => test-xmss_hashes.inc} (85%) create mode 100644 tests/test-xmss_sign_verify.c diff --git a/.reuse/dep5 b/.reuse/dep5 index 7260352..fadb43b 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -1,6 +1,6 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: xmss-library -Source: https://github.com/foxcrypto/xmss-library +Source: https://github.com/FoxCryptoNL/xmss-library Files: docs/doxygen-awesome-css/* Copyright: 2021-2023 jothepro diff --git a/CMakeLists.txt b/CMakeLists.txt index 45cc94a..1a68692 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # CMake 3.22.1 is the version installed with Ubuntu 22.04.1. cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) -project(xmss-library VERSION 1.0.0 LANGUAGES C) +project(xmss-library VERSION 2.0.0 LANGUAGES C) # All compiler options are in a separate file include("cmake/compiler_options.cmake") @@ -47,6 +47,14 @@ else() endif() endif() +# When set to OFF (default), static libraries are built. +# When set to ON, shared libraries are built. +option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones." OFF) + +# When set to ON (default), both the full library (including signing) and a verification-only library are built. +# When set to OFF, just the verification-only library is built. +option(XMSS_ENABLE_SIGNING "Enable signing support in the library." ON) + # The C tests are part of the early validation of the compiler/platform combination with the XMSS library and must not # be disabled for normal testing or production builds. # The full test suite contains several tests that require hash overrides that will completely fail these tests, though. @@ -65,12 +73,24 @@ else() set(XMSS_ENABLE_SHA256 ON) endif() +if(XMSS_SHA256 STREQUAL "Default") + set(XMSS_ENABLE_SHA256_DEFAULT ON) +else() + set(XMSS_ENABLE_SHA256_DEFAULT OFF) +endif() + if(XMSS_SHAKE256_256 STREQUAL "Disabled") set(XMSS_ENABLE_SHAKE256_256 OFF) else() set(XMSS_ENABLE_SHAKE256_256 ON) endif() +if(XMSS_SHAKE256_256 STREQUAL "Default") + set(XMSS_ENABLE_SHAKE256_256_DEFAULT ON) +else() + set(XMSS_ENABLE_SHAKE256_256_DEFAULT OFF) +endif() + if(NOT XMSS_ENABLE_SHA256 AND NOT XMSS_ENABLE_SHAKE256_256) message(FATAL_ERROR "XMSS_SHA256 and XMSS_SHAKE256_256 cannot be both Disabled.") endif() @@ -139,7 +159,14 @@ try_compile(XMSS_CAN_USE_EXTENSION_STATIC_ASSERT ${CMAKE_CURRENT_BINARY_DIR} configure_file(include/xmss_config.in.h include/xmss_config.h NO_SOURCE_PERMISSIONS) configure_file(src/config.in.h src/config.h NO_SOURCE_PERMISSIONS) +add_library(config INTERFACE) +target_include_directories(config + INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include + INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/src +) + enable_testing() +set_property(GLOBAL PROPERTY USE_FOLDERS ON) add_subdirectory(include) add_subdirectory(src) diff --git a/README.md b/README.md index 639e50c..a27d650 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ eXtended Merkle Signature Scheme is a post-quantum safe signature algorithm ([RFC](https://datatracker.ietf.org/doc/html/rfc8391)). ![XMSS C Library License](https://img.shields.io/github/license/FoxCryptoNL/xmss-library?style=plastic) -[![XMSS C Library Release](https://img.shields.io/github/v/release/FoxCryptoNL/xmss-library?style=plastic)]( - https://github.com/FoxCryptoNL/xmss-library/releases) +[![XMSS C Library Release](https://img.shields.io/github/v/release/FoxCryptoNL/xmss-library?style=plastic)](https://github.com/FoxCryptoNL/xmss-library/releases) ## XMSS C Library Source Code diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 84162e2..87dce4b 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -3,6 +3,7 @@ set(XMSS_HEADER_FILES compat.h + errors.h generic_digest.h opaque_structures.h override_sha256_generic.h @@ -13,6 +14,7 @@ set(XMSS_HEADER_FILES structures.h types.h verification.h + version.h ${CMAKE_CURRENT_BINARY_DIR}/xmss_config.h ) @@ -41,6 +43,7 @@ int main(void) { return 0; } PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ) + set_property(TARGET _verify_${HEADER_FILENAME} PROPERTY FOLDER tests/verify_headers) # Note that building the executable is the real test. They're added as tests, anyway, to ensure that they're built # as part of the test cycle. add_test(NAME "Verify ${HEADER_FILENAME} is standalone" COMMAND "_verify_${HEADER_FILENAME}") @@ -50,3 +53,5 @@ add_library(xmss_headers INTERFACE ${XMSS_HEADER_FILES}) target_include_directories(xmss_headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) + +install(FILES ${XMSS_HEADER_FILES} DESTINATION include/xmss) diff --git a/include/compat.h b/include/compat.h index 3f2a6e2..c1d91a7 100644 --- a/include/compat.h +++ b/include/compat.h @@ -27,21 +27,21 @@ /** * @brief - * Syntactical equivalent of _Static_assert(), which is C11. + * Syntactical equivalent of `_Static_assert()`, which is C11. * * @details - * C11 supports _Static_assert(). CMake can detect if it is available anyway, even if the compiler is not C11. + * C11 supports `_Static_assert()`. CMake can detect if it is available anyway, even if the compiler is not C11. * If CMake detects that static asserts are not supported, then this macro is a no-op. * * @see XMSS_CAN_USE_STATIC_ASSERT * @see XMSS_CAN_USE_EXTENSION_STATIC_ASSERT */ #if (__STDC_VERSION__ >= 201112L) || XMSS_CAN_USE_STATIC_ASSERT || defined(DOXYGEN) -# define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) +# define XMSS_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) #elif XMSS_CAN_USE_EXTENSION_STATIC_ASSERT -# define STATIC_ASSERT(cond, msg) __extension__ _Static_assert(cond, msg) +# define XMSS_STATIC_ASSERT(cond, msg) __extension__ _Static_assert(cond, msg) #else -# define STATIC_ASSERT(cond, msg) struct xmss_static_assert_unsupported +# define XMSS_STATIC_ASSERT(cond, msg) struct xmss_static_assert_unsupported #endif #endif /* !XMSS_COMPAT_H_INCLUDED */ diff --git a/include/errors.h b/include/errors.h new file mode 100644 index 0000000..80a883d --- /dev/null +++ b/include/errors.h @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Thomas Schaap + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Public API for XMSS error handling. + */ + +#pragma once + +#ifndef XMSS_ERRORS_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_ERRORS_H_INCLUDED + +#include + + +/** + * Translate an XMSS error to the string with the enumeration-constant name. + * + * @details + * As an example, `xmss_error_to_name(XMSS_OKAY)` returns `"XMSS_OKAY"`. + * + * @remark + * This function returns `"XmssError_Undefined"` for values of `error` that are not defined in #XmssError. + * + * @param[in] error The error to translate. + * + * @returns A pointer to a static string with the enumeration-constant name corresponding to `error`. + */ +const char *xmss_error_to_name(XmssError error); + + +/** + * Translate an XMSS error to a human-readable message. + * + * @param[in] error The error to translate. + * + * @returns A pointer to a static string with a message corresponding to `error`. + */ +const char *xmss_error_to_description(XmssError error); + + +#endif /* !XMSS_ERRORS_H_INCLUDED */ diff --git a/include/generic_digest.h b/include/generic_digest.h index 7176ed6..c71cc7e 100644 --- a/include/generic_digest.h +++ b/include/generic_digest.h @@ -9,8 +9,8 @@ * @file * @brief Abstract typedefs for hash function overrides using the generic interface. * @details - * Do not include this file. Instead, either include `override_sha256_generic.h` or - * `override_shake256_256_generic.h`, depending on the specific algorithm you are overriding. + * Do not include this file. Instead, either include override_sha256_generic.h or + * override_shake256_256_generic.h, depending on the specific algorithm you are overriding. * * For each digest algorithm (SHA-256 and/or SHAKE256/256), the library allows to override its internal implementation. * The main use case is hardware acceleration. @@ -20,9 +20,9 @@ * * When supplying an override using the generic interface, you will have to implement 3 functions (per algorithm, that * you are overriding): - * - initialize - * - update - * - finalize + * - #XmssGenericDigestInit + * - #XmssGenericDigestUpdate + * - #XmssGenericDigestFinalize * * The library guarantees that the functions are called in the following order: * - exactly one call to the initialize function @@ -31,7 +31,7 @@ * * Per thread, there will be at most one digest in use at any one time. This implies that if you use the library single * threaded, then you could use a single statically allocated context. In that case the opaque `context` parameter does - * not necessarily have to be provided or used (i.e., it could simply be 0). + * not necessarily have to be provided or used (i.e., it could simply be NULL). * * **Error handling** * @@ -84,10 +84,10 @@ typedef void *(*XmssGenericDigestInit)(void); * @param[in] context An opaque context, i.e., the result of the most recent call to the initialization function on * this thread. * @param[in] data The byte stream of additional data to be included in the message; may be NULL if and only if - * data_length is zero. - * @param[in] data_length The number of bytes pointed to by data. + * `data_length` is zero. + * @param[in] data_length The number of bytes pointed to by `data`. */ -typedef void (*XmssGenericDigestUpdate)(void *restrict context, const uint8_t *restrict data, size_t data_length); +typedef void (*XmssGenericDigestUpdate)(void *context, const uint8_t *data, size_t data_length); /** * @brief @@ -98,6 +98,6 @@ typedef void (*XmssGenericDigestUpdate)(void *restrict context, const uint8_t *r * this thread. * @param[out] digest The output of the hash function. */ -typedef void (*XmssGenericDigestFinalize)(void *restrict context, XmssValue256 *restrict digest); +typedef void (*XmssGenericDigestFinalize)(void *context, XmssValue256 *digest); #endif /* !XMSS_GENERIC_DIGEST_H_INCLUDED */ diff --git a/include/opaque_structures.h b/include/opaque_structures.h index ead9096..290d5a1 100644 --- a/include/opaque_structures.h +++ b/include/opaque_structures.h @@ -30,7 +30,7 @@ * @details * The signing context defines the parameter set and the hash functions to use. * - * When creating an XmssSigningContext, the XMSS_SIGNING_CONTEXT_SIZE macro can be used to allocate the correct size. + * When creating an XmssSigningContext, the #XMSS_SIGNING_CONTEXT_SIZE macro can be used to allocate the correct size. * * XmssSigningContext is an opaque type, do not access its members. */ @@ -40,7 +40,7 @@ typedef struct XmssSigningContext XmssSigningContext; * @brief * The size in bytes of an XmssSigningContext. */ -#define XMSS_SIGNING_CONTEXT_SIZE (4u + 4u + 4u + 4u + 11u * sizeof(void(*)())) +#define XMSS_SIGNING_CONTEXT_SIZE (4u + 4u + 4u + 4u + 4u * sizeof(void(*)(void))) /** * @brief @@ -60,7 +60,7 @@ void xmss_free_signing_context(XmssSigningContext *signing_context); * In-memory representation of a loaded cache. * * @details - * When creating an XmssInternalCache, the XMSS_INTERNAL_CACHE_SIZE macro can be used to allocate the correct size. + * When creating an XmssInternalCache, the #XMSS_INTERNAL_CACHE_SIZE() macro can be used to allocate the correct size. * * XmssInternalCache is an opaque type, do not access its members. */ @@ -70,12 +70,12 @@ typedef struct XmssInternalCache XmssInternalCache; * @brief * The number of cached entries for a specific cache configuration. * - * @note The arguments to XMSS_CACHE_ENTRY_COUNT will be evaluated multiple times. + * @note The arguments to #XMSS_CACHE_ENTRY_COUNT() will be evaluated multiple times. * * @param[in] cache_type The cache type that is used. * @param[in] cache_level The cache level that is to be held. * @param[in] param_set The parameter set of the key for which the cache will be used. - * @see xmss_generate_public_key for more information about the cache type and level. + * @see xmss_generate_public_key() for more information about the cache type and level. */ #define XMSS_CACHE_ENTRY_COUNT(cache_type, cache_level, param_set) \ ((cache_type) == XMSS_CACHE_NONE ? 0u : \ @@ -92,12 +92,12 @@ typedef struct XmssInternalCache XmssInternalCache; * @brief * The size in bytes of an XmssInternalCache. * - * @note The arguments to XMSS_INTERNAL_CACHE_SIZE will be evaluated multiple times. + * @note The arguments to #XMSS_INTERNAL_CACHE_SIZE() will be evaluated multiple times. * * @param[in] cache_type The cache type that is used. * @param[in] cache_level The cache level that is to be held. * @param[in] param_set The parameter set of the key for which the cache will be used. - * @see xmss_generate_public_key for more information about the cache type and level. + * @see xmss_generate_public_key() for more information about the cache type and level. */ #define XMSS_INTERNAL_CACHE_SIZE(cache_type, cache_level, param_set) \ (4 + 4 + sizeof(XmssValue256) * XMSS_CACHE_ENTRY_COUNT((cache_type), (cache_level), (param_set))) @@ -116,7 +116,7 @@ typedef struct XmssInternalCache XmssInternalCache; * Context for using the signature generation part of the library, with a loaded private key partition. * * @details - * When creating an XmssKeyContext, the XMSS_KEY_CONTEXT_SIZE macro can be used to allocate the correct size. + * When creating an XmssKeyContext, the #XMSS_KEY_CONTEXT_SIZE macro can be used to allocate the correct size. * * XmssKeyContext is an opaque type, do not access its members. */ @@ -144,10 +144,10 @@ typedef struct XmssKeyContext XmssKeyContext; * @brief * The size in bytes of an XmssKeyContext. * - * @note The param_set argument will be evaluated multiple times. + * @note The `param_set` argument will be evaluated multiple times. * - * @param[in] param_set The XmssParameterSetOID that is to be used for the private key. - * @param[in] obfuscation_setting The XmssIndexObfuscationSetting that is to be used with the private key. + * @param[in] param_set The #XmssParameterSetOID that is to be used for the private key. + * @param[in] obfuscation_setting The #XmssIndexObfuscationSetting that is to be used with the private key. */ #define XMSS_KEY_CONTEXT_SIZE(param_set, obfuscation_setting) \ (4u + 4u + XMSS_SIGNING_CONTEXT_SIZE + XMSS_PRIVATE_KEY_STATELESS_PART_SIZE + \ @@ -170,7 +170,7 @@ void xmss_free_key_context(XmssKeyContext *key_context); * The temporary context to gather all the results of generating a public key. * * @details - * When creating an XmssKeyGenerationContext, the XMSS_KEY_GENERATION_CONTEXT_SIZE macro may be used to allocate the + * When creating an XmssKeyGenerationContext, the #XMSS_KEY_GENERATION_CONTEXT_SIZE macro may be used to allocate the * correct size. * * The elements of an XmssKeyGenerationContext are generally to be considered invalid outside of their specific use in @@ -185,7 +185,7 @@ typedef struct XmssKeyGenerationContext XmssKeyGenerationContext; * The size in bytes of XmssKeyGenerationContext. * * @param[in] generation_partitions The number of calculation partitions that will divide the work. - * @see xmss_generate_public_key for more information about generation_partitions + * @see xmss_generate_public_key() for more information about generation_partitions. */ #define XMSS_KEY_GENERATION_CONTEXT_SIZE(generation_partitions) \ (sizeof(void*) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(void*) + sizeof(void*) + \ diff --git a/include/override_sha256_generic.h b/include/override_sha256_generic.h index 8a9fa64..0498aab 100644 --- a/include/override_sha256_generic.h +++ b/include/override_sha256_generic.h @@ -37,7 +37,7 @@ * @details * This is the specialization for the SHA-256 algorithm. */ -void *sha256_init(void); +void *xmss_sha256_init(void); /** * @copydoc XmssGenericDigestUpdate @@ -45,7 +45,7 @@ void *sha256_init(void); * @details * This is the specialization for the SHA-256 algorithm. */ -void sha256_update(void *restrict context, const uint8_t *restrict data, size_t data_length); +void xmss_sha256_update(void *context, const uint8_t *data, size_t data_length); /** * @copydoc XmssGenericDigestFinalize @@ -53,6 +53,6 @@ void sha256_update(void *restrict context, const uint8_t *restrict data, size_t * @details * This is the specialization for the SHA-256 algorithm. */ -void sha256_finalize(void *restrict context, XmssValue256 *restrict digest); +void xmss_sha256_finalize(void *context, XmssValue256 *digest); #endif /* !XMSS_OVERRIDE_SHA256_GENERIC_H_INCLUDED */ diff --git a/include/override_sha256_internal.h b/include/override_sha256_internal.h index 0307129..325787c 100644 --- a/include/override_sha256_internal.h +++ b/include/override_sha256_internal.h @@ -52,9 +52,12 @@ * This function implements the loop body of the SHA-256($M$) function as defined by NIST FIPS 180-4, Section 6.2.2, * Steps 1, 2, 3, and 4. * + * For performance reasons, it is recommended not to validate the input. This function is guaranteed to be called by the + * library with valid input. + * * @param[in,out] Hi Intermediate hash value $H_i$ in native form. * @param[in] Mi Message block $M_i$ in native form (uint32_t[16] == 64 bytes). */ -void sha256_process_block(XmssNativeValue256 *restrict Hi, const uint32_t *restrict Mi); +void xmss_sha256_process_block(XmssNativeValue256 *Hi, const uint32_t *Mi); #endif /* !XMSS_OVERRIDE_SHA256_INTERNAL_H_INCLUDED */ diff --git a/include/override_shake256_256_generic.h b/include/override_shake256_256_generic.h index 0562aa0..bf8183b 100644 --- a/include/override_shake256_256_generic.h +++ b/include/override_shake256_256_generic.h @@ -37,7 +37,7 @@ * @details * This is the specialization for the SHAKE256/256 algorithm. */ -void * shake256_256_init(void); +void * xmss_shake256_256_init(void); /** * @copydoc XmssGenericDigestUpdate @@ -45,7 +45,7 @@ void * shake256_256_init(void); * @details * This is the specialization for the SHAKE256/256 algorithm. */ -void shake256_256_update(void *restrict context, const uint8_t *restrict data, size_t data_length); +void xmss_shake256_256_update(void *context, const uint8_t *data, size_t data_length); /** * @copydoc XmssGenericDigestFinalize @@ -53,6 +53,6 @@ void shake256_256_update(void *restrict context, const uint8_t *restrict data, s * @details * This is the specialization for the SHAKE256/256 algorithm. */ -void shake256_256_finalize(void *restrict context, XmssValue256 *restrict digest); +void xmss_shake256_256_finalize(void *context, XmssValue256 *digest); #endif /* !XMSS_OVERRIDE_SHAKE256_256_GENERIC_H_INCLUDED */ diff --git a/include/override_shake256_256_internal.h b/include/override_shake256_256_internal.h index 44c4a68..f10e632 100644 --- a/include/override_shake256_256_internal.h +++ b/include/override_shake256_256_internal.h @@ -56,16 +56,20 @@ * Absorbs additional bytes into the state array **A**. * * @details - * offset + byte_count does not extend beyond the block size of 136 bytes for SHAKE256/256. - * This function XORs the additional bytes into the internal layout of the state array. + * `offset` + `byte_count` does not extend beyond the block size of 136 bytes for SHAKE256/256. + * This function XORs the additional bytes into the internal layout of the state array `A`. + * + * This function is never called with empty data; i.e. `byte_count` >= 1. + * + * For performance reasons, it is recommended not to validate the input. This function is guaranteed to be called by the + * library with valid input. * * @param[in,out] A The state array. * @param[in] offset The offset into the 136-byte block where to start absorbing the bytes. - * @param[in] bytes Input bytes; may be NULL if and only if byte_count is 0. + * @param[in] bytes Input bytes. * @param[in] byte_count Length in bytes of the input. */ -void sponge_absorb(uint64_t *restrict A, uint_fast16_t offset, const uint8_t *restrict bytes, - uint_fast16_t byte_count); +void xmss_sponge_absorb(uint64_t *A, uint_fast8_t offset, const uint8_t *bytes, uint_fast8_t byte_count); /** * @brief @@ -73,32 +77,45 @@ void sponge_absorb(uint64_t *restrict A, uint_fast16_t offset, const uint8_t *re * * @details * Absorption starts at the beginning of the 136 bytes block for SHAKE256/256. - * word_count does not extend beyond the block size of 136 bytes for SHAKE256/256; i.e., word_count <= 34. - * This function XORs the words into the internal layout of the state array. + * `word_count` does not extend beyond the block size of 136 bytes for SHAKE256/256; i.e., `word_count` <= 34. + * This function XORs the words into the internal layout of the state array `A`. + * + * This function is never called with empty data; i.e. `word_count` >= 1. + * + * For performance reasons, it is recommended not to validate the input. This function is guaranteed to be called by the + * library with valid input. * * @param[in,out] A The state array. - * @param[in] words Input words; may be NULL if and only if byte_count is 0. + * @param[in] words Input words. * @param[in] word_count Length in 32-bit words of the input. */ -void sponge_absorb_native(uint64_t *restrict A, const uint32_t *restrict words, uint_fast16_t word_count); +void xmss_sponge_absorb_native(uint64_t *A, const uint32_t *words, uint_fast8_t word_count); /** * @brief * Extracts the digest from the state array **A**. * + * @details + * For performance reasons, it is recommended not to validate the input. This function is guaranteed to be called by the + * library with valid input. + * * @param[out] digest The output digest. * @param[in] A The final state array. */ -void sponge_squeeze(XmssValue256 *restrict digest, const uint64_t *restrict const A); +void xmss_sponge_squeeze(XmssValue256 *digest, const uint64_t *A); /** * @brief * Extracts the digest from the state array **A**. * + * @details + * For performance reasons, it is recommended not to validate the input. This function is guaranteed to be called by the + * library with valid input. + * * @param[out] native_digest The output digest. * @param[in] A The final state array. */ -void sponge_squeeze_native(XmssNativeValue256 *restrict native_digest, const uint64_t *restrict const A); +void xmss_sponge_squeeze_native(XmssNativeValue256 *native_digest, const uint64_t *A); /** * @brief @@ -112,8 +129,11 @@ void sponge_squeeze_native(XmssNativeValue256 *restrict native_digest, const uin * * Note that instead of returning the result, this function transforms the input state array in-place. * + * For performance reasons, it is recommended not to validate the input. This function is guaranteed to be called by the + * library with valid input. + * * @param[in,out] A The state array. */ -void keccak_p_1600_24(uint64_t *A); +void xmss_keccak_p_1600_24(uint64_t *A); #endif /* !XMSS_OVERRIDE_SHAKE256_256_INTERNAL_H_INCLUDED */ diff --git a/include/signing.h b/include/signing.h index 8ef7259..e9045ac 100644 --- a/include/signing.h +++ b/include/signing.h @@ -31,13 +31,13 @@ * ================= * * The library needs to allocate and deallocate memory during several calls. The user of the library must provide a - * realloc() and free() implementation. This allows, for example, an implementation that does not use any dynamic memory - * allocations. If dynamic memory allocation is available on the target platform, then one can simply pass the standard - * C library implementations. + * #XmssReallocFunction and #XmssFreeFunction implementation. This allows, for example, an implementation that does not + * use any dynamic memory allocations. If dynamic memory allocation is available on the target platform, then one can + * simply pass the standard C library implementations for `realloc()` and `free()`. * * Whenever a function in this library has a pointer-to-pointer-to-struct parameter, it will allocate memory for that - * structure using the provided pointer's initial value as input pointer to realloc(). For every - * pointer-to-pointer-to-struct parameter a single call to realloc() will be made. + * structure using the provided pointer's initial value as input pointer to the user-provided #XmssReallocFunction. For + * every pointer-to-pointer-to-struct parameter a single call to the user-provided #XmssReallocFunction will be made. * * ``` * XmssKeyContext *key_context = NULL; @@ -54,11 +54,12 @@ * promises to deallocate the memory, or by explicitly calling the correct deallocation function for the type. * * Deallocating memory is only done by library calls when explicitly mentioned, for example by - * xmss_generate_public_key(), and by the structure deallocation functions. If memory needs to be - * deallocated, free() will be called once with a non-NULL argument for every structure that needs to be deallocated. + * xmss_generate_public_key(), and by the structure deallocation functions. If memory needs to be deallocated, the + * user-provided #XmssFreeFunction will be called once with a non-NULL argument for every structure that needs to be + * deallocated. * - * Non-standard implementations for realloc() and free() can be used to create a library instantiation that only uses - * static memory allocations. + * Non-standard implementations for #XmssReallocFunction and #XmssFreeFunction can be used to create a library + * instantiation that only uses static memory allocations. * * ``` * void *static_realloc(void *ptr, size_t size) { return ptr; } @@ -82,17 +83,16 @@ * @param[in] custom_free The library will use this function to deallocate previously allocated memory. * @param[in] zeroize If this is not NULL, the library will use this function to erase sensitive data. * Special care must be taken to select a function that can not be optimized away by the - * compiler. No such function exists in pure C99, so the default implementation represents + * compiler. No such function exists in pure C99, but the default implementation offers * a best-effort solution that is known to work on almost all compilers. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER context, custom_realloc, or custom_free is NULL. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_INVALID_ARGUMENT An unsupported parameter set is specified. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER `context`, `custom_realloc`, or `custom_free` is NULL. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_INVALID_ARGUMENT An unsupported parameter set is specified. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_context_initialize( - XmssSigningContext *restrict *restrict context, XmssParameterSetOID parameter_set, +XmssError xmss_context_initialize(XmssSigningContext **context, XmssParameterSetOID parameter_set, XmssReallocFunction custom_realloc, XmssFreeFunction custom_free, XmssZeroizeFunction zeroize); /* === Key loading === */ @@ -113,9 +113,9 @@ XmssError xmss_context_initialize( * * This function is bit error resilient in that a singe random bit error cannot cause it to load an invalid private key, * a stateful part that does not correspond to the stateless part, or a private key with a parameter set that does not - * match context. + * match `context`. * - * @see xmss_partition_signature_space for more information on private key partitions. + * @see xmss_partition_signature_space() for more information on private key partitions. * * @param[in,out] key_context The context required for further use of the library with this key. * @param[in] private_key The stateless part of the private key. This must be a valid XmssPrivateKeyStatelessBlob. @@ -124,19 +124,18 @@ XmssError xmss_context_initialize( * @param[in] context The initialized context of the library. This must be initialized for the parameter set that was * used to create the private key. After xmss_load_private_key() successfully returns this * context may be deallocated: only key_context is needed for further use of the library. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ARGUMENT_MISMATCH private_key does not contain a private key that was generated for the parameter - * set for which context was initialized, or the data in private_key and - * key_usage is not part of the same private key. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_INVALID_BLOB The data in private_key or key_usage is not valid. - * @retval XMSS_ERR_BAD_CONTEXT context is not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ARGUMENT_MISMATCH `private_key` does not contain a private key that was generated for the + * parameter set for which context was initialized, or the data in `private_key` + * and `key_usage` is not part of the same private key. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_INVALID_BLOB The data in `private_key` or `key_usage` is not valid. + * @retval #XMSS_ERR_BAD_CONTEXT `context` is not a correctly initialized context. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_load_private_key( - XmssKeyContext **key_context, const XmssPrivateKeyStatelessBlob *private_key, +XmssError xmss_load_private_key(XmssKeyContext **key_context, const XmssPrivateKeyStatelessBlob *private_key, const XmssPrivateKeyStatefulBlob *key_usage, const XmssSigningContext *context); /** @@ -150,35 +149,34 @@ XmssError xmss_load_private_key( * * The public key also includes a previously generated cache, which greatly speeds up signature generation. * - * @see xmss_generate_public_key for more information about caching + * @see xmss_generate_public_key() for more information about caching. * * If public_key contains caching data, xmss_load_public_key() will load the cache structure in cache and add it to - * the context. If public_key does not contain any cache, cache's value will be changed to point to NULL. Static memory - * implementations that do not know the size of the cache up front can pass a preallocated cache buffer of size + * the context. If public_key does not contain any cache, cache's value will be changed to point to NULL. Static + * memory implementations that do not know the size of the cache up front can pass a preallocated cache buffer of size * XMSS_INTERNAL_CACHE_SIZE(tree_depth) to accommodate all possible caches. * * This function is bit error resilient in that a single random bit error cannot cause it to load an invalid public key - * or a valid public key that does not match key_context. + * or a valid public key that does not match `key_context`. * - * @param[in,out] cache A pointer to the initial pointer to pass to realloc() to allocate memory for a cache to be - * loaded. If a cache is indeed loaded, this argument's contents will be set to NULL; the - * allocated cache is made part of the key context, instead. + * @param[in,out] cache A pointer to the initial pointer to pass to the user-provided #XmssReallocFunction to + * allocate memory for a cache to be loaded. If a cache is indeed loaded, this argument's + * contents will be set to NULL; the allocated cache is made part of the key context, instead. * @param[in] key_context The context with the loaded private key for which the public key is to be loaded. * The context will be updated with the loaded public key and cache. * @param[in] public_key The public key. This must be a valid XmssPublicKeyInternalBlob. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ARGUMENT_MISMATCH public_key does not contain the public key for the private key that was loaded - * in key_context. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_INVALID_BLOB The data in public_Key is not valid. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ARGUMENT_MISMATCH `public_key` does not contain the public key for the private key that was loaded + * in `key_context`. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_INVALID_BLOB The data in `public_key` is not valid. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_load_public_key( - XmssInternalCache **restrict cache, XmssKeyContext *restrict key_context, - const XmssPublicKeyInternalBlob *restrict public_key); +XmssError xmss_load_public_key(XmssInternalCache **cache, XmssKeyContext *key_context, + const XmssPublicKeyInternalBlob *public_key); /* === Key generation === */ @@ -192,7 +190,7 @@ XmssError xmss_load_public_key( * * A newly generated private key is a single partition that covers the entire key's signature space. * - * @see xmss_partition_signature_space for more information on private key partitions. + * @see xmss_partition_signature_space() for more information on private key partitions. * * Index Obfuscation * ================= @@ -211,31 +209,30 @@ XmssError xmss_load_public_key( * @param[in,out] private_key The stateless part of the private key. This must be stored in a secure location. It * will never change. * @param[in,out] key_usage The stateful part of the private key. This must be stored in a secure location. It - * must be possible to update the key_usage in its secure storage location every time + * must be possible to update the `key_usage` in its secure storage location every time * signatures need to be created. * @param[in] secure_random Must contain 96 bytes of cryptographically secure random data. These random bytes * will be used to generate the secure parts of the private key. * @param[in] index_obfuscation_setting Selects whether or not index obfuscation will be used for the key. * @param[in] random Random data for initialization of the index obfuscation. If index obfuscation is - * enabled, then this needs to contain 32 bytes of random data. If index_obfuscation - is set to XMSS_INDEX_OBFUSCATION_OFF, then this parameter is ignored and may be - NULL. + * enabled, then this needs to contain 32 bytes of random data. If + * `index_obfuscation_setting` is #XMSS_INDEX_OBFUSCATION_OFF, then this parameter is + * ignored and may be NULL. * @param[in] context The initialized context of the library. This must be initialized for the parameter * set that was used to create the private key. After xmss_load_private_key() - * successfully returns this context may be deallocated: only key_context is needed for - * further use of the library. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_BAD_CONTEXT context is not a correctly initialized context. - * @retval XMSS_ERR_INVALID_ARGUMENT An invalid index_obfuscation_setting is passed. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * successfully returns this context may be deallocated: only `key_context` is needed + * for further use of the library. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_BAD_CONTEXT `context` is not a correctly initialized context. + * @retval #XMSS_ERR_INVALID_ARGUMENT An invalid `index_obfuscation_setting` is passed. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_generate_private_key( - XmssKeyContext **key_context, XmssPrivateKeyStatelessBlob **private_key, XmssPrivateKeyStatefulBlob **key_usage, - const XmssBuffer *secure_random, const XmssIndexObfuscationSetting index_obfuscation_setting, - const XmssBuffer *random, const XmssSigningContext *context); +XmssError xmss_generate_private_key(XmssKeyContext **key_context, XmssPrivateKeyStatelessBlob **private_key, + XmssPrivateKeyStatefulBlob **key_usage, const XmssBuffer *secure_random, + XmssIndexObfuscationSetting index_obfuscation_setting, const XmssBuffer *random, const XmssSigningContext *context); /** * @brief @@ -267,8 +264,8 @@ XmssError xmss_generate_private_key( * Caching types * ============= * - * Two different types of caching can be employed. The type to use is passed in the cache_type parameter. The effects of - * the cache depends on the cache level. Single level caching will cache the nodes at only that level, whereas top + * Two different types of caching can be employed. The type to use is passed in the `cache_type` parameter. The effects + * of the cache depends on the cache level. Single level caching will cache the nodes at only that level, whereas top * caching will also cache the nodes between that level and the root. Top caching can be considered the cumulative * version of single level caching, adding one more level of cache as the level decreases whereas single level caching * simply caches the next level. @@ -292,22 +289,23 @@ XmssError xmss_generate_private_key( * Despite their differences, either cache type greatly speeds up the signature generation process compared to not using * any cache. * - * If memory and storage are not constrained, top caching with minimum cache level is advised. It requires roughly - * 64MiB of memory and public key storage for cache level 0. + * If memory and storage are not constrained, top caching with minimum cache level is advised. For a tree height + * $h = 20$, it requires roughly 64 MiB of memory and public key storage for cache level 0. * * Using a cache does not impact the exported public key: the cache is only stored in the XmssPublicKeyInternalBlob, the * internal, extended format for a public key. * * @param[in,out] generation_buffer The work-in-progress public key. This context contains all the (temporary) data * needed to calculate the entire public key and any requested cache layer. - * @param[in,out] cache A pointer to the initial pointer to pass to realloc() to allocate memory for a cache to - * be loaded. If a cache is to be generated, this argument's contents will be set to NULL; - * the allocated cache is made part of the key generation context, instead. - * @param[in,out] generation_cache A pointer to the initial pointer to pass to realloc() to allocate memory for the - * temporary cache needed while generating the public key. This argument's contents - * will be set to NULL when the temporary cache is allocated: it will be made part of - * the key generation context, instead. For static allocation, the size can be - * determined with @ref XMSS_PUBLIC_KEY_GENERATION_CACHE_SIZE. + * @param[in,out] cache A pointer to the initial pointer to pass to the user-provided #XmssReallocFunction to + * allocate memory for a cache to be loaded. If a cache is to be generated, this argument's + * contents will be set to NULL; the allocated cache is made part of the key generation + * context, instead. + * @param[in,out] generation_cache A pointer to the initial pointer to pass to the user-provided #XmssReallocFunction + * to allocate memory for the temporary cache needed while generating the public key. + * This argument's contents will be set to NULL when the temporary cache is allocated: + * it will be made part of the key generation context, instead. For static allocation, + * the size can be determined with #XMSS_PUBLIC_KEY_GENERATION_CACHE_SIZE. * @param[in] key_context The context with the private key for which the public will be calculated. * @param[in] cache_type The type of caching to use. Top caching is advised unless memory or storage is * constrained. @@ -324,45 +322,44 @@ XmssError xmss_generate_private_key( * higher power of 2 (at most 2^tree depth), calculating the public key will require as * many calls to xmss_calculate_public_key_part() to calculate (most of) the public * key. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_INVALID_ARGUMENT An invalid cache_type is passed, cache_level is out of range or - * generation_partitions is out of range or not a power of 2. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_INVALID_ARGUMENT An invalid `cache_type` is passed, `cache_level` is out of range or + * `generation_partitions` is out of range or not a power of 2. */ -XmssError xmss_generate_public_key( - XmssKeyGenerationContext **generation_buffer, XmssInternalCache **cache, XmssInternalCache **generation_cache, - const XmssKeyContext *key_context, const XmssCacheType cache_type, const uint8_t cache_level, - const uint32_t generation_partitions); +XmssError xmss_generate_public_key(XmssKeyGenerationContext **generation_buffer, XmssInternalCache **cache, + XmssInternalCache **generation_cache, const XmssKeyContext *key_context, XmssCacheType cache_type, + uint8_t cache_level, uint32_t generation_partitions); /** * @brief * Perform work on an ongoing public key calculation. * - * @see xmss_generate_public_key for more information on generating a public key. + * @see xmss_generate_public_key() for more information on generating a public key. * * @details - * This function must be called exactly generation_partitions time to perform all the calculations on the public key. - * generation_partitions is set during the public key generation setup, in xmss_generate_public_key(). + * This function must be called exactly `generation_partitions` time to perform all the calculations on the public key. + * `generation_partitions` is set during the public key generation setup, in xmss_generate_public_key(). * * @param[in] generation_buffer The work-in-progress public key that calculations will be done for. - * @param[in] partition_index The partition number, running from 0 to generation_partitions - 1, for which the - * calculations will be done. Each partition index must be used exactly once. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_INVALID_ARGUMENT partition_index is out of range for the passed generation_buffer. - * @retval XMSS_ERR_PARTITION_DONE The generation partition indicated by partition_index has already been started. + * @param[in] partition_index The partition number, running from 0 to `generation_partitions` - 1, for which the + * calculations will be done. Each `partition_index` must be used exactly once. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_INVALID_ARGUMENT `partition_index` is out of range for the passed generation_buffer. + * @retval #XMSS_ERR_PARTITION_DONE The generation partition indicated by `partition_index` has already been + * started. */ -XmssError xmss_calculate_public_key_part( - XmssKeyGenerationContext *generation_buffer, const uint32_t partition_index); +XmssError xmss_calculate_public_key_part(XmssKeyGenerationContext *generation_buffer, uint32_t partition_index); /** * @brief * Finalize calculation on a public key. * - * @see xmss_generate_public_key for more information on generating a public key. + * @see xmss_generate_public_key() for more information on generating a public key. * * @details * This function will gather all the work done in the separate xmss_calculate_public_key_part() calls and combine @@ -370,24 +367,24 @@ XmssError xmss_calculate_public_key_part( * * Calling this function before each required call to xmss_calculate_public_key_part() has finished is an error. * - * On success, generation_buffer and generation_cache will be deallocated. + * On success, `generation_buffer` and `generation_cache` will be deallocated. * * @param[in,out] public_key The blob with the public key and cache. It is recommended to store this blob for later * use. * @param[in] generation_buffer The work-in-progress public key for which the calculations will be finalized. Part of - * this buffer will be moved to key_context, the rest will be deallocated. The value of the - * pointer will be set to NULL to reflect this. + * this buffer will be moved to `key_context`, the rest will be deallocated. The value of + * the pointer will be set to NULL to reflect this. * @param[in] key_context The private key for which these calculations are being done. This context will be * updated to have the newly generated public key loaded. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_UNFINISHED_PARTITIONS Not all generation partitions' calculations have been finished. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_UNFINISHED_PARTITIONS Not all generation partitions' calculations have been finished. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_finish_calculate_public_key( - XmssPublicKeyInternalBlob **public_key, XmssKeyGenerationContext **generation_buffer, XmssKeyContext *key_context); +XmssError xmss_finish_calculate_public_key(XmssPublicKeyInternalBlob **public_key, + XmssKeyGenerationContext **generation_buffer, XmssKeyContext *key_context); /* === Signing === */ @@ -409,24 +406,24 @@ XmssError xmss_finish_calculate_public_key( * available. It is still possible for a bit error to cause an incorrect number of signatures to be claimed. Before * overwriting the stateful blob, check that the correct number of signatures is available, and if not, abort. * - * Calling xmss_request_future_signatures repeatedly will not cause previously requested but unused signatures to be + * Calling xmss_request_future_signatures() repeatedly will not cause previously requested but unused signatures to be * lost, but will simply increase the number of allowed signatures. * * @param[in,out] new_key_usage The blob with the new stateful private key part. * @param[in] key_context The private key for which signatures are to be created. * @param[in] signature_count The number of signatures that will be created. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE The private key partition does not contain signature_count unused + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE The private key partition does not contain `signature_count` unused * keys. - * @retval XMSS_ERR_NO_PUBLIC_KEY key_context does not have a public key loaded. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. + * @retval #XMSS_ERR_NO_PUBLIC_KEY `key_context` does not have a public key loaded. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_request_future_signatures( - XmssPrivateKeyStatefulBlob **restrict new_key_usage, XmssKeyContext *restrict key_context, - const uint32_t signature_count); +XmssError xmss_request_future_signatures(XmssPrivateKeyStatefulBlob **new_key_usage, XmssKeyContext *key_context, + uint32_t signature_count); /** * @brief @@ -434,7 +431,7 @@ XmssError xmss_request_future_signatures( * * @details * This function will only create signatures after a call to xmss_request_future_signatures(), after which at most - * signature_count calls to xmss_sign_message can succeed. + * `signature_count` calls to xmss_sign_message() can succeed. * * The library is written in such a way that a single random bit error cannot cause this function to output a signature * that re-uses a one-time signature key. A single bit error can still cause it to output an invalid signature, in which @@ -448,17 +445,17 @@ XmssError xmss_request_future_signatures( * @param[in] key_context The private key with which the signature will be made. A public key must have been loaded or * generated for this private key. * @param[in] message The message of arbitrary length to sign. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE No additional signatures can be created without calling - * xmss_request_future_signatures first. - * @retval XMSS_ERR_NO_PUBLIC_KEY key_context does not have a public key loaded. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected; note that not all bit errors will be detected.. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE No additional signatures can be created without calling + * xmss_request_future_signatures() first. + * @retval #XMSS_ERR_NO_PUBLIC_KEY `key_context` does not have a public key loaded. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected; note that not all bit errors will be detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_sign_message( - XmssSignatureBlob **restrict signature, XmssKeyContext *restrict key_context, const XmssBuffer *restrict message); +XmssError xmss_sign_message(XmssSignatureBlob **signature, XmssKeyContext *key_context, const XmssBuffer *message); /* === Signature space partitioning === */ @@ -472,7 +469,7 @@ XmssError xmss_sign_message( * created by dividing the private key into multiple partitions. The stateless parts of the two partitioned private keys * remain the same, but their stateful parts ensure that key reuse can't occur. * - * Partitioning a private key creates a second key usage blob which can create new_partition_size signatures, and + * Partitioning a private key creates a second key usage blob which can create `new_partition_size` signatures, and * adjusts the loaded private key so it will no longer be able to place those same signatures. * * A successful partitioning will also result in a new blob for the stateful part of the currently loaded private key. @@ -486,14 +483,14 @@ XmssError xmss_sign_message( * secure location. It should be (securely) wiped from memory afterwards, even if either blob can't be stored. * * New partitions are created from the end of the signature space, whereas requests to create signatures are taken from - * the beginning. A proper backup scheme, which can utilize use xmss_merge_signature_space() to extend the current + * the beginning. A proper backup scheme, which can utilize xmss_merge_signature_space() to extend the current * private key with a part of the backups when the time comes, first creates the last backup partition, then the * second-to-last, and so on, every time decreasing the current private key a bit more, making it the first (and * presumably active) partition. As long as only the lowest partition is used, two consecutive partitions will be able * to be merged. * * A single bit error cannot cause this function to generate new partitions that exceed the bounds of the previous - * signature space, or to produce new partitions with overlap. However, a bit error in new_partition_size can still + * signature space, or to produce new partitions with overlap. However, a bit error in `new_partition_size` can still * cause the resulting new partitions to have incorrect sizes. The size of the new partitions must be checked before * overwriting the old. * @@ -503,17 +500,17 @@ XmssError xmss_sign_message( * @param[in] key_context The private key to partition into two parts. * @param[in] new_partition_size The number of possible signatures to move to the new partition. The private key must * have at least this amount of signatures left. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE The private key context contains less than new_partition_size + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE The private key context contains less than `new_partition_size` * unused keys. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected; note that not all bit errors will be detected. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected; note that not all bit errors will be detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_partition_signature_space( - XmssPrivateKeyStatefulBlob **new_partition, XmssPrivateKeyStatefulBlob **updated_current_partition, - XmssKeyContext *key_context, const uint32_t new_partition_size); +XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **new_partition, + XmssPrivateKeyStatefulBlob **updated_current_partition, XmssKeyContext *key_context, uint32_t new_partition_size); /** * @brief @@ -542,18 +539,17 @@ XmssError xmss_partition_signature_space( * @param[in] partition_extension The stateful private part of the partition to merge into the currently loaded * private key. Both partitions must be for the same private key and must be * consecutive. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_INVALID_BLOB The data in partition_extension is not valid. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_PARTITIONS_NOT_CONSECUTIVE The private key partition in partition_extension is not consecutive - * with the private key partition in key_context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. + * @retval #XMSS_ERR_INVALID_BLOB The data in partition_extension is not valid. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_PARTITIONS_NOT_CONSECUTIVE The private key partition in `partition_extension` is not consecutive + * with the private key partition in `key_context`. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_merge_signature_space( - XmssPrivateKeyStatefulBlob **new_key_usage, XmssKeyContext *key_context, +XmssError xmss_merge_signature_space(XmssPrivateKeyStatefulBlob **new_key_usage, XmssKeyContext *key_context, const XmssPrivateKeyStatefulBlob *partition_extension); /* === Informationals === */ @@ -566,15 +562,16 @@ XmssError xmss_merge_signature_space( * Signatures that have already been requested with xmss_request_future_signatures() but have not yet been created * with xmss_sign_message() are *not* shown in the number returned by this function. * - * @see xmss_partition_signature_space for more information about private key partitions. + * @see xmss_partition_signature_space() for more information about private key partitions. * * @param[out] total_count The total number of signatures that can be created by the key in its entirety. * @param[out] remaining_count The number of available signatures in this partition. * @param[in] key_context The private key partition to query. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected; note that not all bit errors will be detected. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected; note that not all bit errors will be detected. + * (Note that bit errors can also cause different errors or segfaults.) */ XmssError xmss_get_signature_count(size_t *total_count, size_t *remaining_count, const XmssKeyContext *key_context); @@ -583,7 +580,7 @@ XmssError xmss_get_signature_count(size_t *total_count, size_t *remaining_count, * Verify the validity and integrity of a public key blob. * * @details - * This function is bit error resilient in that a single random bit error cannot cause it to wrongly output XMSS_OKAY. + * This function is bit error resilient in that a single random bit error cannot cause it to wrongly output #XMSS_OKAY. * If a function checks the return value of this function and bit error resilience is required, the return value should * be stored in a volatile variable which is then checked twice. * @@ -592,76 +589,75 @@ XmssError xmss_get_signature_count(size_t *total_count, size_t *remaining_count, * was created for this private key. May be NULL. * @param[in] key_context A private key to verify against. Providing this verifies that the public key was created for * the private key in the key context. May be NULL. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER pub_key is NULL. - * @retval XMSS_ERR_ARGUMENT_MISMATCH private_key was passed and pub_key does not contain a public key that - * corresponds to the private key contained in private_key, or key_context was - * passed and pub_key does not contain a public key that corresponds to the private - * key loaded in key_context. - * @retval XMSS_ERR_INVALID_BLOB The data in pub_key is not valid or private_key is passed and contains invalid + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER `pub_key` is NULL. + * @retval #XMSS_ERR_ARGUMENT_MISMATCH `private_key` was passed and `pub_key` does not contain a public key that + * corresponds to the private key contained in `private_key`, or `key_context` was + * passed and `pub_key` does not contain a public key that corresponds to the + * private key loaded in `key_context`. + * @retval #XMSS_ERR_INVALID_BLOB The data in `pub_key` is not valid or `private_key` is passed and contains invalid * data. - * @retval XMSS_ERR_BAD_CONTEXT key_context is passed and is not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is passed and is not a correctly initialized context. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_verify_public_key( - const XmssPublicKeyInternalBlob *restrict pub_key, const XmssPrivateKeyStatelessBlob *restrict private_key, - const XmssKeyContext *restrict key_context); +XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *pub_key, + const XmssPrivateKeyStatelessBlob *private_key, const XmssKeyContext *key_context); /** * @brief * Verify the validity and integrity of a stateless private key part. * * @details - * This function is bit error resilient in that a single random bit error cannot cause it to wrongly output XMSS_OKAY. + * This function is bit error resilient in that a single random bit error cannot cause it to wrongly output #XMSS_OKAY. * If a function checks the return value of this function and bit error resilience is required, the return value should * be stored in a volatile variable which is then checked twice. * - * @param private_key The private key part to verify. - * @param context The signing context that's required for the verification. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_INVALID_BLOB private_key contains invalid data. - * @retval XMSS_ERR_ARGUMENT_MISMATCH The signing context was created for a different parameter set than the private + * @param[in] private_key The private key part to verify. + * @param[in] context The signing context that's required for the verification. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_INVALID_BLOB `private_key` contains invalid data. + * @retval #XMSS_ERR_ARGUMENT_MISMATCH The signing context was created for a different parameter set than the private * key blob. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ XmssError xmss_verify_private_key_stateless(const XmssPrivateKeyStatelessBlob *private_key, - const XmssSigningContext *context); + const XmssSigningContext *context); /** * @brief * Verify the validity and integrity of a stateful private key partition part. * * @details - * This function is bit error resilient in that a single random bit error cannot cause it to wrongly output XMSS_OKAY. + * This function is bit error resilient in that a single random bit error cannot cause it to wrongly output #XMSS_OKAY. * If a function checks the return value of this function and bit error resilience is required, the return value should * be stored in a volatile variable which is then checked twice. * - * @param key_usage The stateful private key part to verify. - * @param private_key A stateless private key part to verify against. Providing this verifies that the two private - * key part are part of the same private key. May be NULL. - * @param key_context A private key to verify against. Providing this verifies that the stateful private key part - * corresponds to the stateless private key in the key_context. May be NULL if and only if - * signing_context is non-NULL. - * @param signing_context The signing context, required to perform a verification operation. May be NULL if and only - * if key_context is non-NULL. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER key_usage is NULL, or if both key_context and signing_context are NULL. - * @retval XMSS_ERR_ARGUMENT_MISMATCH private_key was passed and key_usage does not contain stateful data that - * corresponds to the private key contained in private_key, or key_context was - * passed and key_usage does not contain stateful data that corresponds to the - * private key loaded in key_context. - * @retval XMSS_ERR_INVALID_BLOB The data in pub_key is not valid or private_key is passed and contains invalid + * @param[in] key_usage The stateful private key part to verify. + * @param[in] private_key A stateless private key part to verify against. Providing this verifies that the two + * private key part are part of the same private key. May be NULL. + * @param[in] key_context A private key to verify against. Providing this verifies that the stateful private key + * part corresponds to the stateless private key in the `key_context`. May be NULL if and + * only if `signing_context` is non-NULL. + * @param[in] signing_context The signing context, required to perform a verification operation. May be NULL if and + * only if key_context is non-NULL. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER `key_usage` is NULL, or if both `key_context` and `signing_context` are NULL. + * @retval #XMSS_ERR_ARGUMENT_MISMATCH `private_key` was passed and `key_usage` does not contain stateful data that + * corresponds to the private key contained in `private_key`, or `key_context` was + * passed and `key_usage` does not contain stateful data that corresponds to the + * private key loaded in `key_context`. + * @retval #XMSS_ERR_INVALID_BLOB The data in `pub_key` is not valid or `private_key` is passed and contains invalid * data. - * @retval XMSS_ERR_BAD_CONTEXT key_context is passed and not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is passed and not a correctly initialized context. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_verify_private_key_stateful( - const XmssPrivateKeyStatefulBlob *key_usage, const XmssPrivateKeyStatelessBlob *private_key, - const XmssKeyContext *key_context, const XmssSigningContext *signing_context); +XmssError xmss_verify_private_key_stateful(const XmssPrivateKeyStatefulBlob *key_usage, + const XmssPrivateKeyStatelessBlob *private_key, const XmssKeyContext *key_context, + const XmssSigningContext *signing_context); /** * @brief @@ -670,17 +666,17 @@ XmssError xmss_verify_private_key_stateful( * @details * This function is primarily to support implementations with advanced memory management. * - * @param cache_type The type of cache as found in the public key blob. - * @param cache_level The cache level as found in the public key blob. - * @param pub_key The public key blob to query. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_INVALID_BLOB The data in pub_key is not valid. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @param[out] cache_type The type of cache as found in the public key blob. + * @param[out] cache_level The cache level as found in the public key blob. + * @param[in] pub_key The public key blob to query. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_INVALID_BLOB The data in `pub_key` is not valid. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_get_caching_in_public_key( - XmssCacheType *cache_type, uint32_t *cache_level, const XmssPublicKeyInternalBlob *pub_key); +XmssError xmss_get_caching_in_public_key(XmssCacheType *cache_type, uint32_t *cache_level, + const XmssPublicKeyInternalBlob *pub_key); /* === Exportable public key === */ @@ -692,18 +688,17 @@ XmssError xmss_get_caching_in_public_key( * The internal format contains features like integrity checking. This function will export the public key to the format * specified by RFC 8391 Section 4.1.7. * - * @param exported_pub_key The exported public key. - * @param key_context The private key to export the public key from. A public key must have been loaded or - * generated for this private key. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_NO_PUBLIC_KEY key_context does not have a public key loaded. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @param[in,out] exported_pub_key The exported public key. + * @param[in] key_context The private key to export the public key from. A public key must have been loaded or + * generated for this private key. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_NO_PUBLIC_KEY `key_context` does not have a public key loaded. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_export_public_key(XmssPublicKeyBlob **exported_pub_key, const XmssKeyContext *key_context); +XmssError xmss_export_public_key(XmssPublicKey *exported_pub_key, const XmssKeyContext *key_context); /** * @brief @@ -713,17 +708,16 @@ XmssError xmss_export_public_key(XmssPublicKeyBlob **exported_pub_key, const Xms * The exportable format does not contain any form of integrity checking. With this function the correctness can be * verified. * - * @param exported_pub_key The exported public key to verify. - * @param key_context The private key to verify the public key against. A public key must have been loaded or - * generated for this private key. - * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_INVALID_BLOB The data in exported_pub_key is not valid. - * @retval XMSS_ERR_BAD_CONTEXT key_context is not a correctly initialized context. - * @retval XMSS_ERR_NO_PUBLIC_KEY key_context does not have a public key loaded. - * @retval XMSS_ERR_ARGUMENT_MISMATCH exported_pub_key seems to be a valid exported public key, but is not the same + * @param[in] exported_pub_key The exported public key to verify. + * @param[in] key_context The private key to verify the public key against. A public key must have been loaded or + * generated for this private key. + * @retval #XMSS_OKAY Success. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_BAD_CONTEXT `key_context` is not a correctly initialized context. + * @retval #XMSS_ERR_NO_PUBLIC_KEY `key_context` does not have a public key loaded. + * @retval #XMSS_ERR_ARGUMENT_MISMATCH `exported_pub_key` seems to be a valid exported public key, but is not the same * public key as is loaded in key_context. */ -XmssError xmss_verify_exported_public_key(const XmssPublicKeyBlob *exported_pub_key, const XmssKeyContext *key_context); +XmssError xmss_verify_exported_public_key(const XmssPublicKey *exported_pub_key, const XmssKeyContext *key_context); #endif /* !XMSS_SIGNING_H_INCLUDED */ diff --git a/include/structures.h b/include/structures.h index 33b844d..9a0e71d 100644 --- a/include/structures.h +++ b/include/structures.h @@ -31,13 +31,13 @@ * Use this structure's contents to store or read a stateless private key part. * * After writing a stateless private key part, it is strongly recommended to read it back and validate the correctness - * of the read back version using xmss_verify_private_key_stateless. + * of the read back version using xmss_verify_private_key_stateless(). * - * When creating an XmssPrivateKeyStatelessBlob, the XMSS_PRIVATE_KEY_STATELESS_BLOB_SIZE macro can be used to allocate - * the correct data_size for the structure and its opaque contents. + * When creating an XmssPrivateKeyStatelessBlob, the #XMSS_PRIVATE_KEY_STATELESS_BLOB_SIZE macro can be used to allocate + * the correct `data_size` for the structure and its opaque contents. */ -typedef struct { - /** @brief The size in bytes of the data array. */ +typedef struct XmssPrivateKeyStatelessBlob { + /** @brief The size in bytes of the `data` array. */ size_t data_size; /** @brief The data to be stored. */ uint8_t data[]; @@ -58,13 +58,13 @@ typedef struct { * Use this structure's contents to store or read a stateful private key part. * * After writing a stateful private key part, it is strongly recommended to read it back and validate the correctness of - * the read back version using xmss_verify_private_key_stateful. + * the read back version using xmss_verify_private_key_stateful(). * - * When creating an XmssPrivateKeyStatefulBlob, the XMSS_PRIVATE_KEY_STATEFUL_BLOB_SIZE() macro can be used to allocate + * When creating an XmssPrivateKeyStatefulBlob, the #XMSS_PRIVATE_KEY_STATEFUL_BLOB_SIZE macro can be used to allocate * the correct size for the structure and its opaque contents. */ -typedef struct { - /** @brief The size in bytes of the data array. */ +typedef struct XmssPrivateKeyStatefulBlob { + /** @brief The size in bytes of the `data` array. */ size_t data_size; /** @brief The data to be stored. */ uint8_t data[]; @@ -86,13 +86,13 @@ typedef struct { * Use this structure's contents to store or read a public key for the signing library. * * After writing a public key, it is strongly recommended to read it back and validate the correctness of the read back - * version using xmss_verify_public_key. + * version using xmss_verify_public_key(). * - * When creating an XmssPublicKeyInternalBlob, the XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE() macro can be used to allocate + * When creating an XmssPublicKeyInternalBlob, the #XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE() macro can be used to allocate * the correct size for the structure and its opaque contents. */ -typedef struct { - /** @brief The size in bytes of the data array. */ +typedef struct XmssPublicKeyInternalBlob { + /** @brief The size in bytes of the `data` array. */ size_t data_size; /** @brief The data to be stored. */ uint8_t data[]; @@ -102,12 +102,12 @@ typedef struct { * @brief * The size in bytes of an XmssPublicKeyInternalBlob. * - * @note The arguments to XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE will be evaluated multiple times. + * @note The arguments to #XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE() will be evaluated multiple times. * - * @param cache_type The cache type that is used. - * @param cache_level The cache level that is to be held. - * @param param_set The parameter set for the public key. - * @see xmss_generate_public_key for more information about the cache type and level. + * @param[in] cache_type The cache type that is used. + * @param[in] cache_level The cache level that is to be held. + * @param[in] param_set The parameter set for the public key. + * @see xmss_generate_public_key() for more information about the cache type and level. */ #define XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(cache_type, cache_level, param_set) \ (sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssValue256) + 4u + 4u + sizeof(XmssValue256) + sizeof(XmssValue256) + \ @@ -120,18 +120,20 @@ typedef struct { * * @details * This is a public key in the format described by RFC 8391, Section 4.1.7. + * Note that for all parameter sets supported by this library the XMSS security parameter $n = 32$. + * Therefore, this structure has a fixed size; it will not fit public keys for other values of $n$. */ -typedef struct { +typedef struct XmssPublicKey { /** * @brief - * The XmssParameterSetOID with which this public key was generated. + * The #XmssParameterSetOID with which this public key was generated. * * @details - * scheme_identifier must be written in big-endian notation. + * `scheme_identifier` must be written in big-endian notation. */ uint32_t scheme_identifier; /** @brief The public key root. */ - XmssValue256 public_key; + XmssValue256 root; /** @brief The public SEED. */ XmssValue256 seed; } XmssPublicKey; @@ -142,41 +144,9 @@ typedef struct { * * @details * Included for API consistency. - * For API use, use XmssPublicKeyBlob and XMSS_PUBLIC_KEY_BLOB_SIZE. -*/ -#define XMSS_PUBLIC_KEY_SIZE \ - sizeof(XmssPublicKey) - -/** - * @brief - * Structure containing the size and the public key formatted for export. - * - * @details - * This blob-structure embeds the public key in the format described by RFC 8391, Section 4.1.7, along with the size - * in bytes of the public key. */ -typedef struct { - /** - * @brief - * The size in bytes of the XmssPublicKey data member. - */ - size_t data_size; - /** - * @brief - * Public key in the format described by RFC 8391, Section 4.1.7. - */ - XmssPublicKey data; -} XmssPublicKeyBlob; - -/** - * @brief - * The size of the XmssPublicKeyBlob. - * - * @details - * Included for API consistency. -*/ -#define XMSS_PUBLIC_KEY_BLOB_SIZE \ - sizeof(XmssPublicKeyBlob) +#define XMSS_PUBLIC_KEY_SIZE \ + (sizeof(XmssPublicKey)) /** * @brief @@ -185,19 +155,19 @@ typedef struct { * @details * This is a signature in the format described by RFC 8391, Section 4.1.8. * - * For use with the XMSS library APIs, use XmssSignatureBlob and the XMSS_SIGNATURE_BLOB_SIZE() macro. + * For use with the XMSS library APIs, use XmssSignatureBlob and the #XMSS_SIGNATURE_BLOB_SIZE() macro. */ -typedef struct { +typedef struct XmssSignature { /** * @brief * The leaf index of the key that was used for generating the signature. If index obfuscation is used, then * this is the obfuscated index. * * @details - * leaf_index must be written in big-endian notation. + * `leaf_index` must be written in big-endian notation. */ uint32_t leaf_index; - /** @brief The randomized hashing string r. */ + /** @brief The randomized hashing string $r$. */ XmssValue256 random_bytes; /** @brief The WOTS+ signature. */ XmssValue256 wots_signature[67]; @@ -206,7 +176,7 @@ typedef struct { * The authentication path. * * @details - * auth_path contains one node for every level in the tree, so tree_depth nodes in total. + * `authentication_path` contains one node for every level in the tree, so `tree_depth` nodes in total. */ XmssValue256 authentication_path[]; } XmssSignature; @@ -218,18 +188,19 @@ typedef struct { * @details * This is a signature in the format described by RFC 8391, Section 4.1.8. * - * When creating an XmssSignatureBlob, the XMSS_SIGNATURE_BLOB_SIZE() macro can be used to allocate the correct + * When creating an XmssSignatureBlob, the #XMSS_SIGNATURE_BLOB_SIZE() macro can be used to allocate the correct * size. */ -typedef struct { +typedef struct XmssSignatureBlob { /** * @brief * The size in bytes of the exported signature. * * @details * The size of *the contents of* the signature blob: - * data_size = XMSS_SIGNATURE_SIZE(parameter_set) - * data_size is written in native-endian and is not part of the exported data. + * `data_size` = #XMSS_SIGNATURE_SIZE(parameter_set). + * + * `data_size` is written in native-endian and is not part of the exported data. */ size_t data_size; /** @@ -244,10 +215,11 @@ typedef struct { * @brief * Provide access to an XmssSignatureBlob's data as a structured type. * - * @param signature The signature to access as a struct. + * @param[in] signature The signature to access as a struct. * @returns A pointer to the signature struct. NULL if signature is NULL. */ -inline XmssSignature *xmss_get_signature_struct(const XmssSignatureBlob *const signature) { +static inline XmssSignature *xmss_get_signature_struct(const XmssSignatureBlob *const signature) +{ if (signature == NULL) { return NULL; } @@ -258,11 +230,11 @@ inline XmssSignature *xmss_get_signature_struct(const XmssSignatureBlob *const s * @brief * The size in bytes of an XmssSignature. * - * Note that when using the XMSS API, XmssSignatureBlob and XMSS_SIGNATURE_BLOB_SIZE should be used. + * Note that when using the XMSS API, XmssSignatureBlob and #XMSS_SIGNATURE_BLOB_SIZE() should be used. * - * @note The argument to XMSS_SIGNATURE_SIZE will be evaluated multiple times. + * @note The argument to #XMSS_SIGNATURE_SIZE() will be evaluated multiple times. * - * @param param_set The XmssParameterSetOID that was used for the signature. + * @param[in] param_set The #XmssParameterSetOID that was used for the signature. */ #define XMSS_SIGNATURE_SIZE(param_set) \ (sizeof(XmssSignature) + sizeof(XmssValue256) * XMSS_TREE_DEPTH(param_set)) @@ -271,18 +243,32 @@ inline XmssSignature *xmss_get_signature_struct(const XmssSignatureBlob *const s * @brief * The size in bytes of an XmssSignatureBlob. * - * @note The argument to XMSS_SIGNATURE_BLOB_SIZE will be evaluated multiple times. + * @note The argument to #XMSS_SIGNATURE_BLOB_SIZE() will be evaluated multiple times. * - * @param param_set The XmssParameterSetOID that was used for the signature. + * @param[in] param_set The #XmssParameterSetOID that was used for the signature. */ #define XMSS_SIGNATURE_BLOB_SIZE(param_set) \ (sizeof(XmssSignatureBlob) + XMSS_SIGNATURE_SIZE(param_set)) /** @private */ -STATIC_ASSERT(sizeof(XmssSignatureBlob) + sizeof(XmssSignature) + 10 * sizeof(XmssValue256) == XMSS_SIGNATURE_BLOB_SIZE(XMSS_PARAM_SHA2_10_256), - "XMSS_SIGNATURE_BLOB_SIZE mismatch"); +XMSS_STATIC_ASSERT(sizeof(XmssSignatureBlob) + sizeof(XmssSignature) + 10 * sizeof(XmssValue256) + == XMSS_SIGNATURE_BLOB_SIZE(XMSS_PARAM_SHA2_10_256), "XMSS_SIGNATURE_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssSignatureBlob) + sizeof(XmssSignature) + 16 * sizeof(XmssValue256) == XMSS_SIGNATURE_BLOB_SIZE(XMSS_PARAM_SHA2_16_256), - "XMSS_SIGNATURE_BLOB_SIZE mismatch"); +XMSS_STATIC_ASSERT(sizeof(XmssSignatureBlob) + sizeof(XmssSignature) + 16 * sizeof(XmssValue256) + == XMSS_SIGNATURE_BLOB_SIZE(XMSS_PARAM_SHA2_16_256), "XMSS_SIGNATURE_BLOB_SIZE mismatch"); + +/** @brief Size of an XmssVerificationContext. */ +#define XMSS_VERIFICATION_CONTEXT_SIZE (4 + 4 + 8 + 8 + 200 + 8 + 32) + +/** + * @brief + * The context for signature verification. + */ +typedef union XmssVerificationContext { + /** @brief Content of the XmssVerificationContext, opaque to the library user. */ + uint8_t data[XMSS_VERIFICATION_CONTEXT_SIZE]; + /** @brief Enforces alignment of `data`. */ + uint64_t alignment; +} XmssVerificationContext; #endif /* !XMSS_STRUCTURES_H_INCLUDED */ diff --git a/include/types.h b/include/types.h index 63c2bed..c8a5cff 100644 --- a/include/types.h +++ b/include/types.h @@ -32,7 +32,7 @@ * @details * These code words are used to provide values with high hamming distance to other enums' values. * - * XMSS_DISTANT_VALUE_0 is guaranteed to be 0, allowing its use for values that should automatically be set when a + * #XMSS_DISTANT_VALUE_0 is guaranteed to be 0, allowing its use for values that should automatically be set when a * structure it contains is cleared to all-zeroes. */ enum XmssDistantValues @@ -92,13 +92,22 @@ enum XmssDistantValues * * @details * The values of these return codes are chosen from a Hamming(8,4) code to ensure bit error resilience. - * Note that XMSS_DISTANT_VALUE_0 (with value 0) is unused to avoid accidentally confusing it with an uninitialized + * Note that #XMSS_DISTANT_VALUE_0 (with value 0) is unused to avoid accidentally confusing it with an uninitialized * value. * * @see XmssDistantValues + * @see xmss_error_to_description() + * @see xmss_error_to_name() */ typedef enum XmssError { + /* NOTE to developers + * ================== + * + * Keep the enumeration-constants synchronized with xmss_error_to_name(). + * Keep the Doxygen descriptions for the enumeration-constants synchronized with xmss_error_to_description(). + */ + /* XMSS_DISTANT_VALUE_0 must not be used. */ /** @brief Success. */ @@ -107,7 +116,8 @@ typedef enum XmssError /** @brief An unexpected NULL pointer was passed. */ XMSS_ERR_NULL_POINTER = XMSS_DISTANT_VALUE_2, - /* XMSS_DISTANT_VALUE_3 is currently unused. */ + /** @brief The signature is invalid. */ + XMSS_ERR_INVALID_SIGNATURE = XMSS_DISTANT_VALUE_3, /** @brief A mismatch was detected between arguments. */ XMSS_ERR_ARGUMENT_MISMATCH = XMSS_DISTANT_VALUE_4, @@ -139,8 +149,23 @@ typedef enum XmssError /** @brief The key context does not have a public key loaded. */ XMSS_ERR_NO_PUBLIC_KEY = XMSS_DISTANT_VALUE_D, - /** @brief A bit error was detected. (Note that bit errors can also cause different errors or segfaults.) */ - XMSS_ERR_BIT_ERROR_DETECTED = XMSS_DISTANT_VALUE_E + /** + * @brief + * A fault was detected. + * + * @details + * Note that faults can also cause different errors or segfaults. + */ + XMSS_ERR_FAULT_DETECTED = XMSS_DISTANT_VALUE_E, + + /** + * @brief + * Function returned prematurely. + * + * @details + * Dummy value to initialize return value variables before the correct value is known. + */ + XMSS_UNINITIALIZED = XMSS_DISTANT_VALUE_F } XmssError; /** @@ -182,9 +207,9 @@ typedef enum XmssParameterSetOID * @brief * The tree depth for a given XMSS parameter set. * - * @note The argument to XMSS_TREE_DEPTH will be evaluated multiple times. + * @note The argument to #XMSS_TREE_DEPTH() will be evaluated multiple times. * - * @param param_set A valid XmssParameterSetOID. + * @param[in] param_set A valid #XmssParameterSetOID. */ #define XMSS_TREE_DEPTH(param_set) \ (((param_set) == XMSS_PARAM_SHA2_10_256 || (param_set) == XMSS_PARAM_SHAKE256_10_256) ? 10u : \ @@ -247,11 +272,11 @@ typedef enum XmssCacheType * For all supported parameter sets, both digests and seeds are 256-bit values. * * This type makes no guarantees about the memory alignment of the object. - * It may be freely cast to and from a uint8_t pointer. + * It may be freely cast to and from a `uint8_t` pointer. * * This type ensures that both caller and callee agree on the amount of data pointed to. */ -typedef struct { +typedef struct XmssValue256 { /** @brief The byte stream representation of the value. */ uint8_t data[32]; } XmssValue256; @@ -266,7 +291,7 @@ typedef struct { #define XMSS_VALUE_256_WORDS 8 /** @private */ -STATIC_ASSERT(XMSS_VALUE_256_WORDS == sizeof(XmssValue256) / sizeof(uint32_t), +XMSS_STATIC_ASSERT(XMSS_VALUE_256_WORDS == sizeof(XmssValue256) / sizeof(uint32_t), "inconsistent value of XMSS_VALUE_256_WORDS"); /** @@ -282,29 +307,29 @@ STATIC_ASSERT(XMSS_VALUE_256_WORDS == sizeof(XmssValue256) / sizeof(uint32_t), * For all supported parameter sets, both digests and seeds are 256-bit values. * * This form of the value is guaranteed to be aligned on a 32-bit memory boundary. - * It may be freely cast to and from a uint32_t pointer. + * It may be freely cast to and from a `uint32_t` pointer. * * May require byte swapping to get XmssValue256. * * This type ensures that both caller and callee agree on the amount of data pointed to. */ -typedef struct { +typedef struct XmssNativeValue256 { /** @brief The contents of the value. */ uint32_t data[XMSS_VALUE_256_WORDS]; } XmssNativeValue256; /** @private */ -STATIC_ASSERT(sizeof(XmssNativeValue256) == sizeof(XmssValue256), +XMSS_STATIC_ASSERT(sizeof(XmssNativeValue256) == sizeof(XmssValue256), "XmssNativeValue256 and XmssValue256 should have equal size"); /** * @brief * A pointer to a buffer with a given size. */ -typedef struct { - /** @brief The size in bytes of the data. */ +typedef struct XmssBuffer { + /** @brief The size in bytes of `data`. */ size_t data_size; - /** @brief The data. May be NULL if and only if data_size is 0. */ + /** @brief The data. May be NULL if and only if `data_size` is 0. */ uint8_t *data; } XmssBuffer; @@ -315,15 +340,15 @@ typedef struct { * @details * All memory allocations will be done using this function. * - * Note that the signature for this function type is identical to that of standard C realloc(). + * Note that the signature for this function type is identical to that of standard C `realloc()`. * - * @see xmss_context_initialize for additional information about memory management. + * @see xmss_context_initialize() for additional information about memory management. * - * @param ptr The pointer to an existing block of memory to resize, or NULL to allocate a new block of memory. If the - * memory reallocation function returns a non-NULL value that is different from this argument, this pointer - * is considered to no longer be valid and will neither be used nor passed to a memory deallocation - * function. - * @param size The requested size of the memory block. + * @param[in] ptr The pointer to an existing block of memory to resize, or NULL to allocate a new block of memory. If + * the memory reallocation function returns a non-NULL value that is different from this argument, this + * pointer is considered to no longer be valid and will neither be used nor passed to a memory + * deallocation function. + * @param[in] size The requested size of the memory block. * @returns The pointer to the memory block, which is at least size bytes large, or NULL if (re)allocation failed. */ typedef void *(*XmssReallocFunction)(void *ptr, size_t size); @@ -335,13 +360,13 @@ typedef void *(*XmssReallocFunction)(void *ptr, size_t size); * @details * All memory deallocations will be done using this function. * - * Note that the signature for this function type is identical to that of standard C free(). + * Note that the signature for this function type is identical to that of standard C `free()`. * - * @see xmss_context_initialize for additional information about memory management. + * @see xmss_context_initialize() for additional information about memory management. * - * @param ptr The pointer to the allocated memory. After the memory deallocation function returns this pointer is - * considered to no longer be valid and will neither be used nor passed to a memory deallocation function - * again. + * @param[in] ptr The pointer to the allocated memory. After the memory deallocation function returns this pointer is + * considered to no longer be valid and will neither be used nor passed to a memory deallocation + * function again. */ typedef void (*XmssFreeFunction)(void *ptr); @@ -352,8 +377,8 @@ typedef void (*XmssFreeFunction)(void *ptr); * @details * This function overwrites sensitive data in memory with zeros. * - * @param ptr The pointer to the object to erase. - * @param size The size in bytes of the object. + * @param[in] ptr The pointer to the object to erase. + * @param[in] size The size in bytes of the object. */ typedef void (*XmssZeroizeFunction)(void *ptr, size_t size); diff --git a/include/verification.h b/include/verification.h index 8727426..d34d3f2 100644 --- a/include/verification.h +++ b/include/verification.h @@ -3,6 +3,7 @@ * SPDX-License-Identifier: MIT * * SPDX-FileContributor: Thomas Schaap + * SPDX-FileContributor: Frans van Dorsselaer */ /** @@ -17,38 +18,83 @@ /** @private @brief Include guard. */ #define XMSS_VERIFICATION_H_INCLUDED +#include "opaque_structures.h" #include "structures.h" #include "types.h" /** * @brief - * Calculate the expected public key for a given message and signature. + * Initialize a context for signature verification. * * @details - * This does not verify the correctness of the public key. Returning a single value to indicate 'good' or 'bad' is not - * resilient enough for many use cases. Implementations can use the expected public key in a verification algorithm - * that provides the level of resilience that meets their needs, ranging from a simple entirely non-resilient - * memcmp() to a fully fault injection-proof check with multiple redundancies. - * - * The public key is still passed to this function, but only for the additional public data it contains. This data is - * used to calculate both part of the signature and part of the public key that would verify the signature's - * correctness. - * - * @param[out] expected_public_key The 32-byte public key that would be calculated from the signature and the message. - * If and only if this matches the actual public key, then the signature is valid. - * @param[in] msg The arbitrary-length message to calculate the expected public key for. - * @param[in] pub_key The public key to verify against. This is *not* used to actually verify the - * signature but contains (public) data that is needed to calculate the expected public - * key from the message. - * @param[in] signature The signature over the message that needs to be verified. - * @retval XMSS_OKAY The calculation was successful. Note that this does not necessarily mean that the signature is - * valid. - * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. - * @retval XMSS_ERR_INVALID_ARGUMENT The scheme identifier in pub_key is not supported or invalid. - * @retval XMSS_ERR_INVALID_BLOB The data in pub_key or signature is not valid. + * The context stores the pointers to the public key and the signature. It is the caller's responsibility to keep the + * pointed-to objects available and constant for every call that uses the context. + * To reduce memory usage, the objects themselves are not copied into the context. + * + * @param[out] context The context for the signature verification. + * @param[in] public_key The public key to verify `signature` against. + * @param[in] signature The signature over the message that needs to be verified. + * @param[in] signature_length The length of `signature` in bytes. + * @retval #XMSS_OKAY `context` was initialized successfully. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_INVALID_ARGUMENT The parameter set of `public_key` is not supported. + * @retval #XMSS_ERR_INVALID_SIGNATURE `signature` cannot be valid, either because the parameter set of `signature` + * does not match `public_key`, or `signature_length` is incorrect. + */ +XmssError xmss_verification_init(XmssVerificationContext *context, const XmssPublicKey *public_key, + const XmssSignature *signature, size_t signature_length); + +/** + * @brief + * Update the verification context with the next chunk of the message. + * + * @details + * When it isn't practical to hold the entire message in memory, this function can be used to process the message in + * chunks. + * + * When fault injection tolerance is required, provide a non-NULL `part_verify` parameter. After this function + * completes successfully, compare the value returned in `*part_verify` with the original message `part` pointer. + * + * @param[in,out] context The context for the verification. + * @param[in] part The next part of the message. May be NULL if part_length is 0. + * @param[in] part_length The length of `part` in bytes. For optimal performance, this should be a multiple of the + * hash function's block size (64 bytes for SHA256, 136 for SHAKE256_256) if possible, + * but this is not required. + * @param[out] part_verify (optional, may be NULL) Outputs a copy of `part` to verify the correct message was + * processed. This can be used to mitigate fault injections. + * @retval #XMSS_OKAY `context` was updated successfully. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_BAD_CONTEXT Either `context` was not initialized correctly, or xmss_verification_check() was + * already called. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) + */ +XmssError xmss_verification_update(XmssVerificationContext *context, const uint8_t *part, size_t part_length, + const uint8_t *volatile *part_verify); + +/** + * @brief + * Perform a single validation of the message signature. + * + * @details + * When all message parts have been processed with xmss_verification_update(), this function performs a single + * (non-redundant) validation of the signature. This function may be called multiple times to provide fault injection + * tolerance. + * + * Provide the same pointer to the public key that was also provided to xmss_verification_init(). This function will + * verify that the two copies of the pointer value match, such that a single pointer manipulation cannot be used by an + * attacker to spoof the public key. + * + * @param[out] context The context for the signature verification. + * @param[in] public_key The public key to verify the signature against. This is used to mitigate fault injection. + * @retval #XMSS_OKAY The signature is valid. + * @retval #XMSS_ERR_NULL_POINTER A NULL pointer was passed. + * @retval #XMSS_ERR_BAD_CONTEXT `context` was not initialized correctly. + * @retval #XMSS_ERR_INVALID_SIGNATURE The signature did not pass the verification. + * @retval #XMSS_ERR_FAULT_DETECTED A bit error was detected, or `public_key` does not match the public key provided + * to xmss_verification_init(). + * (Note that bit errors can also cause different errors or segfaults.) */ -XmssError xmss_calculate_expected_public_key( - XmssValue256 *restrict expected_public_key, const XmssBuffer *restrict msg, - const XmssPublicKeyBlob *restrict pub_key, const XmssSignatureBlob *restrict signature); +XmssError xmss_verification_check(XmssVerificationContext *context, const XmssPublicKey *public_key); #endif /* !XMSS_VERIFICATION_H_INCLUDED */ diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..4096062 --- /dev/null +++ b/include/version.h @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Public API for XMSS library version control. + * + * The macros and functions are currently all prefixed with `XMSS_LIBRARY_` and `xmss_library_` as currently only the + * library implementation is versioned, not the XMSS algorithm itself. + */ + +#pragma once + +#ifndef XMSS_VERSION_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_VERSION_H_INCLUDED + +#include "xmss_config.h" + +#include + +#include "compat.h" + +/** @private */ +XMSS_STATIC_ASSERT(XMSS_LIBRARY_VERSION_MAJOR >= 1 && XMSS_LIBRARY_VERSION_MAJOR <= 255, + "XMSS_LIBRARY_VERSION inconsistent"); +/** @private */ +XMSS_STATIC_ASSERT(XMSS_LIBRARY_VERSION_MINOR >= 0 && XMSS_LIBRARY_VERSION_MINOR <= 255, + "XMSS_LIBRARY_VERSION inconsistent"); +/** @private */ +XMSS_STATIC_ASSERT(XMSS_LIBRARY_VERSION_PATCH >= 0 && XMSS_LIBRARY_VERSION_PATCH <= 255, + "XMSS_LIBRARY_VERSION inconsistent"); + +/** + * @brief + * Construct an amalgamated semantic version from parts. + * + * @details + * The resulting value may be compared directly against #XMSS_LIBRARY_VERSION (intended for compile-time checks) and/or + * xmss_library_get_version() (intended for run time checks). + * + * @remarks + * The individual parts will be silently truncated to uint8_t; i.e., their values must be between 0 and 255 (inclusive). + * + * @param[in] major The major version. + * @param[in] minor The minor version. + * @param[in] patch The patch version. + * @returns The amalgamated semantic version, as an uint32_t. + */ +#define XMSS_LIBRARY_VERSION_CONSTRUCT(major, minor, patch) \ + ((((uint32_t)(uint8_t)(major)) << 16) \ + | (((uint32_t)(uint8_t)(minor)) << 8) \ + | ((uint32_t)(uint8_t)(patch))) + +/** + * @brief + * The amalgamated semantic version (SemVer 2.0) of the library headers. + * + * @details + * To verify at compile-time that you are compiling against the expected library version, compare this value against + * the expected value constructed with #XMSS_LIBRARY_VERSION_CONSTRUCT(). For example: + * + * ``` + * static_assert(XMSS_LIBRARY_VERSION == XMSS_LIBRARY_VERSION_CONSTRUCT(1,2,3), "Unexpected library version"); + * ``` + * + * Alternatively, compare the values of the individual parts. For example: + * + * ``` + * static_assert(XMSS_LIBRARY_VERSION_MAJOR == 2, "Unexpected library version"); + * ``` + * + * @see xmss_library_get_version() + */ +#define XMSS_LIBRARY_VERSION (XMSS_LIBRARY_VERSION_CONSTRUCT(XMSS_LIBRARY_VERSION_MAJOR, XMSS_LIBRARY_VERSION_MINOR, \ + XMSS_LIBRARY_VERSION_PATCH)) + +/** + * @brief + * Retrieve the major version from an amalgamated semantic version. + * + * @see #XMSS_LIBRARY_VERSION + * @see xmss_library_get_version() + * + * @param[in] version An amalgamated semantic version. + * @returns The major version, as an uint8_t. + */ +#define XMSS_LIBRARY_GET_VERSION_MAJOR(version) ((uint8_t)((((uint32_t)(version)) >> 16) & UINT8_MAX)) + +/** + * @brief + * Retrieve the minor version from an amalgamated semantic version. + * + * @see #XMSS_LIBRARY_VERSION + * @see xmss_library_get_version() + * + * @param[in] version An amalgamated semantic version. + * @returns The minor version, as an uint8_t. + */ +#define XMSS_LIBRARY_GET_VERSION_MINOR(version) ((uint8_t)((((uint32_t)(version)) >> 8) & UINT8_MAX)) + +/** + * @brief + * Retrieve the patch version from an amalgamated semantic version. + * + * @see #XMSS_LIBRARY_VERSION + * @see xmss_library_get_version() + * + * @param[in] version An amalgamated semantic version. + * @returns The patch version, as an uint8_t. + */ +#define XMSS_LIBRARY_GET_VERSION_PATCH(version) ((uint8_t)(((uint32_t)(version)) & UINT8_MAX)) + +/** @private */ +XMSS_STATIC_ASSERT(XMSS_LIBRARY_GET_VERSION_MAJOR(XMSS_LIBRARY_VERSION) == XMSS_LIBRARY_VERSION_MAJOR, + "XMSS_LIBRARY_VERSION inconsistent"); +/** @private */ +XMSS_STATIC_ASSERT(XMSS_LIBRARY_GET_VERSION_MINOR(XMSS_LIBRARY_VERSION) == XMSS_LIBRARY_VERSION_MINOR, + "XMSS_LIBRARY_VERSION inconsistent"); +/** @private */ +XMSS_STATIC_ASSERT(XMSS_LIBRARY_GET_VERSION_PATCH(XMSS_LIBRARY_VERSION) == XMSS_LIBRARY_VERSION_PATCH, + "XMSS_LIBRARY_VERSION inconsistent"); + +/** + * @brief + * Retrieve, at application runtime, the amalgamated semantic version (SemVer 2.0) of the library at build-time of the + * library. + * + * @details + * To verify at runtime that you are using the expected (binary) library version, compare this value against the + * expected value constructed with #XMSS_LIBRARY_VERSION_CONSTRUCT(), or against #XMSS_LIBRARY_VERSION. For example: + * + * ``` + * if (xmss_library_get_version() != XMSS_LIBRARY_VERSION) { + * // handle library version mismatch + * } + * ``` + * + * Alternatively, compare individual parts using #XMSS_LIBRARY_GET_VERSION_MAJOR(), #XMSS_LIBRARY_GET_VERSION_MINOR(), + * and/or #XMSS_LIBRARY_GET_VERSION_MINOR(). For example: + * + * ``` + * if (XMSS_LIBRARY_GET_VERSION_MAJOR(xmss_library_get_version()) != XMSS_LIBRARY_VERSION_MAJOR) + * { + * // handle library version mismatch + * } + * ``` + * + * @returns The library version. + */ +uint32_t xmss_library_get_version(void); + +#endif /* !XMSS_VERSION_H_INCLUDED */ diff --git a/include/xmss_config.in.h b/include/xmss_config.in.h index 686c815..6866509 100644 --- a/include/xmss_config.in.h +++ b/include/xmss_config.in.h @@ -27,7 +27,7 @@ * @details * This option is automatically detected by CMake. * - * @see STATIC_ASSERT + * @see XMSS_STATIC_ASSERT */ #cmakedefine01 XMSS_CAN_USE_STATIC_ASSERT @@ -38,8 +38,47 @@ * @details * This option is automatically detected by CMake. * - * @see STATIC_ASSERT + * @see XMSS_STATIC_ASSERT */ #cmakedefine01 XMSS_CAN_USE_EXTENSION_STATIC_ASSERT +/** + * @brief + * Indicates whether the library is built with signing support. + * + * @details + * By default, signing support is enabled. This macro is defined with the value 0 if you compile the library with + * ``` + * cmake -DXMSS_ENABLE_SIGNING=OFF + * ``` + */ +#cmakedefine01 XMSS_ENABLE_SIGNING + +/** + * @brief + * The major version of the library headers. + * + * @see #XMSS_LIBRARY_VERSION + * @see xmss_library_get_version() + */ +#define XMSS_LIBRARY_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ + +/** + * @brief + * The minor version of the library headers. + * + * @see #XMSS_LIBRARY_VERSION + * @see xmss_library_get_version() + */ +#define XMSS_LIBRARY_VERSION_MINOR @PROJECT_VERSION_MINOR@ + +/** + * @brief + * The patch version of the library headers. + * + * @see #XMSS_LIBRARY_VERSION + * @see xmss_library_get_version() + */ +#define XMSS_LIBRARY_VERSION_PATCH @PROJECT_VERSION_PATCH@ + #endif /* !XMSS_XMSS_CONFIG_H_INCLUDED */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa32415..5b391d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,160 +1,87 @@ # SPDX-FileCopyrightText: 2023 Fox Crypto B.V. # SPDX-License-Identifier: MIT -if (XMSS_ENABLE_SHA256) - - if(XMSS_SHA256 STREQUAL "OverrideGeneric") - set(SHA256_SOURCES - endianness.h - generic_xmss_hashes.h - sha256_generic_xmss_hashes.h - sha256_xmss_hashes.c - sha256_xmss_hashes.h - xmss_hashes_base.h - ${XMSS_SHA256_OVERRIDE_SOURCES} - ) - else() - set(SHA256_SOURCES - endianness.h +if(XMSS_ENABLE_SHA256) + if(XMSS_ENABLE_SHA256_DEFAULT) + list(APPEND SOURCES sha256_internal_default.c) + endif() + if(NOT XMSS_ENABLE_SHA256_GENERIC) + list(APPEND SOURCES + sha256_internal_H0.c sha256_internal_xmss_hashes.c - sha256_internal_xmss_hashes.h - sha256_xmss_hashes.c - sha256_xmss_hashes.h - xmss_hashes_base.h ) - if(XMSS_SHA256 STREQUAL "Default") - list(APPEND SHA256_SOURCES sha256_internal_default.c) - else() - # "OverrideInternal" - list(APPEND SHA256_SOURCES ${XMSS_SHA256_OVERRIDE_SOURCES}) - endif() endif() - - add_library(sha256 STATIC ${SHA256_SOURCES}) - target_include_directories(sha256 - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} - ) - target_link_libraries(sha256 - PUBLIC xmss_headers - ) - endif() - -if (XMSS_ENABLE_SHAKE256_256) - - if(XMSS_SHAKE256_256 STREQUAL "OverrideGeneric") - set(SHAKE256_256_SOURCES - endianness.h - generic_xmss_hashes.h - shake256_256_generic_xmss_hashes.h - shake256_256_xmss_hashes.c - shake256_256_xmss_hashes.h - xmss_hashes_base.h - ${XMSS_SHAKE256_256_OVERRIDE_SOURCES} - ) - else() - set(SHAKE256_256_SOURCES - shake256_256_internal_xmss_hashes.c - shake256_256_internal_xmss_hashes.h - shake256_256_xmss_hashes.c - shake256_256_xmss_hashes.h - xmss_hashes_base.h - ) - if(XMSS_SHAKE256_256 STREQUAL "Default") - list(APPEND SHAKE256_256_SOURCES shake256_256_internal_default.c) - else() - # "OverrideInternal" - list(APPEND SHAKE256_256_SOURCES ${XMSS_SHAKE256_256_OVERRIDE_SOURCES}) - endif() +if(XMSS_ENABLE_SHAKE256_256) + if(XMSS_ENABLE_SHAKE256_256_DEFAULT) + list(APPEND SOURCES shake256_256_internal_default.c) endif() - - add_library(shake256_256 STATIC ${SHAKE256_256_SOURCES}) - target_include_directories(shake256_256 - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} - ) - target_link_libraries(shake256_256 - PUBLIC xmss_headers + if(NOT XMSS_ENABLE_SHAKE256_256_GENERIC) + list(APPEND SOURCES shake256_256_internal_xmss_hashes.c) + endif() +endif() +if(XMSS_ENABLE_HASH_ABSTRACTION) + list(APPEND SOURCES + sha256_xmss_hashes.c + shake256_256_xmss_hashes.c ) - endif() - -add_library(xmss_hashes STATIC +list(APPEND SOURCES + errors.c + rand_hash.c xmss_hashes.c - xmss_hashes.h -) -target_include_directories(xmss_hashes - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} + xmss_ltree.c + wotsp.c + wotsp_verification.c + utils.c + verification.c + version.c ) -if (XMSS_ENABLE_SHA256) - target_link_libraries(xmss_hashes PUBLIC sha256) -endif() -if (XMSS_ENABLE_SHAKE256_256) - target_link_libraries(xmss_hashes PUBLIC shake256_256) +if(XMSS_ENABLE_SIGNING) + list(APPEND SOURCES + zeroize.c + wotsp_signing.c + xmss_tree.c + index_permutation.c + signing.c + ) endif() -if(XMSS_HASH_OVERRIDE_SOURCES) - # Compile needed voor override(s). - add_library(hash_overrides STATIC ${XMSS_HASH_OVERRIDE_SOURCES}) - target_include_directories(hash_overrides - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} - ) - target_link_libraries(hash_overrides - PUBLIC xmss_headers - ) - if(XMSS_HASH_OVERRIDE_LINK_LIBRARIES) - # Link needed as well. - target_link_libraries(hash_overrides PUBLIC ${XMSS_HASH_OVERRIDE_LINK_LIBRARIES}) +# This target verifies that the source files *could* be compiled individually. +add_library(sources_sanity OBJECT ${SOURCES}) +target_link_libraries(sources_sanity + PRIVATE xmss_headers + PRIVATE config +) + +if(BUILD_SHARED_LIBS) + # These are used when building a Windows DLL; they are no-ops for other platforms. + configure_file(version.in.rc version.rc NO_SOURCE_PERMISSIONS) + list(APPEND ADDITIONAL_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + if(XMSS_ENABLE_SIGNING) + list(APPEND ADDITIONAL_SOURCES libxmss.def) + else() + list(APPEND ADDITIONAL_SOURCES libxmss_no_signing.def) endif() - target_link_libraries(xmss_hashes PUBLIC hash_overrides) -elseif(XMSS_HASH_OVERRIDE_LINK_LIBRARIES) - # Link-only override(s) - target_link_libraries(xmss_hashes PUBLIC ${XMSS_HASH_OVERRIDE_LINK_LIBRARIES}) endif() -add_library(utils STATIC - utils.c - utils.h -) -target_include_directories(utils - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} -) -target_link_libraries(utils - PUBLIC xmss_headers +# Our actual library target amalgamates all sources into one; see comment in libxmss.c for rationale. +add_library(xmss + libxmss.c + ${XMSS_HASH_OVERRIDE_SOURCES} + ${ADDITIONAL_SOURCES} ) -add_library(xmss_signing STATIC - signing.c - xmss_tree.c - index_permutation.c - private.h -) -target_link_libraries(xmss_signing - PUBLIC xmss_headers - PRIVATE utils - PRIVATE xmss_hashes - PRIVATE wotsp -) -add_library(xmss_verification STATIC - verification.c - private.h -) -target_link_libraries(xmss_verification - PUBLIC xmss_headers - PRIVATE xmss_hashes - PRIVATE wotsp -) -add_library(wotsp STATIC - wotsp.c - wotsp.h -) -target_include_directories(wotsp - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_CURRENT_BINARY_DIR} -) -target_link_libraries(wotsp +set_target_properties(xmss PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) +if(BUILD_SHARED_LIBS AND MSVC) + # According to CMake documentation (https://cmake.org/cmake/help/latest/prop_tgt/VERSION.html), this should be set + # automatically. But it is not (cmake version 3.30.2), so we set it explicitly. + target_link_options(xmss PRIVATE "/VERSION:${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") + # Prevent the default inclusion of a manifest resource, which is intended for .NET assembly DLLs only. + target_link_options(xmss PRIVATE "/MANIFEST:NO") +endif() +target_link_libraries(xmss PUBLIC xmss_headers + PRIVATE config + PUBLIC ${XMSS_HASH_OVERRIDE_LINK_LIBRARIES} ) +install(TARGETS xmss) diff --git a/src/compat_stdatomic.h b/src/compat_stdatomic.h index e53153f..c156a79 100644 --- a/src/compat_stdatomic.h +++ b/src/compat_stdatomic.h @@ -27,10 +27,12 @@ * @details * Atomics did not exist before C11; even in C11 support is optional. * When supported, _Atomic is a keyword and no header inclusion is required. However, within the library source code, - * we require that compat_stdatomic.h is included, so this compatibility macro is defined as a no-op in case - * C11 _Atomic is not supported. + * we require that compat_stdatomic.h is included. Note that we cannot simply define _Atomic itself, as the namespace + * (underscore) is reserved. Besides, some implementations (e.g., newlib) already define _Atomic in a non-standard way. */ -# define _Atomic +# define ATOMIC +#else +# define ATOMIC _Atomic #endif #if ((__STDC_VERSION__ < 201112L) || defined(__STDC_NO_ATOMICS__)) && !defined(DOXYGEN) diff --git a/src/config.in.h b/src/config.in.h index 26aed65..3d97170 100644 --- a/src/config.in.h +++ b/src/config.in.h @@ -35,6 +35,18 @@ */ #cmakedefine01 XMSS_ENABLE_SHA256 +/** + * @brief + * Indicates whether the SHA-256 default implementation is enabled. + * + * @details + * This macro is defined with the value 0 if you compile the library with + * ``` + * cmake -DXMSS_SHA256={Disabled | OverrideInternal | OverrideGeneric} + * ``` + */ +#cmakedefine01 XMSS_ENABLE_SHA256_DEFAULT + /** * @brief * Indicates whether SHA-256 uses the generic interface. @@ -60,6 +72,18 @@ */ #cmakedefine01 XMSS_ENABLE_SHAKE256_256 +/** + * @brief + * Indicates whether the SHAKE256/256 default implementation is enabled. + * + * @details + * This macro is defined with the value 0 if you compile the library with + * ``` + * cmake -DXMSS_SHAKE256_256={Disabled | OverrideInternal | OverrideGeneric} + * ``` + */ +#cmakedefine01 XMSS_ENABLE_SHAKE256_256_DEFAULT + /** * @brief * Indicates whether SHAKE256/256 uses the generic interface. diff --git a/src/endianness.h b/src/endianness.h index 3eb010a..84af989 100644 --- a/src/endianness.h +++ b/src/endianness.h @@ -37,7 +37,7 @@ * @param[in] src The big-endian source byte stream. * @param[in] count The number of uint32_t in dst. */ -static inline void big_endian_to_native(uint32_t *restrict dst, const uint8_t *restrict src, uint_fast16_t count) +static inline void big_endian_to_native(uint32_t *dst, const uint8_t *src, uint_fast32_t count) { for (; count > 0; --count, ++dst, src += 4) { *dst = ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | ((uint32_t)src[2] << 8) | src[3]; @@ -53,7 +53,7 @@ static inline void big_endian_to_native(uint32_t *restrict dst, const uint8_t *r * @param[out] dst The destination native value. * @param[in] src The big-endian source value byte stream. */ -static inline void big_endian_to_native_256(XmssNativeValue256 *restrict dst, const XmssValue256 *restrict src) +static inline void big_endian_to_native_256(XmssNativeValue256 *dst, const XmssValue256 *src) { big_endian_to_native(dst->data, src->data, XMSS_VALUE_256_WORDS); } @@ -72,7 +72,7 @@ static inline void big_endian_to_native_256(XmssNativeValue256 *restrict dst, co * @param[in,out] data The buffer containing a big-endian byte stream on input, and a native uint32_t array on output. * @param[in] count The number of uint32_t in buf. */ -static inline void inplace_big_endian_to_native(uint32_t *data, uint_fast16_t count) +static inline void inplace_big_endian_to_native(uint32_t *data, uint_fast32_t count) { for (; count > 0; --count, ++data) { const uint8_t *const bytes = (const uint8_t *)data; @@ -102,7 +102,7 @@ static inline void inplace_big_endian_to_native_256(XmssNativeValue256 *value) * @param[in,out] data The buffer containing a native uint32_t array on input, and a big-endian byte stream on output. * @param[in] count The number of uint32_t in buf. */ -static inline void inplace_native_to_big_endian(uint32_t *data, uint_fast16_t count) +static inline void inplace_native_to_big_endian(uint32_t *data, uint_fast32_t count) { /* The operation is the same in either direction, so use the native to big-endian implementation. */ inplace_big_endian_to_native(data, count); @@ -137,7 +137,7 @@ static inline void inplace_native_to_big_endian_256(XmssNativeValue256 *value) * @param[in] src The native uint32_t source array. * @param[in] count The number of uint32_t in src. */ -static inline void native_to_big_endian(uint8_t *restrict dst, const uint32_t *restrict src, uint_fast16_t count) +static inline void native_to_big_endian(uint8_t *dst, const uint32_t *src, uint_fast32_t count) { for (; count; --count, dst += 4, ++src) { dst[0] = (uint8_t)(*src >> 24); @@ -156,7 +156,7 @@ static inline void native_to_big_endian(uint8_t *restrict dst, const uint32_t *r * @param[out] dst The destination big-endian byte stream. * @param[in] src The native source value. */ -static inline void native_to_big_endian_256(XmssValue256 *restrict dst, const XmssNativeValue256 *restrict src) +static inline void native_to_big_endian_256(XmssValue256 *const dst, const XmssNativeValue256 *const src) { native_to_big_endian(dst->data, src->data, XMSS_VALUE_256_WORDS); } diff --git a/src/errors.c b/src/errors.c new file mode 100644 index 0000000..a4627c8 --- /dev/null +++ b/src/errors.c @@ -0,0 +1,97 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Thomas Schaap + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Public API for XMSS error handling. + */ + +#include "errors.h" + +const char *xmss_error_to_name(const XmssError error) +{ + switch (error) { + case XMSS_OKAY: + return "XMSS_OKAY"; + case XMSS_ERR_NULL_POINTER: + return "XMSS_ERR_NULL_POINTER"; + case XMSS_ERR_INVALID_SIGNATURE: + return "XMSS_ERR_INVALID_SIGNATURE"; + case XMSS_ERR_ARGUMENT_MISMATCH: + return "XMSS_ERR_ARGUMENT_MISMATCH"; + case XMSS_ERR_ALLOC_ERROR: + return "XMSS_ERR_ALLOC_ERROR"; + case XMSS_ERR_INVALID_BLOB: + return "XMSS_ERR_INVALID_BLOB"; + case XMSS_ERR_BAD_CONTEXT: + return "XMSS_ERR_BAD_CONTEXT"; + case XMSS_ERR_INVALID_ARGUMENT: + return "XMSS_ERR_INVALID_ARGUMENT"; + case XMSS_ERR_PARTITION_DONE: + return "XMSS_ERR_PARTITION_DONE"; + case XMSS_ERR_UNFINISHED_PARTITIONS: + return "XMSS_ERR_UNFINISHED_PARTITIONS"; + case XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE: + return "XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE"; + case XMSS_ERR_PARTITIONS_NOT_CONSECUTIVE: + return "XMSS_ERR_PARTITIONS_NOT_CONSECUTIVE"; + case XMSS_ERR_NO_PUBLIC_KEY: + return "XMSS_ERR_NO_PUBLIC_KEY"; + case XMSS_ERR_FAULT_DETECTED: + return "XMSS_ERR_FAULT_DETECTED"; + case XMSS_UNINITIALIZED: + return "XMSS_UNINITIALIZED"; + default: + return "XmssError_Undefined"; + } +} + +const char *xmss_error_to_description(const XmssError error) +{ + /* NOTE to developers + * ================== + * + * Keep the returned strings synchronized with the Doxygen descriptions for the XmssError enum values. + */ + + switch (error) { + case XMSS_OKAY: + return "Success"; + case XMSS_ERR_NULL_POINTER: + return "An unexpected NULL pointer was passed"; + case XMSS_ERR_INVALID_SIGNATURE: + return "The signature is invalid"; + case XMSS_ERR_ARGUMENT_MISMATCH: + return "A mismatch was detected between arguments"; + case XMSS_ERR_ALLOC_ERROR: + return "An error occurred with memory allocation"; + case XMSS_ERR_INVALID_BLOB: + return "A blob structure was found to be invalid"; + case XMSS_ERR_BAD_CONTEXT: + return "The passed context is in an incorrect state"; + case XMSS_ERR_INVALID_ARGUMENT: + return "The value of an argument was invalid"; + case XMSS_ERR_PARTITION_DONE: + return "The calculations for the key generation partition were already performed"; + case XMSS_ERR_UNFINISHED_PARTITIONS: + return "Not all key generation partition calculations were completed"; + case XMSS_ERR_TOO_FEW_SIGNATURES_AVAILABLE: + return "There are not enough signatures available to allow the operation"; + case XMSS_ERR_PARTITIONS_NOT_CONSECUTIVE: + return "Partitions are not consecutive"; + case XMSS_ERR_NO_PUBLIC_KEY: + return "The key context does not have a public key loaded"; + case XMSS_ERR_FAULT_DETECTED: + return "A fault was detected"; + case XMSS_UNINITIALIZED: + return "Function returned prematurely"; + default: + return "Invalid error code"; + } +} diff --git a/src/fault_detection_helpers.h b/src/fault_detection_helpers.h new file mode 100644 index 0000000..96a938c --- /dev/null +++ b/src/fault_detection_helpers.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * Helper macros to detect accidental or induced faults. + */ + +#pragma once + +#ifndef XMSS_FAULT_DETECTION_HELPERS_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_FAULT_DETECTION_HELPERS_H_INCLUDED + +/** + * @brief + * Make the function that uses the macro return return_value if condition is true. + * + * @details + * The condition is checked twice to ensure that a fault cannot cause the return to be skipped. To ensure that the + * compiler cannot optimize the second if-statement away, the condition should involve a volatile variable. + * + * @param[in] condition The condition to check, should involve a volatile variable. + * @param[in] return_value Value for the function to return if the condition is true. + */ +#define REDUNDANT_RETURN_IF(condition, return_value) \ + { \ + if (condition) { \ + return return_value; \ + } \ + if (condition) { \ + return return_value; \ + } \ + } + +/** + * @brief + * This macro checks if result is XMSS_OKAY. If not, it makes the function that uses the macro return result. + * + * @details + * This macro checks result multiple times for fault resilience. To make sure that the compiler cannot optimize + * away the redundant checks, result should be a volatile variable. If a fault causes the program to end up in the + * result != XMSS_OKAY arm when actually result == XMSS_OKAY, or when the first if-statement is skipped even though + * result != XMSS_OKAY, XMSS_ERR_FAULT_DETECTED is returned. + * + * @param[in] result XmssError to check. Must be stored in a volatile variable. +*/ +#define REDUNDANT_RETURN_ERR(result) \ + { \ + if ((result) != XMSS_OKAY) { \ + if ((result) == XMSS_OKAY) { \ + return XMSS_ERR_FAULT_DETECTED; \ + } \ + return result; \ + } \ + if ((result) != XMSS_OKAY) { \ + return XMSS_ERR_FAULT_DETECTED; \ + } \ + } + +#endif /* !XMSS_FAULT_DETECTION_HELPERS_H_INCLUDED */ diff --git a/src/generic_xmss_hashes.h b/src/generic_xmss_hashes.h index 2486f12..4694e7a 100644 --- a/src/generic_xmss_hashes.h +++ b/src/generic_xmss_hashes.h @@ -25,7 +25,7 @@ #include "xmss_hashes_base.h" /** - * @copydoc prototype_digest + * @copydoc prototype_F * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. @@ -35,72 +35,97 @@ * @details * This is the specialization using the generic interface. */ -static inline void generic_digest(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssValue256 *restrict const digest, const uint8_t *restrict const message, - const size_t message_length) +static inline void generic_F(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssNativeValue256 *const native_digest, const Input_F *const input) { void *context = init(); - update(context, message, message_length); - finalize(context, digest); + uint8_t input_big_endian[96]; + native_to_big_endian(input_big_endian, (const uint32_t *)input, 24); + update(context, input_big_endian, 96); + finalize(context, (XmssValue256 *)native_digest); + inplace_big_endian_to_native_256(native_digest); } /** - * @copydoc prototype_native_digest + * @copydoc prototype_H * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. * - * @see prototype_native_digest + * @see prototype_digest * * @details * This is the specialization using the generic interface. */ -static inline void generic_native_digest(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const uint32_t *restrict words, size_t word_count) +static inline void generic_H(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssNativeValue256 *const native_digest, const Input_H *const input) { void *context = init(); - /* Optimized for SHA-256 block size, but also works for SHAKE256/256. */ - uint8_t block[64]; - /* full blocks */ - while (word_count >= 16) { - native_to_big_endian(block, words, 16); - update(context, block, 64); - word_count -= 16; - words += 16; - } - /* remainder */ - native_to_big_endian(block, words, word_count); - update(context, block, word_count * 4); + uint8_t input_big_endian[128]; + native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); + update(context, input_big_endian, 128); finalize(context, (XmssValue256 *)native_digest); inplace_big_endian_to_native_256(native_digest); } /** - * @copydoc prototype_F + * @copydoc prototype_H_msg_init * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. + * + * @see prototype_H_msg_init + * + * @details + * This is the specialization using the generic interface. + */ +static inline void generic_H_msg_init(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + XmssHMsgCtx *const ctx, const Input_H_msg *const input) +{ + ctx->generic_ctx = init(); + uint8_t input_big_endian[128]; + native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); + update(ctx->generic_ctx, input_big_endian, 128); +} + +/** + * @copydoc prototype_H_msg_update + * @param[in] update The update function for the digest. + * + * @see prototype_H_msg_update + * + * @details + * This is the specialization using the generic interface. + */ +static inline void generic_H_msg_update(const XmssGenericDigestUpdate update, XmssHMsgCtx *const ctx, + const uint8_t *const part, const size_t part_length, const uint8_t *volatile *const part_verify) +{ + const uint8_t *volatile const volatile_part = part; + update(ctx->generic_ctx, volatile_part, part_length); + if (part_verify != NULL) + { + *part_verify = volatile_part; + } +} + +/** + * @copydoc prototype_H_msg_finalize * @param[in] finalize The finalize function for the digest. * - * @see prototype_digest + * @see prototype_H_msg_finalize * * @details * This is the specialization using the generic interface. */ -static inline void generic_F(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const Input_F *restrict const input) +static inline void generic_H_msg_finalize(const XmssGenericDigestFinalize finalize, + XmssNativeValue256 *const native_digest, XmssHMsgCtx *const ctx) { - void *context = init(); - uint8_t input_big_endian[96]; - native_to_big_endian(input_big_endian, (const uint32_t *)input, 24); - update(context, input_big_endian, 96); - finalize(context, (XmssValue256 *)native_digest); + /* We place the big-endian digest in native_digest, then reorder it in-place. */ + finalize(ctx->generic_ctx, (XmssValue256 *)native_digest); inplace_big_endian_to_native_256(native_digest); } /** - * @copydoc prototype_H + * @copydoc prototype_PRF * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. @@ -110,20 +135,21 @@ static inline void generic_F(XmssGenericDigestInit init, XmssGenericDigestUpdate * @details * This is the specialization using the generic interface. */ -static inline void generic_H(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const Input_H *restrict const input) +static inline void generic_PRF(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssNativeValue256 *const native_digest, const Input_PRF *const input) { void *context = init(); - uint8_t input_big_endian[128]; - native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); - update(context, input_big_endian, 128); + uint8_t input_big_endian[96]; + native_to_big_endian(input_big_endian, (const uint32_t *)input, 24); + update(context, input_big_endian, 96); finalize(context, (XmssValue256 *)native_digest); inplace_big_endian_to_native_256(native_digest); } +#if XMSS_ENABLE_SIGNING + /** - * @copydoc prototype_H_msg + * @copydoc prototype_PRFkeygen * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. @@ -133,21 +159,20 @@ static inline void generic_H(XmssGenericDigestInit init, XmssGenericDigestUpdate * @details * This is the specialization using the generic interface. */ -static inline void generic_H_msg(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const Input_H_msg *restrict const input, const uint8_t *restrict const message, const size_t message_length) +static inline void generic_PRFkeygen(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssNativeValue256 *const native_digest, + const Input_PRFkeygen *const input) { void *context = init(); uint8_t input_big_endian[128]; native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); update(context, input_big_endian, 128); - update(context, message, message_length); finalize(context, (XmssValue256 *)native_digest); inplace_big_endian_to_native_256(native_digest); } /** - * @copydoc prototype_PRF + * @copydoc prototype_PRFindex * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. @@ -157,20 +182,20 @@ static inline void generic_H_msg(XmssGenericDigestInit init, XmssGenericDigestUp * @details * This is the specialization using the generic interface. */ -static inline void generic_PRF(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const Input_PRF *restrict const input) +static inline void generic_PRFindex(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssNativeValue256 *const native_digest, + const Input_PRFindex *const input) { void *context = init(); - uint8_t input_big_endian[96]; - native_to_big_endian(input_big_endian, (const uint32_t *)input, 24); - update(context, input_big_endian, 96); + uint8_t input_big_endian[128]; + native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); + update(context, input_big_endian, 128); finalize(context, (XmssValue256 *)native_digest); inplace_big_endian_to_native_256(native_digest); } /** - * @copydoc prototype_PRFkeygen + * @copydoc prototype_digest * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. @@ -180,39 +205,47 @@ static inline void generic_PRF(XmssGenericDigestInit init, XmssGenericDigestUpda * @details * This is the specialization using the generic interface. */ -static inline void generic_PRFkeygen(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const Input_PRFkeygen *restrict const input) +static inline void generic_digest(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssValue256 *const digest, const uint8_t *const message, + const size_t message_length) { void *context = init(); - uint8_t input_big_endian[128]; - native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); - update(context, input_big_endian, 128); - finalize(context, (XmssValue256 *)native_digest); - inplace_big_endian_to_native_256(native_digest); + update(context, message, message_length); + finalize(context, digest); } /** - * @copydoc prototype_PRFindex + * @copydoc prototype_native_digest * @param[in] init The initialize function for the digest. * @param[in] update The update function for the digest. * @param[in] finalize The finalize function for the digest. * - * @see prototype_digest + * @see prototype_native_digest * * @details * This is the specialization using the generic interface. */ -static inline void generic_PRFindex(XmssGenericDigestInit init, XmssGenericDigestUpdate update, - XmssGenericDigestFinalize finalize, XmssNativeValue256 *restrict const native_digest, - const Input_PRFindex *restrict const input) +static inline void generic_native_digest(const XmssGenericDigestInit init, const XmssGenericDigestUpdate update, + const XmssGenericDigestFinalize finalize, XmssNativeValue256 *const native_digest, const uint32_t *words, + size_t word_count) { void *context = init(); - uint8_t input_big_endian[128]; - native_to_big_endian(input_big_endian, (const uint32_t *)input, 32); - update(context, input_big_endian, 128); + /* Optimized for SHA-256 block size, but also works for SHAKE256/256. */ + uint8_t block[64]; + /* full blocks */ + while (word_count >= 16) { + native_to_big_endian(block, words, 16); + update(context, block, 64); + word_count -= 16; + words += 16; + } + /* remainder */ + native_to_big_endian(block, words, word_count); + update(context, block, word_count * 4); finalize(context, (XmssValue256 *)native_digest); inplace_big_endian_to_native_256(native_digest); } +#endif /* XMSS_ENABLE_SIGNING */ + #endif /* !XMSS_GENERIC_XMSS_HASHES_H_INCLUDED */ diff --git a/src/index_permutation.c b/src/index_permutation.c index ab3c1f5..55b4993 100644 --- a/src/index_permutation.c +++ b/src/index_permutation.c @@ -17,14 +17,14 @@ #include "xmss_config.h" #include "endianness.h" -#include "private.h" +#include "signing_private.h" #include "wotsp.h" /** * @brief * The context structure for the index pseudo random number generation. */ -typedef struct { +typedef struct ObfuscationRandomContext { /** * @brief * The input to the digest function used as a deterministic random generator. @@ -74,9 +74,8 @@ typedef struct { * @param[in] range The range from which to generate a selection, must be in [1, UINT32_MAX]. * @returns a value in the range of [0, range - 1]. */ -static uint32_t deterministic_selection( - HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - ObfuscationRandomContext *const rng_context, const uint32_t range) +static uint32_t deterministic_selection(HASH_FUNCTIONS_PARAMETER ObfuscationRandomContext *const rng_context, + const uint32_t range) { /* Initialize the result to an out-of-band value. */ uint32_t result = range; @@ -91,7 +90,7 @@ static uint32_t deterministic_selection( while (1) { if (rng_context->random_pool_word_size == 0) { - xmss_PRFindex(HASH_ABSTRACTION(hash_functions) &rng_context->random_pool, &rng_context->prf_index_input); + xmss_PRFindex(HASH_FUNCTIONS &rng_context->random_pool, &rng_context->prf_index_input); rng_context->prf_index_input.drbg_counter++; rng_context->random_pool_word_size = sizeof(XmssNativeValue256) / sizeof(uint32_t); } @@ -106,9 +105,8 @@ static uint32_t deterministic_selection( } } -XmssError generate_pseudorandom_permutation( - HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - uint32_t *permutation, const uint32_t num_elements, const XmssNativeValue256 *const index_permutation_seed, +XmssError generate_pseudorandom_permutation(HASH_FUNCTIONS_PARAMETER uint32_t *const permutation, + const uint32_t num_elements, const XmssNativeValue256 *const index_permutation_seed, const XmssNativeValue256 *const SEED) { const uint32_t max_index = num_elements - 1; @@ -119,8 +117,8 @@ XmssError generate_pseudorandom_permutation( return XMSS_ERR_NULL_POINTER; } - memcpy(rng_context.prf_index_input.S_INDEX.data, index_permutation_seed, sizeof(XmssNativeValue256)); - memcpy(rng_context.prf_index_input.SEED.data, SEED, sizeof(XmssNativeValue256)); + rng_context.prf_index_input.S_INDEX = *index_permutation_seed; + rng_context.prf_index_input.SEED = *SEED; /* Populate the initial non-permuted index space. */ for (uint32_t i = 0; i < num_elements; i++) { @@ -130,7 +128,7 @@ XmssError generate_pseudorandom_permutation( for (uint32_t i = 0; i < max_index; i++) { uint32_t tmp = permutation[max_index - i]; /* select one value in range [0, max_index - i]. */ - uint32_t selection = deterministic_selection(HASH_ABSTRACTION(hash_functions) &rng_context, max_index - i + 1); + uint32_t selection = deterministic_selection(HASH_FUNCTIONS &rng_context, max_index - i + 1); permutation[max_index - i] = permutation[selection]; permutation[selection] = tmp; } diff --git a/src/index_permutation.h b/src/index_permutation.h index f81af6e..29cca7f 100644 --- a/src/index_permutation.h +++ b/src/index_permutation.h @@ -20,13 +20,13 @@ #include "xmss_config.h" #include "compat.h" +#include "libxmss.h" #include "opaque_structures.h" +#include "signing_private.h" #include "structures.h" #include "types.h" #include "xmss_hashes.h" -#include "private.h" - /** * @brief * Generate a pseudo-random permutation. @@ -44,9 +44,8 @@ * @retval XMSS_OKAY The permutation was successfully generated. * @retval XMSS_ERR_NULL_POINTER permutation, index_permutation_seed or SEED was NULL. */ -XmssError generate_pseudorandom_permutation( - HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - uint32_t *permutation, uint32_t num_elements, const XmssNativeValue256 *index_permutation_seed, - const XmssNativeValue256 *SEED); +LIBXMSS_STATIC +XmssError generate_pseudorandom_permutation(HASH_FUNCTIONS_PARAMETER uint32_t *permutation, + uint32_t num_elements, const XmssNativeValue256 *index_permutation_seed, const XmssNativeValue256 *SEED); #endif /* !XMSS_INDEX_PERMUTATION_H_INCLUDED */ diff --git a/src/libxmss.c b/src/libxmss.c new file mode 100644 index 0000000..f7aef03 --- /dev/null +++ b/src/libxmss.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Amalgamated source of the entire library. + * + * @details + * This source file includes all the library source files while LIBXMSS=1 is defined. + * This ensures that: + * + * - All library internals with "external linkage" are actually static (i.e., they are not external after all). + * Hence, they do not show up in the external symbols of the resulting library. + * - Compilers that do not use link time optimization (LTO) have an opportunity to optimize code spread out over + * multiple .c files. + * - We do not have to use weird namespaces such as `_xmss` or `__xmss` for non-API external symbols. + */ + +#ifndef DOXYGEN + +#define LIBXMSS 1 +#include "libxmss.h" + +#include "config.h" + +/* + * Note that we do not explicitly include the following source files: + * + * sha256_internal_H0.c + * sha256_xmss_hashes.c + * shake256_256_xmss_hashes.c + * + * These define data, which for our amalgamated library source needs to be static. Therefore, these files are included + * by their corresponding header, so the data gets defined at first inclusion. The reason is that static data cannot be + * forward-declared. + */ + +#if XMSS_ENABLE_SHA256 +# if XMSS_ENABLE_SHA256_DEFAULT +# include "sha256_internal_default.c" +# endif +# if !XMSS_ENABLE_SHA256_GENERIC +# include "sha256_internal_xmss_hashes.c" +# endif +#endif + +#if XMSS_ENABLE_SHAKE256_256 +# if XMSS_ENABLE_SHAKE256_256_DEFAULT + # include "shake256_256_internal_default.c" +# endif +# if !XMSS_ENABLE_SHAKE256_256_GENERIC + # include "shake256_256_internal_xmss_hashes.c" +# endif +#endif + +#include "xmss_hashes.c" +#include "utils.c" +#include "rand_hash.c" +#include "xmss_ltree.c" +#include "wotsp.c" +#include "wotsp_verification.c" +#include "verification.c" +#include "version.c" +#include "errors.c" + +#if XMSS_ENABLE_SIGNING +# include "zeroize.c" +# include "wotsp_signing.c" +# include "xmss_tree.c" +# include "index_permutation.c" +# include "signing.c" +#endif + +#endif /* !DOXYGEN */ diff --git a/src/libxmss.def b/src/libxmss.def new file mode 100644 index 0000000..8595f50 --- /dev/null +++ b/src/libxmss.def @@ -0,0 +1,35 @@ +; SPDX-FileCopyrightText: 2023 Fox Crypto B.V. +; SPDX-License-Identifier: MIT + +; This file lists all exported functions in the resulting Windows DLL when compiled with +; XMSS_ENABLE_SIGNING=ON (default) and BUILD_SHARED_LIBS=ON. + +EXPORTS + xmss_error_to_description + xmss_error_to_name + xmss_library_get_version + xmss_verification_check + xmss_verification_init + xmss_verification_update + + xmss_calculate_public_key_part + xmss_context_initialize + xmss_export_public_key + xmss_finish_calculate_public_key + xmss_free_key_context + xmss_free_key_generation_context + xmss_free_signing_context + xmss_generate_private_key + xmss_generate_public_key + xmss_get_caching_in_public_key + xmss_get_signature_count + xmss_load_private_key + xmss_load_public_key + xmss_merge_signature_space + xmss_partition_signature_space + xmss_request_future_signatures + xmss_sign_message + xmss_verify_exported_public_key + xmss_verify_public_key + xmss_verify_private_key_stateless + xmss_verify_private_key_stateful diff --git a/src/libxmss.h b/src/libxmss.h new file mode 100644 index 0000000..39d98d5 --- /dev/null +++ b/src/libxmss.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Helpers to hide symbols in the resulting library with external linkage that are not part of the public API. + * + * @details + * These are implementation details that should *not* be considered stable. + */ + +#pragma once + +#ifndef XMSS_LIBXMSS_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_LIBXMSS_H_INCLUDED + +#if !defined(LIBXMSS) || !LIBXMSS +# undef LIBXMSS + /** + * @brief + * When compiling the amalgamated library (libxmss.c), this macro is defined as 1 (true); it is 0 (false) otherwise. + */ +# define LIBXMSS 0 + /** + * @brief + * Helper macro to hide symbols with external linkage that are not part of the public API. + * + * @details + * When compiling the amalgamated library (libxmss.c), this macro is defined as `static`, such that library + * internals do not end up as external symbols. + */ +# define LIBXMSS_STATIC +#else +# undef LIBXMSS +# define LIBXMSS 1 +# define LIBXMSS_STATIC static +#endif + +#endif /* !XMSS_LIBXMSS_H_INCLUDED */ diff --git a/src/libxmss_no_signing.def b/src/libxmss_no_signing.def new file mode 100644 index 0000000..66559d2 --- /dev/null +++ b/src/libxmss_no_signing.def @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2023 Fox Crypto B.V. +; SPDX-License-Identifier: MIT + +; This file lists all exported functions in the resulting Windows DLL when compiled with +; XMSS_ENABLE_SIGNING=OFF and BUILD_SHARED_LIBS=ON. + +EXPORTS + xmss_error_to_description + xmss_error_to_name + xmss_library_get_version + xmss_verification_check + xmss_verification_init + xmss_verification_update diff --git a/src/rand_hash.c b/src/rand_hash.c new file mode 100644 index 0000000..c2d2dc3 --- /dev/null +++ b/src/rand_hash.c @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Pepijn Westen + */ + +/** + * @file + * @brief + * XMSS tree hashing. + */ + +#include + +#include "rand_hash.h" + + +void rand_hash(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const digest_out, Input_PRF *const rand_hash_state, + const XmssNativeValue256 *const left, const XmssNativeValue256 *const right) +{ + Input_H input_h = INIT_INPUT_H; + + assert(digest_out != NULL); + assert(rand_hash_state != NULL); + assert(left != NULL); + assert(right != NULL); + ASSERT_HASH_FUNCTIONS(); + + /* Compute KEY for H. */ + rand_hash_state->M.ADRS.typed.L_tree_Address.keyAndMask = 0; + xmss_PRF(HASH_FUNCTIONS &input_h.KEY, rand_hash_state); + + /* Compute and apply BM_0 to LEFT input node. */ + rand_hash_state->M.ADRS.typed.L_tree_Address.keyAndMask = 1; + xmss_PRF(HASH_FUNCTIONS &input_h.M[0], rand_hash_state); + for (size_t i = 0; i < XMSS_VALUE_256_WORDS; i++) { + input_h.M[0].data[i] ^= left->data[i]; + } + + /* Compute and apply BM_1 to RIGHT input node. */ + rand_hash_state->M.ADRS.typed.L_tree_Address.keyAndMask = 2; + xmss_PRF(HASH_FUNCTIONS &input_h.M[1], rand_hash_state); + for (size_t i = 0; i < XMSS_VALUE_256_WORDS; i++) { + input_h.M[1].data[i] ^= right->data[i]; + } + + /* Compute output */ + xmss_H(HASH_FUNCTIONS digest_out, &input_h); +} diff --git a/src/rand_hash.h b/src/rand_hash.h new file mode 100644 index 0000000..9e878b9 --- /dev/null +++ b/src/rand_hash.h @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Pepijn Westen + */ + +/** + * @file + * @brief + * XMSS randomized tree hashing primitive. + */ + +#pragma once + +#ifndef XMSS_XMSS_RAND_HASH_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_XMSS_RAND_HASH_H_INCLUDED + +#include "libxmss.h" +#include "xmss_hashes.h" + +/** + * @brief + * Implementation of Randomized Tree Hashing (RFC 8391 section 4.1.4). + * + * @warning The caller is responsible for providing valid pointers. For performance reasons these will not be checked. + * + * @param[in] hash_functions The hash functions to use. + * @param[out] digest_out The randomized hash digest. + * @param[in,out] rand_hash_state Structure containing the SEED and the ADRS for this operation. + * The keyAndMask of the address is written by this function. + * @param[in] left The left input node. May alias with digest_out but not with right. + * @param[in] right The right input node. May alias with digest_out but not with left. + */ +LIBXMSS_STATIC +void rand_hash(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *digest_out, Input_PRF *rand_hash_state, + const XmssNativeValue256 *left, const XmssNativeValue256 *right); + +#endif /* !XMSS_XMSS_RAND_HASH_H_INCLUDED */ diff --git a/src/sha256_generic_xmss_hashes.h b/src/sha256_generic_xmss_hashes.h index b970954..c37db64 100644 --- a/src/sha256_generic_xmss_hashes.h +++ b/src/sha256_generic_xmss_hashes.h @@ -34,68 +34,64 @@ #include "types.h" /** - * @copydoc prototype_digest - * @see prototype_digest + * @copydoc prototype_F + * @see prototype_F * * @details * This is the specialization for the SHA-256 algorithm using the generic interface. - * - * This function implements the SHA-256($M$) function as defined by NIST FIPS 180-4, Section 6.2. */ -static inline void sha256_digest(XmssValue256 *restrict digest, const uint8_t *restrict const message, - size_t message_length) +static inline void sha256_F(XmssNativeValue256 *const native_digest, const Input_F *const input) { - generic_digest(sha256_init, sha256_update, sha256_finalize, digest, message, message_length); + generic_F(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, native_digest, input); } /** - * @copydoc prototype_native_digest - * @see prototype_native_digest + * @copydoc prototype_H + * @see prototype_H * * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_native_digest(XmssNativeValue256 *restrict native_digest, - const uint32_t *restrict const words, size_t word_count) +static inline void sha256_H(XmssNativeValue256 *const native_digest, const Input_H *const input) { - generic_native_digest(sha256_init, sha256_update, sha256_finalize, native_digest, words, word_count); + generic_H(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, native_digest, input); } /** - * @copydoc prototype_F - * @see prototype_F + * @copydoc prototype_H_msg_init + * @see prototype_H_msg_init * * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_F(XmssNativeValue256 *restrict const native_digest, const Input_F *restrict const input) +static inline void sha256_H_msg_init(XmssHMsgCtx *const ctx, const Input_H_msg *const input) { - generic_F(sha256_init, sha256_update, sha256_finalize, native_digest, input); + generic_H_msg_init(xmss_sha256_init, xmss_sha256_update, ctx, input); } /** - * @copydoc prototype_H - * @see prototype_H + * @copydoc prototype_H_msg_update + * @see prototype_H_msg_update * * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_H(XmssNativeValue256 *restrict const native_digest, const Input_H *restrict const input) +static inline void sha256_H_msg_update(XmssHMsgCtx *const ctx, const uint8_t *const part, const size_t part_length, + const uint8_t *volatile *const part_verify) { - generic_H(sha256_init, sha256_update, sha256_finalize, native_digest, input); + generic_H_msg_update(xmss_sha256_update, ctx, part, part_length, part_verify); } /** - * @copydoc prototype_H_msg - * @see prototype_H_msg + * @copydoc prototype_H_msg_finalize + * @see prototype_H_msg_finalize * * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_H_msg(XmssNativeValue256 *restrict const native_digest, const Input_H_msg *restrict const input, - const uint8_t *restrict const message, const size_t message_length) +static inline void sha256_H_msg_finalize(XmssNativeValue256 *const native_digest, XmssHMsgCtx *const ctx) { - generic_H_msg(sha256_init, sha256_update, sha256_finalize, native_digest, input, message, message_length); + generic_H_msg_finalize(xmss_sha256_finalize, native_digest, ctx); } /** @@ -105,11 +101,13 @@ static inline void sha256_H_msg(XmssNativeValue256 *restrict const native_digest * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_PRF(XmssNativeValue256 *restrict const native_digest, const Input_PRF *restrict const input) +static inline void sha256_PRF(XmssNativeValue256 *const native_digest, const Input_PRF *const input) { - generic_PRF(sha256_init, sha256_update, sha256_finalize, native_digest, input); + generic_PRF(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, native_digest, input); } +#if XMSS_ENABLE_SIGNING + /** * @copydoc prototype_PRFkeygen * @see prototype_PRFkeygen @@ -117,10 +115,9 @@ static inline void sha256_PRF(XmssNativeValue256 *restrict const native_digest, * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_PRFkeygen(XmssNativeValue256 *restrict const native_digest, - const Input_PRFkeygen *restrict const input) +static inline void sha256_PRFkeygen(XmssNativeValue256 *const native_digest, const Input_PRFkeygen *const input) { - generic_PRFkeygen(sha256_init, sha256_update, sha256_finalize, native_digest, input); + generic_PRFkeygen(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, native_digest, input); } /** @@ -130,10 +127,38 @@ static inline void sha256_PRFkeygen(XmssNativeValue256 *restrict const native_di * @details * This is the specialization for the SHA-256 algorithm using the generic interface. */ -static inline void sha256_PRFindex(XmssNativeValue256 *restrict const native_digest, - const Input_PRFindex *restrict const input) +static inline void sha256_PRFindex(XmssNativeValue256 *const native_digest, const Input_PRFindex *const input) +{ + generic_PRFindex(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, native_digest, input); +} + +/** + * @copydoc prototype_digest + * @see prototype_digest + * + * @details + * This is the specialization for the SHA-256 algorithm using the generic interface. + * + * This function implements the SHA-256($M$) function as defined by NIST FIPS 180-4, Section 6.2. + */ +static inline void sha256_digest(XmssValue256 *const digest, const uint8_t *const message, const size_t message_length) { - generic_PRFindex(sha256_init, sha256_update, sha256_finalize, native_digest, input); + generic_digest(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, digest, message, message_length); } +/** + * @copydoc prototype_native_digest + * @see prototype_native_digest + * + * @details + * This is the specialization for the SHA-256 algorithm using the generic interface. + */ +static inline void sha256_native_digest(XmssNativeValue256 *const native_digest, const uint32_t *const words, + const size_t word_count) +{ + generic_native_digest(xmss_sha256_init, xmss_sha256_update, xmss_sha256_finalize, native_digest, words, word_count); +} + +#endif /* XMSS_ENABLE_SIGNING */ + #endif /* !XMSS_SHA256_GENERIC_XMSS_HASHES_H_INCLUDED */ diff --git a/src/sha256_internal_H0.c b/src/sha256_internal_H0.c new file mode 100644 index 0000000..9c974cd --- /dev/null +++ b/src/sha256_internal_H0.c @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * The initial state of SHA-256. + */ + +#include "config.h" + +#if !XMSS_ENABLE_SHA256 +# error "SHA-256 is disabled, so SHA-256 related source files must not be compiled." +#endif +#if XMSS_ENABLE_SHA256_GENERIC +# error "SHA-256 uses generic interface, so SHA-256 related internal source files must not be compiled." +#endif + +#include "sha256_internal_H0.h" + + +LIBXMSS_STATIC +const XmssNativeValue256 sha256_H0 = { { + 0x6a09e667, + 0xbb67ae85, + 0x3c6ef372, + 0xa54ff53a, + 0x510e527f, + 0x9b05688c, + 0x1f83d9ab, + 0x5be0cd19 +} }; diff --git a/src/sha256_internal_H0.h b/src/sha256_internal_H0.h new file mode 100644 index 0000000..a77a6f7 --- /dev/null +++ b/src/sha256_internal_H0.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * The initial state of SHA-256. + */ + +#pragma once + +#ifndef XMSS_SHA256_INTERNAL_H0_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_SHA256_INTERNAL_H0_H_INCLUDED + +#include "config.h" + +#if !XMSS_ENABLE_SHA256 +# error "SHA-256 is disabled, so SHA-256 related headers must not be included." +#endif +#if XMSS_ENABLE_SHA256_GENERIC +# error "SHA-256 uses generic interface, so SHA-256 related internal headers must not be included." +#endif + +#include "libxmss.h" +#include "types.h" + + +#if LIBXMSS + +/* + * It is not possible to forward-declare static data. + * For our amalgamated library source, we define the data right here and now. + */ +# include "sha256_internal_H0.c" + +#else + +/** + * @brief + * The initial native SHA-256 hash value as defined by the standard. + * + * @details + * See NIST FIPS 180-4, Section 5.3.3. + */ +extern const XmssNativeValue256 sha256_H0; + +#endif + +#endif /* !XMSS_SHA256_INTERNAL_H0_H_INCLUDED */ diff --git a/src/sha256_internal_default.c b/src/sha256_internal_default.c index 49e4407..c6fdffc 100644 --- a/src/sha256_internal_default.c +++ b/src/sha256_internal_default.c @@ -13,6 +13,8 @@ #include "config.h" +#include + #if !XMSS_ENABLE_SHA256 # error "SHA-256 is disabled, so SHA-256 related source files must not be compiled." #endif @@ -37,6 +39,14 @@ #include +#include "libxmss.h" +#if LIBXMSS +# include "types.h" + // Forward-declare our implementation as static before including the public header. + LIBXMSS_STATIC + void xmss_sha256_process_block(XmssNativeValue256 *Hi, const uint32_t *Mi); +#endif + #include "override_sha256_internal.h" /** @@ -210,8 +220,12 @@ static const uint32_t K[64] = { 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; -void sha256_process_block(XmssNativeValue256 *restrict const Hi, const uint32_t *restrict const Mi) +LIBXMSS_STATIC +void xmss_sha256_process_block(XmssNativeValue256 *const Hi, const uint32_t *const Mi) { + assert(Hi != NULL); + assert(Mi != NULL); + /* * See: NIST FIPS 180-4, Section 6.2.2. * @@ -265,3 +279,14 @@ void sha256_process_block(XmssNativeValue256 *restrict const Hi, const uint32_t Hi->data[j] += working_variables[j]; } } + +#ifndef DOXYGEN +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#endif diff --git a/src/sha256_internal_xmss_hashes.c b/src/sha256_internal_xmss_hashes.c index a020331..2dcd46f 100644 --- a/src/sha256_internal_xmss_hashes.c +++ b/src/sha256_internal_xmss_hashes.c @@ -26,128 +26,180 @@ #include "endianness.h" #include "override_sha256_internal.h" +#include "sha256_internal_H0.h" #include "xmss_hashes_base.h" -const XmssNativeValue256 sha256_H0 = { { - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19 -} }; - -void sha256_native_digest(XmssNativeValue256 *restrict native_digest, const uint32_t *restrict words, - size_t word_count) +void sha256_process_message_final(XmssNativeValue256 *const native_digest, const uint8_t *message, + size_t message_length, const uint64_t prefix_length) { /* See: NIST FIPS 180-4, Section 6.2 * * This function handles: * - NIST FIPS 180-4, Section 6.2.1: SHA-256 Preprocessing - * - NIST FIPS 180-4, Section 6.2.2: SHA-256 Hash Computation + * - NIST FIPS 180-4, Section 6.2.2: SHA-256 Hash Computation (outer loop) */ /* message block i = [1..N]; initialization postponed for performance reasons */ uint32_t Mi[TO_WORDS(SHA256_BLOCK_SIZE)]; /* index into Mi; initialization postponed for performance reasons */ - unsigned int t; + uint_fast8_t t; /* the total number of bytes in the message */ - uint64_t total = word_count * sizeof(uint32_t); - - /* See NIST FIPS 180-4, Section 6.2.1, Step 1 */ - native_256_copy(native_digest, &sha256_H0); + uint64_t total = prefix_length + message_length; /* a) First we handle the complete message blocks (64 bytes each) */ /* See NIST FIPS 180-4, Section 6.2.2, outer loop */ - for (; word_count >= TO_WORDS(SHA256_BLOCK_SIZE); - words += TO_WORDS(SHA256_BLOCK_SIZE), word_count -= TO_WORDS(SHA256_BLOCK_SIZE)) { - sha256_process_block(native_digest, words); + for (; message_length >= SHA256_BLOCK_SIZE; message += SHA256_BLOCK_SIZE, message_length -= SHA256_BLOCK_SIZE) { + big_endian_to_native(Mi, message, TO_WORDS(SHA256_BLOCK_SIZE)); + xmss_sha256_process_block(native_digest, Mi); } /* b) Next we handle the remainder (if any). */ - t = (unsigned int)(word_count); - memcpy(Mi, words, word_count * sizeof(uint32_t)); - Mi[t] = 0x80000000; + + /* b.1) First the multiples of uint32_t. */ + t = (uint_fast8_t)(message_length / sizeof(uint32_t)); + big_endian_to_native(Mi, message, t); + message += t * sizeof(uint32_t); + + /* b.2) Next the final uint32_t of the message including padding bit; see NIST FIPS 180-4, Section 5.1.1 */ + switch (message_length % sizeof(uint32_t)) { + case 0: + Mi[t] = 0x80000000; + break; + case 1: + Mi[t] = ((uint32_t)message[0] << 24) | 0x00800000; + break; + case 2: + Mi[t] = ((uint32_t)message[0] << 24) | ((uint32_t)message[1] << 16) | 0x00008000; + break; + case 3: + Mi[t] = ((uint32_t)message[0] << 24) | ((uint32_t)message[1] << 16) | ((uint32_t)message[2] << 8) | 0x00000080; + break; + } ++t; - /* We do not update words and word_count as they are no longer needed. */ + /* We do not update message and message_length as they are no longer needed. */ /* b.3) Add the total length; see NIST FIPS 180-4, Section 5.1.1. */ if (t > TO_WORDS(SHA256_BLOCK_SIZE) - sizeof(uint64_t) / sizeof(uint32_t)) { /* The length of the message must be stored in the last 64 bits of a block, so we need another block. */ memset(&Mi[t], 0, (TO_WORDS(SHA256_BLOCK_SIZE) - t) * sizeof(uint32_t)); - sha256_process_block(native_digest, Mi); + xmss_sha256_process_block(native_digest, Mi); t = 0; } - memset(&Mi[t], 0, (14 - t) * sizeof(uint32_t)); + memset(&Mi[t], 0, ((size_t)14 - t) * sizeof(uint32_t)); /* Note that 'total' represents number of bytes, whereas SHA-256 requires number of bits. */ Mi[14] = (uint32_t)(total >> 29); Mi[15] = (uint32_t)(total << 3); - sha256_process_block(native_digest, Mi); + xmss_sha256_process_block(native_digest, Mi); +} + + +void sha256_H_msg_update(XmssHMsgCtx *const ctx, const uint8_t *const part, const size_t part_length, + const uint8_t *volatile *const part_verify) +{ + const uint8_t *volatile const volatile_part = part; + size_t remaining_part_length = part_length; + size_t offset = 0; + + /* If there is a partial block in ctx, complete it. */ + if (ctx->sha256_ctx.bytes_in_partial_block.value > 0) { + size_t bytes_free_in_partial_block = SHA256_BLOCK_SIZE - ctx->sha256_ctx.bytes_in_partial_block.value; + + if (bytes_free_in_partial_block > part_length) { + /* We don't have enough new data for a whole block, so we add it all to the partial block. */ + memcpy(ctx->sha256_ctx.partial_block + ctx->sha256_ctx.bytes_in_partial_block.value, volatile_part + offset, + part_length); + ctx->sha256_ctx.bytes_in_partial_block.value += part_length; + if (part_verify != NULL) + { + *part_verify = volatile_part; + } + return; + } else { + /* Complete the partial block and update the intermediate hash in ctx. */ + memcpy(ctx->sha256_ctx.partial_block + ctx->sha256_ctx.bytes_in_partial_block.value, volatile_part + offset, + bytes_free_in_partial_block); + inplace_big_endian_to_native((uint32_t *)ctx->sha256_ctx.partial_block, + sizeof(ctx->sha256_ctx.partial_block) / sizeof(uint32_t)); + xmss_sha256_process_block(&ctx->sha256_ctx.intermediate_hash, (uint32_t *)ctx->sha256_ctx.partial_block); + ctx->sha256_ctx.bytes_hashed += SHA256_BLOCK_SIZE; + ctx->sha256_ctx.bytes_in_partial_block.value = 0; + remaining_part_length -= bytes_free_in_partial_block; + offset += bytes_free_in_partial_block; + } + } + + /* Process whole blocks in the input. */ + while (remaining_part_length >= SHA256_BLOCK_SIZE) { + /* The partial_block in ctx is empty right now, so let's reuse the memory. */ + uint32_t *block = (uint32_t *)ctx->sha256_ctx.partial_block; + big_endian_to_native(block, volatile_part + offset, TO_WORDS(SHA256_BLOCK_SIZE)); + xmss_sha256_process_block(&ctx->sha256_ctx.intermediate_hash, block); + ctx->sha256_ctx.bytes_hashed += SHA256_BLOCK_SIZE; + remaining_part_length -= SHA256_BLOCK_SIZE; + offset += SHA256_BLOCK_SIZE; + } + + /* Put the remainder as a partial block in ctx. */ + if (remaining_part_length > 0) { + ctx->sha256_ctx.bytes_in_partial_block.value = remaining_part_length; + memcpy(ctx->sha256_ctx.partial_block, volatile_part + offset, remaining_part_length); + } + + if (part_verify != NULL) + { + *part_verify = volatile_part; + } } -void sha256_process_message_final(XmssNativeValue256 *restrict const native_digest, const uint8_t *restrict message, - size_t message_length, const uint_fast16_t prefix_length) +#if XMSS_ENABLE_SIGNING + +void sha256_native_digest(XmssNativeValue256 *native_digest, const uint32_t *words, size_t word_count) { /* See: NIST FIPS 180-4, Section 6.2 * * This function handles: * - NIST FIPS 180-4, Section 6.2.1: SHA-256 Preprocessing - * - NIST FIPS 180-4, Section 6.2.2: SHA-256 Hash Computation (outer loop) + * - NIST FIPS 180-4, Section 6.2.2: SHA-256 Hash Computation */ /* message block i = [1..N]; initialization postponed for performance reasons */ uint32_t Mi[TO_WORDS(SHA256_BLOCK_SIZE)]; /* index into Mi; initialization postponed for performance reasons */ - unsigned int t; + uint_fast8_t t; /* the total number of bytes in the message */ - uint64_t total = prefix_length + message_length; + uint64_t total = word_count * sizeof(uint32_t); + + /* See NIST FIPS 180-4, Section 6.2.1, Step 1 */ + *native_digest = sha256_H0; /* a) First we handle the complete message blocks (64 bytes each) */ /* See NIST FIPS 180-4, Section 6.2.2, outer loop */ - for (; message_length >= SHA256_BLOCK_SIZE; message += SHA256_BLOCK_SIZE, message_length -= SHA256_BLOCK_SIZE) { - big_endian_to_native(Mi, message, TO_WORDS(SHA256_BLOCK_SIZE)); - sha256_process_block(native_digest, Mi); + for (; word_count >= TO_WORDS(SHA256_BLOCK_SIZE); + words += TO_WORDS(SHA256_BLOCK_SIZE), word_count -= TO_WORDS(SHA256_BLOCK_SIZE)) { + xmss_sha256_process_block(native_digest, words); } /* b) Next we handle the remainder (if any). */ - - /* b.1) First the multiples of uint32_t. */ - t = (unsigned int)(message_length / sizeof(uint32_t)); - big_endian_to_native(Mi, message, t); - message += t * sizeof(uint32_t); - - /* b.2) Next the final uint32_t of the message including padding bit; see NIST FIPS 180-4, Section 5.1.1 */ - switch (message_length % sizeof(uint32_t)) { - case 0: - Mi[t] = 0x80000000; - break; - case 1: - Mi[t] = ((uint32_t)message[0] << 24) | 0x00800000; - break; - case 2: - Mi[t] = ((uint32_t)message[0] << 24) | ((uint32_t)message[1] << 16) | 0x00008000; - break; - case 3: - Mi[t] = ((uint32_t)message[0] << 24) | ((uint32_t)message[1] << 16) | ((uint32_t)message[2] << 8) | 0x00000080; - break; - } + t = (uint_fast8_t)(word_count); + memcpy(Mi, words, word_count * sizeof(uint32_t)); + Mi[t] = 0x80000000; ++t; - /* We do not update message and message_length as they are no longer needed. */ + /* We do not update words and word_count as they are no longer needed. */ /* b.3) Add the total length; see NIST FIPS 180-4, Section 5.1.1. */ if (t > TO_WORDS(SHA256_BLOCK_SIZE) - sizeof(uint64_t) / sizeof(uint32_t)) { /* The length of the message must be stored in the last 64 bits of a block, so we need another block. */ memset(&Mi[t], 0, (TO_WORDS(SHA256_BLOCK_SIZE) - t) * sizeof(uint32_t)); - sha256_process_block(native_digest, Mi); + xmss_sha256_process_block(native_digest, Mi); t = 0; } - memset(&Mi[t], 0, (14 - t) * sizeof(uint32_t)); + memset(&Mi[t], 0, ((size_t)14 - t) * sizeof(uint32_t)); /* Note that 'total' represents number of bytes, whereas SHA-256 requires number of bits. */ Mi[14] = (uint32_t)(total >> 29); Mi[15] = (uint32_t)(total << 3); - sha256_process_block(native_digest, Mi); + xmss_sha256_process_block(native_digest, Mi); } + +#endif /* XMSS_ENABLE_SIGNING */ diff --git a/src/sha256_internal_xmss_hashes.h b/src/sha256_internal_xmss_hashes.h index 3974318..78ae7be 100644 --- a/src/sha256_internal_xmss_hashes.h +++ b/src/sha256_internal_xmss_hashes.h @@ -29,19 +29,12 @@ #include #include "endianness.h" +#include "libxmss.h" #include "override_sha256_internal.h" +#include "sha256_internal_H0.h" #include "utils.h" #include "xmss_hashes_base.h" -/** - * @brief - * The initial native SHA-256 hash value as defined by the standard. - * - * @details - * See NIST FIPS 180-4, Section 5.3.3. - */ -extern const XmssNativeValue256 sha256_H0; - /** * @brief * Completes the intermediate hash value by processing the final message. @@ -60,50 +53,9 @@ extern const XmssNativeValue256 sha256_H0; * @param[in] prefix_length Number of bytes already processed before calling this function; must be a multiple * of the block size (64 bytes). */ -void sha256_process_message_final(XmssNativeValue256 *restrict native_digest, const uint8_t *restrict message, - size_t message_length, uint_fast16_t prefix_length); - -/** - * @copydoc prototype_digest - * @see prototype_digest - * - * @details - * This is the specialization for the SHA-256 algorithm using the internal interface. - * - * This function implements the SHA-256($M$) function as defined by NIST FIPS 180-4, Section 6.2. - */ -static inline void sha256_digest(XmssValue256 *restrict digest, const uint8_t *restrict const message, - const size_t message_length) -{ - /* - * See: NIST FIPS 180-4, Section 6.2 - * - * This function handles: - * - NIST FIPS 180-4, Section 6.2.1: SHA-256 Preprocessing - * - NIST FIPS 180-4, Section 6.2.2: SHA-256 Hash Computation (outer loop) - */ - - /* initialization postponed for performance reasons */ - XmssNativeValue256 native_digest; - - /* See NIST FIPS 180-4, Section 6.2.1, Step 1 */ - native_256_copy(&native_digest, &sha256_H0); - - sha256_process_message_final(&native_digest, message, message_length, 0); - - /* See NIST FIPS 180-4, Section 6.2.2. */ - native_to_big_endian_256(digest, &native_digest); -} - -/** - * @copydoc prototype_native_digest - * @see prototype_native_digest - * - * @details - * This is the specialization for the SHA-256 algorithm using the internal interface. - */ -void sha256_native_digest(XmssNativeValue256 *restrict native_digest, const uint32_t *restrict words, - size_t word_count); +LIBXMSS_STATIC +void sha256_process_message_final(XmssNativeValue256 *native_digest, const uint8_t *message, size_t message_length, + uint64_t prefix_length); /** * @brief @@ -115,12 +67,11 @@ void sha256_native_digest(XmssNativeValue256 *restrict native_digest, const uint * @param[out] native_digest Output of the (possibly intermediate) native hash value. * @param[in] input Points to 32 uint32_t values, 2 SHA-256 blocks. */ -static inline void sha256_2_blocks(XmssNativeValue256 *restrict const native_digest, - const uint32_t *restrict const input) +static inline void sha256_2_blocks(XmssNativeValue256 *const native_digest, const uint32_t *const input) { - native_256_copy(native_digest, &sha256_H0); - sha256_process_block(native_digest, input); - sha256_process_block(native_digest, input + TO_WORDS(SHA256_BLOCK_SIZE)); + *native_digest = sha256_H0; + xmss_sha256_process_block(native_digest, input); + xmss_sha256_process_block(native_digest, input + TO_WORDS(SHA256_BLOCK_SIZE)); } /** @@ -133,13 +84,12 @@ static inline void sha256_2_blocks(XmssNativeValue256 *restrict const native_dig * @param[out] native_digest Output of the (possibly intermediate) native hash value. * @param[in] input Points to 48 uint32_t values, 3 SHA-256 blocks. */ -static inline void sha256_3_blocks(XmssNativeValue256 *restrict const native_digest, - const uint32_t *restrict const input) +static inline void sha256_3_blocks(XmssNativeValue256 *const native_digest, const uint32_t *const input) { - native_256_copy(native_digest, &sha256_H0); - sha256_process_block(native_digest, input); - sha256_process_block(native_digest, input + TO_WORDS(SHA256_BLOCK_SIZE)); - sha256_process_block(native_digest, input + 2 * TO_WORDS(SHA256_BLOCK_SIZE)); + *native_digest = sha256_H0; + xmss_sha256_process_block(native_digest, input); + xmss_sha256_process_block(native_digest, input + TO_WORDS(SHA256_BLOCK_SIZE)); + xmss_sha256_process_block(native_digest, input + 2 * TO_WORDS(SHA256_BLOCK_SIZE)); } /** @@ -149,7 +99,7 @@ static inline void sha256_3_blocks(XmssNativeValue256 *restrict const native_dig * @details * This is the specialization for the SHA-256 algorithm using the internal interface. */ -static inline void sha256_F(XmssNativeValue256 *restrict const native_digest, const Input_F *restrict const input) +static inline void sha256_F(XmssNativeValue256 *const native_digest, const Input_F *const input) { sha256_2_blocks(native_digest, (const uint32_t *)input); } @@ -161,23 +111,48 @@ static inline void sha256_F(XmssNativeValue256 *restrict const native_digest, co * @details * This is the specialization for the SHA-256 algorithm using the internal interface. */ -static inline void sha256_H(XmssNativeValue256 *restrict const native_digest, const Input_H *restrict const input) +static inline void sha256_H(XmssNativeValue256 *const native_digest, const Input_H *const input) { sha256_3_blocks(native_digest, (const uint32_t *)input); } /** - * @copydoc prototype_H_msg - * @see prototype_H_msg + * @copydoc prototype_H_msg_init + * @see prototype_H_msg_init * * @details * This is the specialization for the SHA-256 algorithm using the internal interface. */ -static inline void sha256_H_msg(XmssNativeValue256 *restrict const native_digest, const Input_H_msg *restrict const input, - const uint8_t *restrict const message, const size_t message_length) +static inline void sha256_H_msg_init(XmssHMsgCtx *const ctx, const Input_H_msg *const input) { - sha256_2_blocks(native_digest, (const uint32_t *)input); - sha256_process_message_final(native_digest, message, message_length, sizeof(Input_H_msg)); + sha256_2_blocks(&ctx->sha256_ctx.intermediate_hash, (const uint32_t *)input); + ctx->sha256_ctx.bytes_in_partial_block.value = 0; + ctx->sha256_ctx.bytes_hashed = sizeof(Input_H_msg); +} + +/** + * @copydoc prototype_H_msg_update + * @see prototype_H_msg_update + * + * @details + * This is the specialization for the SHA-256 algorithm using the internal interface. + */ +LIBXMSS_STATIC +void sha256_H_msg_update(XmssHMsgCtx *ctx, const uint8_t *part, size_t part_length, + const uint8_t *volatile *part_verify); + +/** + * @copydoc prototype_H_msg_finalize + * @see prototype_H_msg_finalize + * + * @details + * This is the specialization for the SHA-256 algorithm using the internal interface. + */ +static inline void sha256_H_msg_finalize(XmssNativeValue256 *const native_digest, XmssHMsgCtx *const ctx) +{ + sha256_process_message_final(&ctx->sha256_ctx.intermediate_hash, ctx->sha256_ctx.partial_block, + ctx->sha256_ctx.bytes_in_partial_block.value, ctx->sha256_ctx.bytes_hashed); + *native_digest = ctx->sha256_ctx.intermediate_hash; } /** @@ -187,11 +162,13 @@ static inline void sha256_H_msg(XmssNativeValue256 *restrict const native_digest * @details * This is the specialization for the SHA-256 algorithm using the internal interface. */ -static inline void sha256_PRF(XmssNativeValue256 *restrict const native_digest, const Input_PRF *restrict const input) +static inline void sha256_PRF(XmssNativeValue256 *const native_digest, const Input_PRF *const input) { sha256_2_blocks(native_digest, (const uint32_t *)input); } +#if XMSS_ENABLE_SIGNING + /** * @copydoc prototype_PRFkeygen * @see prototype_PRFkeygen @@ -199,8 +176,7 @@ static inline void sha256_PRF(XmssNativeValue256 *restrict const native_digest, * @details * This is the specialization for the SHA-256 algorithm using the internal interface. */ -static inline void sha256_PRFkeygen(XmssNativeValue256 *restrict const native_digest, - const Input_PRFkeygen *restrict const input) +static inline void sha256_PRFkeygen(XmssNativeValue256 *const native_digest, const Input_PRFkeygen *const input) { sha256_3_blocks(native_digest, (const uint32_t *)input); } @@ -212,10 +188,52 @@ static inline void sha256_PRFkeygen(XmssNativeValue256 *restrict const native_di * @details * This is the specialization for the SHA-256 algorithm using the internal interface. */ -static inline void sha256_PRFindex(XmssNativeValue256 *restrict const native_digest, - const Input_PRFindex *restrict const input) +static inline void sha256_PRFindex(XmssNativeValue256 *const native_digest, const Input_PRFindex *const input) { sha256_3_blocks(native_digest, (const uint32_t *)input); } +/** + * @copydoc prototype_digest + * @see prototype_digest + * + * @details + * This is the specialization for the SHA-256 algorithm using the internal interface. + * + * This function implements the SHA-256($M$) function as defined by NIST FIPS 180-4, Section 6.2. + */ +static inline void sha256_digest(XmssValue256 *digest, const uint8_t *const message, const size_t message_length) +{ + /* + * See: NIST FIPS 180-4, Section 6.2 + * + * This function handles: + * - NIST FIPS 180-4, Section 6.2.1: SHA-256 Preprocessing + * - NIST FIPS 180-4, Section 6.2.2: SHA-256 Hash Computation (outer loop) + */ + + /* initialization postponed for performance reasons */ + XmssNativeValue256 native_digest; + + /* See NIST FIPS 180-4, Section 6.2.1, Step 1 */ + native_digest = sha256_H0; + + sha256_process_message_final(&native_digest, message, message_length, 0); + + /* See NIST FIPS 180-4, Section 6.2.2. */ + native_to_big_endian_256(digest, &native_digest); +} + +/** + * @copydoc prototype_native_digest + * @see prototype_native_digest + * + * @details + * This is the specialization for the SHA-256 algorithm using the internal interface. + */ +LIBXMSS_STATIC +void sha256_native_digest(XmssNativeValue256 *native_digest, const uint32_t *words, size_t word_count); + +#endif /* XMSS_ENABLE_SIGNING */ + #endif /* !XMSS_SHA256_INTERNAL_XMSS_HASHES_H_INCLUDED */ diff --git a/src/sha256_xmss_hashes.c b/src/sha256_xmss_hashes.c index 477b6dd..95825d4 100644 --- a/src/sha256_xmss_hashes.c +++ b/src/sha256_xmss_hashes.c @@ -13,27 +13,31 @@ #include "config.h" -#if !XMSS_ENABLE_SHA256 -# error "SHA-256 is disabled, so SHA-256 related source files must not be compiled." +#if !XMSS_ENABLE_HASH_ABSTRACTION +# error "Hash abstraction is disabled, so this source file must not be compiled." #endif +#include "libxmss.h" + #if XMSS_ENABLE_SHA256_GENERIC # include "sha256_generic_xmss_hashes.h" #else # include "sha256_internal_xmss_hashes.h" #endif -#if XMSS_ENABLE_HASH_ABSTRACTION - +LIBXMSS_STATIC const xmss_hashes sha256_xmss_hashes = { - .digest = sha256_digest, - .native_digest = sha256_native_digest, .F = sha256_F, .H = sha256_H, - .H_msg = sha256_H_msg, - .PRF = sha256_PRF, + .H_msg_init = sha256_H_msg_init, + .H_msg_update = sha256_H_msg_update, + .H_msg_finalize = sha256_H_msg_finalize, + .PRF = sha256_PRF +#if XMSS_ENABLE_SIGNING + , .PRFkeygen = sha256_PRFkeygen, - .PRFindex = sha256_PRFindex -}; - + .PRFindex = sha256_PRFindex, + .digest = sha256_digest, + .native_digest = sha256_native_digest #endif +}; diff --git a/src/sha256_xmss_hashes.h b/src/sha256_xmss_hashes.h index 36341c3..17148da 100644 --- a/src/sha256_xmss_hashes.h +++ b/src/sha256_xmss_hashes.h @@ -31,8 +31,19 @@ #if XMSS_ENABLE_HASH_ABSTRACTION +# include "libxmss.h" # include "xmss_hashes_base.h" +# if LIBXMSS + +/* + * It is not possible to forward-declare static data. + * For our amalgamated library source, we define the data right here and now. + */ +# include "sha256_xmss_hashes.c" + +# else + /** * @brief * Contains all SHA-256 hash functions in a single abstraction structure. @@ -45,6 +56,8 @@ */ extern const xmss_hashes sha256_xmss_hashes; +# endif + #endif #endif /* !XMSS_SHA256_XMSS_HASHES_H_INCLUDED */ diff --git a/src/shake256_256_generic_xmss_hashes.h b/src/shake256_256_generic_xmss_hashes.h index 2cd99ed..24938e8 100644 --- a/src/shake256_256_generic_xmss_hashes.h +++ b/src/shake256_256_generic_xmss_hashes.h @@ -33,70 +33,64 @@ #include "override_shake256_256_generic.h" /** - * @copydoc prototype_digest - * @see prototype_digest + * @copydoc prototype_F + * @see prototype_F * * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. - * - * This function implements the SHAKE256($M$, 256) function as defined by NIST FIPS 202, Section 6.2. */ -static inline void shake256_256_digest(XmssValue256 *restrict digest, const uint8_t *restrict const message, - size_t message_length) +static inline void shake256_256_F(XmssNativeValue256 *const native_digest, const Input_F *const input) { - generic_digest(shake256_256_init, shake256_256_update, shake256_256_finalize, digest, message, message_length); + generic_F(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, native_digest, input); } /** - * @copydoc prototype_native_digest - * @see prototype_native_digest + * @copydoc prototype_H + * @see prototype_H * * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_native_digest(XmssNativeValue256 *restrict native_digest, - const uint32_t *restrict const words, size_t word_count) +static inline void shake256_256_H(XmssNativeValue256 *const native_digest, const Input_H *const input) { - generic_native_digest(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, words, - word_count); + generic_H(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, native_digest, input); } /** - * @copydoc prototype_F - * @see prototype_F + * @copydoc prototype_H_msg_init + * @see prototype_H_msg_init * * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_F(XmssNativeValue256 *restrict const native_digest, const Input_F *restrict const input) +static inline void shake256_256_H_msg_init(XmssHMsgCtx *const ctx, const Input_H_msg *const input) { - generic_F(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, input); + generic_H_msg_init(xmss_shake256_256_init, xmss_shake256_256_update, ctx, input); } /** - * @copydoc prototype_H - * @see prototype_H + * @copydoc prototype_H_msg_update + * @see prototype_H_msg_update * * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_H(XmssNativeValue256 *restrict const native_digest, const Input_H *restrict const input) +static inline void shake256_256_H_msg_update(XmssHMsgCtx *const ctx, const uint8_t *const part, + const size_t part_length, const uint8_t *volatile *const part_verify) { - generic_H(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, input); + generic_H_msg_update(xmss_shake256_256_update, ctx, part, part_length, part_verify); } /** - * @copydoc prototype_H_msg - * @see prototype_H_msg + * @copydoc prototype_H_msg_finalize + * @see prototype_H_msg_finalize * * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_H_msg(XmssNativeValue256 *restrict const native_digest, - const Input_H_msg *restrict const input, const uint8_t *restrict const message, const size_t message_length) +static inline void shake256_256_H_msg_finalize(XmssNativeValue256 *const native_digest, XmssHMsgCtx *const ctx) { - generic_H_msg(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, input, message, - message_length); + generic_H_msg_finalize(xmss_shake256_256_finalize, native_digest, ctx); } /** @@ -106,12 +100,13 @@ static inline void shake256_256_H_msg(XmssNativeValue256 *restrict const native_ * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_PRF(XmssNativeValue256 *restrict const native_digest, - const Input_PRF *restrict const input) +static inline void shake256_256_PRF(XmssNativeValue256 *const native_digest, const Input_PRF *const input) { - generic_PRF(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, input); + generic_PRF(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, native_digest, input); } +#if XMSS_ENABLE_SIGNING + /** * @copydoc prototype_PRFkeygen * @see prototype_PRFkeygen @@ -119,10 +114,10 @@ static inline void shake256_256_PRF(XmssNativeValue256 *restrict const native_di * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_PRFkeygen(XmssNativeValue256 *restrict const native_digest, - const Input_PRFkeygen *restrict const input) +static inline void shake256_256_PRFkeygen(XmssNativeValue256 *const native_digest, const Input_PRFkeygen *const input) { - generic_PRFkeygen(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, input); + generic_PRFkeygen(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, native_digest, + input); } /** @@ -132,10 +127,42 @@ static inline void shake256_256_PRFkeygen(XmssNativeValue256 *restrict const nat * @details * This is the specialization for the SHAKE256/256 algorithm using the generic interface. */ -static inline void shake256_256_PRFindex(XmssNativeValue256 *restrict const native_digest, - const Input_PRFindex *restrict const input) +static inline void shake256_256_PRFindex(XmssNativeValue256 *const native_digest, const Input_PRFindex *const input) +{ + generic_PRFindex(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, native_digest, + input); +} + +/** + * @copydoc prototype_digest + * @see prototype_digest + * + * @details + * This is the specialization for the SHAKE256/256 algorithm using the generic interface. + * + * This function implements the SHAKE256($M$, 256) function as defined by NIST FIPS 202, Section 6.2. + */ +static inline void shake256_256_digest(XmssValue256 *const digest, const uint8_t *const message, + const size_t message_length) +{ + generic_digest(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, digest, message, + message_length); +} + +/** + * @copydoc prototype_native_digest + * @see prototype_native_digest + * + * @details + * This is the specialization for the SHAKE256/256 algorithm using the generic interface. + */ +static inline void shake256_256_native_digest(XmssNativeValue256 *const native_digest, const uint32_t *const words, + const size_t word_count) { - generic_PRFindex(shake256_256_init, shake256_256_update, shake256_256_finalize, native_digest, input); + generic_native_digest(xmss_shake256_256_init, xmss_shake256_256_update, xmss_shake256_256_finalize, native_digest, + words, word_count); } +#endif /* XMSS_ENABLE_SIGNING */ + #endif /* !XMSS_SHAKE256_256_GENERIC_XMSS_HASHES_H_INCLUDED */ diff --git a/src/shake256_256_internal_default.c b/src/shake256_256_internal_default.c index ec216db..6fe5377 100644 --- a/src/shake256_256_internal_default.c +++ b/src/shake256_256_internal_default.c @@ -20,8 +20,8 @@ * Bit operations on the 64-bit lanes is such that a byte stream message can be absorbed without swapping, i.e., * directly into the memory occupied by the little-endian 64-bit lanes. * - * This means that sponge_absorb() can simply XOR the message (in chunks of 136 bytes, the block size of SHAKE256) into - * the memory pointed to by the state array **A**, whereas sponge_absorb_native() requires byte swapping + * This means that xmss_sponge_absorb() can simply XOR the message (in chunks of 136 bytes, the block size of SHAKE256) + * into the memory pointed to by the state array **A**, whereas xmss_sponge_absorb_native() requires byte swapping * for little-endian platforms. This is not a performance issue; SHA-3 requires read-read-modify-write * memory access, and byte swapping aligned data is free on most architectures. * @@ -36,7 +36,8 @@ * worthwhile. */ -#include "string.h" +#include +#include #include "config.h" @@ -47,19 +48,53 @@ # error "SHAKE256/256 uses generic interface, so SHAKE256/256 related internal source files must not be compiled." #endif +#include "libxmss.h" +#if LIBXMSS +# include +# include "types.h" + // Forward-declare our implementation as static before including the public header. + LIBXMSS_STATIC + void xmss_sponge_absorb(uint64_t *A, uint_fast8_t offset, const uint8_t *bytes, uint_fast8_t byte_count); + LIBXMSS_STATIC + void xmss_sponge_absorb_native(uint64_t *A, const uint32_t *words, uint_fast8_t word_count); +# if XMSS_ENABLE_SIGNING + LIBXMSS_STATIC + void xmss_sponge_squeeze(XmssValue256 *digest, const uint64_t *A); +# endif + LIBXMSS_STATIC + void xmss_sponge_squeeze_native(XmssNativeValue256 *native_digest, const uint64_t *A); + LIBXMSS_STATIC + void xmss_keccak_p_1600_24(uint64_t *A); +#endif + #include "override_shake256_256_internal.h" -void sponge_absorb(uint64_t *restrict const A, uint_fast16_t offset, const uint8_t *restrict bytes, - uint_fast16_t byte_count) +LIBXMSS_STATIC +void xmss_sponge_absorb(uint64_t *const A, const uint_fast8_t offset, const uint8_t *bytes, uint_fast8_t byte_count) { + /* Block size of SHAKE256/256 is 136 bytes. */ + assert(A != NULL); + assert(offset < 136); + assert(bytes != NULL); + assert(byte_count >= 1); + assert(byte_count <= 136); + assert((size_t)offset + byte_count <= 136); + uint8_t *S = (uint8_t *)A + offset; for (; byte_count > 0; --byte_count, ++bytes) { *S++ ^= *bytes; } } -void sponge_absorb_native(uint64_t *restrict const A, const uint32_t *restrict words, uint_fast16_t word_count) +LIBXMSS_STATIC +void xmss_sponge_absorb_native(uint64_t *const A, const uint32_t *words, uint_fast8_t word_count) { + /* Block size of SHAKE256/256 is 136 bytes == 34 words. */ + assert(A != NULL); + assert(words != NULL); + assert(word_count >= 1); + assert(word_count <= 34); + uint8_t *S = (uint8_t *)A; for (; word_count > 0; --word_count, ++words) { *S++ ^= (uint8_t)(*words >> 24); @@ -69,8 +104,14 @@ void sponge_absorb_native(uint64_t *restrict const A, const uint32_t *restrict w } } -void sponge_squeeze(XmssValue256 *restrict const digest, const uint64_t *restrict const A) +#if XMSS_ENABLE_SIGNING + +LIBXMSS_STATIC +void xmss_sponge_squeeze(XmssValue256 *const digest, const uint64_t *const A) { + assert(digest != NULL); + assert(A != NULL); + /* * NIST FIPS 202, Section 4, Step 7 & 8 & 9; we do not need Step 10. * @@ -79,8 +120,14 @@ void sponge_squeeze(XmssValue256 *restrict const digest, const uint64_t *restric memcpy(digest, A, sizeof(XmssValue256)); } -void sponge_squeeze_native(XmssNativeValue256 *restrict const native_digest, const uint64_t *restrict const A) +#endif + +LIBXMSS_STATIC +void xmss_sponge_squeeze_native(XmssNativeValue256 *const native_digest, const uint64_t *const A) { + assert(native_digest != NULL); + assert(A != NULL); + uint8_t *S = (uint8_t *)A; uint32_t *digest_word = native_digest->data; for (unsigned int i = 0; i < XMSS_VALUE_256_WORDS; ++i, ++digest_word, S += 4) { @@ -199,8 +246,11 @@ static void byte_swap_if_required(uint64_t *const A) } } -void keccak_p_1600_24(uint64_t *const A) +LIBXMSS_STATIC +void xmss_keccak_p_1600_24(uint64_t *const A) { + assert(A != NULL); + /* For performance reasons, the local variables are defined uninitialized and not explicitly set to 0. */ /* Working variables, reused for different parts of the algorithm. */ diff --git a/src/shake256_256_internal_xmss_hashes.c b/src/shake256_256_internal_xmss_hashes.c index bc7923a..aa8417a 100644 --- a/src/shake256_256_internal_xmss_hashes.c +++ b/src/shake256_256_internal_xmss_hashes.c @@ -11,6 +11,7 @@ * XMSS hash functions for SHAKE256/256 using the internal interface. */ +#include #include "config.h" #if !XMSS_ENABLE_SHAKE256_256 @@ -58,8 +59,8 @@ /** @brief Block size of SHAKE256-256. */ #define SHAKE256_256_BLOCK_SIZE (r / 8) -/** @brief Size of the Keccak state array. */ -#define KECCAK_STATE_ARRAY_SIZE (b / 8) +/** @private */ +XMSS_STATIC_ASSERT(b / 8 == KECCAK_STATE_ARRAY_SIZE, "Incorrect Keccak state array size."); /** * @brief @@ -91,131 +92,185 @@ static const uint8_t pad_end = 0x80; * @param[in] message Input message; may be NULL if and only if message_length is 0. * @param[in] message_length Input message length in bytes. */ -static void shake256_256_process_message_final(uint64_t *restrict const A, uint_fast16_t offset, - const uint8_t *restrict message, size_t message_length) +static void shake256_256_process_message_final(uint64_t *const A, uint_fast8_t offset, const uint8_t *message, + size_t message_length) { /* NIST FIPS 202, Section 4, Step 2 & 4 & 6 and Section 3.1.2 */ if (offset > 0 && offset + message_length >= r / 8) { - sponge_absorb(A, offset, message, r / 8 - offset); - keccak_p_1600_24(A); + xmss_sponge_absorb(A, offset, message, r / 8 - offset); + xmss_keccak_p_1600_24(A); message += SHAKE256_256_BLOCK_SIZE - offset; message_length -= SHAKE256_256_BLOCK_SIZE - offset; offset = 0; } for (; message_length >= SHAKE256_256_BLOCK_SIZE; message += SHAKE256_256_BLOCK_SIZE , message_length -= SHAKE256_256_BLOCK_SIZE ) { - sponge_absorb(A, 0, message, SHAKE256_256_BLOCK_SIZE); - keccak_p_1600_24(A); + xmss_sponge_absorb(A, 0, message, SHAKE256_256_BLOCK_SIZE); + xmss_keccak_p_1600_24(A); } /* NIST FIPS 202, Section 4, Step 1 & 3 & 6 */ - sponge_absorb(A, offset, message, (uint_fast16_t)message_length); - sponge_absorb(A, offset + (uint_fast16_t)message_length, &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); + if (message_length > 0) + { + xmss_sponge_absorb(A, offset, message, (uint_fast8_t)message_length); + } + xmss_sponge_absorb(A, offset + (uint_fast8_t)message_length, &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); } -void shake256_256_digest(XmssValue256 *const restrict digest, const uint8_t *const restrict message, - const size_t message_length) +void shake256_256_F(XmssNativeValue256 *const native_digest, const Input_F *const input) { - /* - * See: NIST FIPS 202 - * - * shake256_256_digest - * == SHAKE256 (M, d=256) - * == KECCAK[c=512] (M || 1111, d=256) - * == SPONGE[KECCAK-p[b=1600, nr=24], pad10*1, r=b-c=1600-512] (M, d=256) - */ - - /* NIST FIPS 202, Section 4, Step 5 and Section 3.1.2 */ uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - - shake256_256_process_message_final(A, 0, message, message_length); - - /* NIST FIPS 202, Section 4, Step 7 & 8 & 9; we do not need Step 10 */ - sponge_squeeze(digest, A); + xmss_sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_F_DATA_SIZE)); + xmss_sponge_absorb(A, 96, &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); + xmss_sponge_squeeze_native(native_digest, A); } -void shake256_256_native_digest(XmssNativeValue256 *restrict native_digest, const uint32_t *restrict words, - size_t word_count) +void shake256_256_H(XmssNativeValue256 *const native_digest, const Input_H *const input) { - /* NIST FIPS 202, Section 4, Step 5 and Section 3.1.2 */ uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; + xmss_sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_H_DATA_SIZE)); + xmss_sponge_absorb(A, 128, &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); + xmss_sponge_squeeze_native(native_digest, A); +} - /* NIST FIPS 202, Section 4, Step 2 & 4 & 6 and Section 3.1.2 */ - for (; word_count >= TO_WORDS(SHAKE256_256_BLOCK_SIZE); - words += TO_WORDS(SHAKE256_256_BLOCK_SIZE), word_count -= TO_WORDS(SHAKE256_256_BLOCK_SIZE)) { - sponge_absorb_native(A, words, TO_WORDS(SHAKE256_256_BLOCK_SIZE)); - keccak_p_1600_24(A); - } +void shake256_256_H_msg_init(XmssHMsgCtx *const ctx, const Input_H_msg *const input) +{ + memset(ctx->shake256_256_ctx.shake256_256_state_array, 0, sizeof(ctx->shake256_256_ctx.shake256_256_state_array)); + xmss_sponge_absorb_native(ctx->shake256_256_ctx.shake256_256_state_array, (const uint32_t *)input, + TO_WORDS(sizeof(Input_H_msg))); + ctx->shake256_256_ctx.offset.value = sizeof(Input_H_msg); +} - /* NIST FIPS 202, Section 4, Step 1 & 3 & 6 */ - sponge_absorb_native(A, words, (uint_fast16_t)word_count); - sponge_absorb(A, (uint_fast16_t)(word_count * sizeof(uint32_t)), &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); +void shake256_256_H_msg_update(XmssHMsgCtx *const ctx, const uint8_t *const part, size_t part_length, + const uint8_t *volatile *const part_verify) +{ + const uint8_t *volatile const volatile_part = part; + size_t offset = 0; + uint_fast8_t remaining_space_in_block = SHAKE256_256_BLOCK_SIZE - ctx->shake256_256_ctx.offset.value; + if (remaining_space_in_block < SHAKE256_256_BLOCK_SIZE && part_length >= remaining_space_in_block) { + xmss_sponge_absorb(ctx->shake256_256_ctx.shake256_256_state_array, ctx->shake256_256_ctx.offset.value, + volatile_part + offset, remaining_space_in_block); + xmss_keccak_p_1600_24(ctx->shake256_256_ctx.shake256_256_state_array); + offset += remaining_space_in_block; + part_length -= remaining_space_in_block; + ctx->shake256_256_ctx.offset.value = 0; + } + while (part_length >= SHAKE256_256_BLOCK_SIZE) { + xmss_sponge_absorb(ctx->shake256_256_ctx.shake256_256_state_array, 0, volatile_part + offset, + SHAKE256_256_BLOCK_SIZE); + xmss_keccak_p_1600_24(ctx->shake256_256_ctx.shake256_256_state_array); + offset += SHAKE256_256_BLOCK_SIZE; + part_length -= SHAKE256_256_BLOCK_SIZE; + } + if (part_length > 0) + { + xmss_sponge_absorb(ctx->shake256_256_ctx.shake256_256_state_array, ctx->shake256_256_ctx.offset.value, + volatile_part + offset, (uint_fast8_t)part_length); + ctx->shake256_256_ctx.offset.value += (uint_fast8_t)part_length; + } - /* NIST FIPS 202, Section 4, Step 7 & 8 & 9; we do not need Step 10 */ - sponge_squeeze_native(native_digest, A); + if (part_verify != NULL) + { + *part_verify = volatile_part; + } } -void shake256_256_F(XmssNativeValue256 *restrict const native_digest, const Input_F *restrict const input) +void shake256_256_H_msg_finalize(XmssNativeValue256 *const native_digest, XmssHMsgCtx *const ctx) { - uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_F_DATA_SIZE)); - sponge_absorb(A, 96, &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); - sponge_squeeze_native(native_digest, A); + shake256_256_process_message_final(ctx->shake256_256_ctx.shake256_256_state_array, + ctx->shake256_256_ctx.offset.value, NULL, 0); + xmss_sponge_squeeze_native(native_digest, ctx->shake256_256_ctx.shake256_256_state_array); } -void shake256_256_H(XmssNativeValue256 *restrict const native_digest, const Input_H *restrict const input) +void shake256_256_PRF(XmssNativeValue256 *const native_digest, const Input_PRF *const input) { uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_H_DATA_SIZE)); - sponge_absorb(A, 128, &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); - sponge_squeeze_native(native_digest, A); + xmss_sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_PRF_DATA_SIZE)); + xmss_sponge_absorb(A, INPUT_PRF_DATA_SIZE, &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); + xmss_sponge_squeeze_native(native_digest, A); } -void shake256_256_H_msg(XmssNativeValue256 *restrict const native_digest, const Input_H_msg *restrict const input, - const uint8_t *restrict message, size_t message_length) +#if XMSS_ENABLE_SIGNING + +void shake256_256_PRFkeygen(XmssNativeValue256 *const native_digest, const Input_PRFkeygen *const input) { uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - /* We can directly use the size of Input_H_msg because it never contains any SHA-256 padding. */ - sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(sizeof(Input_H_msg))); - shake256_256_process_message_final(A, sizeof(Input_H_msg), message, message_length); - sponge_squeeze_native(native_digest, A); + xmss_sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_PRF_KEYGEN_DATA_SIZE)); + xmss_sponge_absorb(A, INPUT_PRF_KEYGEN_DATA_SIZE, &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); + xmss_sponge_squeeze_native(native_digest, A); } -void shake256_256_PRF(XmssNativeValue256 *restrict const native_digest, const Input_PRF *restrict const input) +void shake256_256_PRFindex(XmssNativeValue256 *const native_digest, const Input_PRFindex *const input) { uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_PRF_DATA_SIZE)); - sponge_absorb(A, INPUT_PRF_DATA_SIZE, &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); - sponge_squeeze_native(native_digest, A); + xmss_sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_PRF_INDEX_DATA_SIZE)); + xmss_sponge_absorb(A, INPUT_PRF_INDEX_DATA_SIZE, &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); + xmss_sponge_squeeze_native(native_digest, A); } -void shake256_256_PRFkeygen(XmssNativeValue256 *restrict const native_digest, - const Input_PRFkeygen *restrict const input) +void shake256_256_digest(XmssValue256 *const digest, const uint8_t *const message, const size_t message_length) { + /* + * See: NIST FIPS 202 + * + * shake256_256_digest + * == SHAKE256 (M, d=256) + * == KECCAK[c=512] (M || 1111, d=256) + * == SPONGE[KECCAK-p[b=1600, nr=24], pad10*1, r=b-c=1600-512] (M, d=256) + */ + + /* NIST FIPS 202, Section 4, Step 5 and Section 3.1.2 */ uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_PRF_KEYGEN_DATA_SIZE)); - sponge_absorb(A, INPUT_PRF_KEYGEN_DATA_SIZE, &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); - sponge_squeeze_native(native_digest, A); + + shake256_256_process_message_final(A, 0, message, message_length); + + /* NIST FIPS 202, Section 4, Step 7 & 8 & 9; we do not need Step 10 */ + xmss_sponge_squeeze(digest, A); } -void shake256_256_PRFindex(XmssNativeValue256 *restrict const native_digest, const Input_PRFindex *restrict const input) +void shake256_256_native_digest(XmssNativeValue256 *const native_digest, const uint32_t *words, size_t word_count) { + /* NIST FIPS 202, Section 4, Step 5 and Section 3.1.2 */ uint64_t A[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)] = { 0 }; - sponge_absorb_native(A, (const uint32_t *)input, TO_WORDS(INPUT_PRF_INDEX_DATA_SIZE)); - sponge_absorb(A, INPUT_PRF_INDEX_DATA_SIZE, &pad_xof, sizeof(pad_xof)); - sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); - keccak_p_1600_24(A); - sponge_squeeze_native(native_digest, A); + + /* NIST FIPS 202, Section 4, Step 2 & 4 & 6 and Section 3.1.2 */ + for (; word_count >= TO_WORDS(SHAKE256_256_BLOCK_SIZE); + words += TO_WORDS(SHAKE256_256_BLOCK_SIZE), word_count -= TO_WORDS(SHAKE256_256_BLOCK_SIZE)) { + xmss_sponge_absorb_native(A, words, TO_WORDS(SHAKE256_256_BLOCK_SIZE)); + xmss_keccak_p_1600_24(A); + } + + /* NIST FIPS 202, Section 4, Step 1 & 3 & 6 */ + if (word_count > 0) + { + xmss_sponge_absorb_native(A, words, (uint_fast8_t)word_count); + } + xmss_sponge_absorb(A, (uint_fast8_t)(word_count * sizeof(uint32_t)), &pad_xof, sizeof(pad_xof)); + xmss_sponge_absorb(A, SHAKE256_256_BLOCK_SIZE - sizeof(pad_end), &pad_end, sizeof(pad_end)); + xmss_keccak_p_1600_24(A); + + /* NIST FIPS 202, Section 4, Step 7 & 8 & 9; we do not need Step 10 */ + xmss_sponge_squeeze_native(native_digest, A); } + +#endif /* XMSS_ENABLE_SIGNING */ + +#ifndef DOXYGEN +#undef b +#undef c +#undef r +#undef SHAKE256_256_BLOCK_SIZE +#undef KECCAK_STATE_ARRAY_SIZE +#endif diff --git a/src/shake256_256_internal_xmss_hashes.h b/src/shake256_256_internal_xmss_hashes.h index 8214f0b..19fa8b7 100644 --- a/src/shake256_256_internal_xmss_hashes.h +++ b/src/shake256_256_internal_xmss_hashes.h @@ -26,56 +26,59 @@ # error "SHAKE256/256 uses generic interface, so SHAKE256/256 related internal headers must not be included." #endif +#include "libxmss.h" #include "xmss_hashes_base.h" /** - * @copydoc prototype_digest - * @see prototype_digest + * @copydoc prototype_F + * @see prototype_F * * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. - * - * This function implements the SHAKE256($M$, 256) function as defined by NIST FIPS 202, Section 6.2. */ -void shake256_256_digest(XmssValue256 *restrict digest, const uint8_t *restrict message, size_t message_length); +LIBXMSS_STATIC +void shake256_256_F(XmssNativeValue256 *native_digest, const Input_F *input); /** - * @copydoc prototype_native_digest - * @see prototype_native_digest + * @copydoc prototype_H + * @see prototype_H * * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_native_digest(XmssNativeValue256 *restrict native_digest, const uint32_t *restrict words, - size_t word_count); +LIBXMSS_STATIC +void shake256_256_H(XmssNativeValue256 *native_digest, const Input_H *input); /** - * @copydoc prototype_F - * @see prototype_F + * @copydoc prototype_H_msg_init + * @see prototype_H_msg_init * * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_F(XmssNativeValue256 *restrict native_digest, const Input_F *restrict input); +LIBXMSS_STATIC +void shake256_256_H_msg_init(XmssHMsgCtx *ctx, const Input_H_msg *input); /** - * @copydoc prototype_H - * @see prototype_H + * @copydoc prototype_H_msg_update + * @see prototype_H_msg_update * * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_H(XmssNativeValue256 *restrict native_digest, const Input_H *restrict input); +LIBXMSS_STATIC +void shake256_256_H_msg_update(XmssHMsgCtx *ctx, const uint8_t *part, size_t part_length, + const uint8_t *volatile *part_verify); /** - * @copydoc prototype_H_msg - * @see prototype_H_msg + * @copydoc prototype_H_msg_finalize + * @see prototype_H_msg_finalize * * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_H_msg(XmssNativeValue256 *restrict native_digest, const Input_H_msg *restrict input, - const uint8_t *restrict message, size_t message_length); +LIBXMSS_STATIC +void shake256_256_H_msg_finalize(XmssNativeValue256 *native_digest, XmssHMsgCtx *ctx); /** * @copydoc prototype_PRF @@ -84,7 +87,10 @@ void shake256_256_H_msg(XmssNativeValue256 *restrict native_digest, const Input_ * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_PRF(XmssNativeValue256 *restrict native_digest, const Input_PRF *restrict input); +LIBXMSS_STATIC +void shake256_256_PRF(XmssNativeValue256 *native_digest, const Input_PRF *input); + +#if XMSS_ENABLE_SIGNING /** * @copydoc prototype_PRFkeygen @@ -93,7 +99,8 @@ void shake256_256_PRF(XmssNativeValue256 *restrict native_digest, const Input_PR * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_PRFkeygen(XmssNativeValue256 *restrict native_digest, const Input_PRFkeygen *restrict input); +LIBXMSS_STATIC +void shake256_256_PRFkeygen(XmssNativeValue256 *native_digest, const Input_PRFkeygen *input); /** * @copydoc prototype_PRFindex @@ -102,6 +109,31 @@ void shake256_256_PRFkeygen(XmssNativeValue256 *restrict native_digest, const In * @details * This is the specialization for the SHAKE256/256 algorithm using the internal interface. */ -void shake256_256_PRFindex(XmssNativeValue256 *restrict native_digest, const Input_PRFindex *restrict input); +LIBXMSS_STATIC +void shake256_256_PRFindex(XmssNativeValue256 *native_digest, const Input_PRFindex *input); + +/** + * @copydoc prototype_digest + * @see prototype_digest + * + * @details + * This is the specialization for the SHAKE256/256 algorithm using the internal interface. + * + * This function implements the SHAKE256($M$, 256) function as defined by NIST FIPS 202, Section 6.2. + */ +LIBXMSS_STATIC +void shake256_256_digest(XmssValue256 *digest, const uint8_t *message, size_t message_length); + +/** + * @copydoc prototype_native_digest + * @see prototype_native_digest + * + * @details + * This is the specialization for the SHAKE256/256 algorithm using the internal interface. + */ +LIBXMSS_STATIC +void shake256_256_native_digest(XmssNativeValue256 *native_digest, const uint32_t *words, size_t word_count); + +#endif /* XMSS_ENABLE_SIGNING */ #endif /* !XMSS_SHAKE256_256_INTERNAL_XMSS_HASHES_H_INCLUDED */ diff --git a/src/shake256_256_xmss_hashes.c b/src/shake256_256_xmss_hashes.c index 5f04877..23f9c71 100644 --- a/src/shake256_256_xmss_hashes.c +++ b/src/shake256_256_xmss_hashes.c @@ -13,23 +13,26 @@ #include "config.h" -#if !XMSS_ENABLE_SHAKE256_256 -# error "SHAKE256/256 is disabled, so SHAKE256/256 related source files must not be compiled." +#if !XMSS_ENABLE_HASH_ABSTRACTION +# error "Hash abstraction is disabled, so this source file must not be compiled." #endif +#include "libxmss.h" #include "shake256_256_xmss_hashes.h" -#if XMSS_ENABLE_HASH_ABSTRACTION - +LIBXMSS_STATIC const xmss_hashes shake256_256_xmss_hashes = { - .digest = shake256_256_digest, - .native_digest = shake256_256_native_digest, .F = shake256_256_F, .H = shake256_256_H, - .H_msg = shake256_256_H_msg, - .PRF = shake256_256_PRF, + .H_msg_init = shake256_256_H_msg_init, + .H_msg_update = shake256_256_H_msg_update, + .H_msg_finalize = shake256_256_H_msg_finalize, + .PRF = shake256_256_PRF +#if XMSS_ENABLE_SIGNING + , .PRFkeygen = shake256_256_PRFkeygen, - .PRFindex = shake256_256_PRFindex -}; - + .PRFindex = shake256_256_PRFindex, + .digest = shake256_256_digest, + .native_digest = shake256_256_native_digest #endif +}; diff --git a/src/shake256_256_xmss_hashes.h b/src/shake256_256_xmss_hashes.h index 44be706..e48add5 100644 --- a/src/shake256_256_xmss_hashes.h +++ b/src/shake256_256_xmss_hashes.h @@ -31,8 +31,19 @@ #if XMSS_ENABLE_HASH_ABSTRACTION +# include "libxmss.h" # include "xmss_hashes_base.h" +# if LIBXMSS + +/* + * It is not possible to forward-declare static data. + * For our amalgamated library source, we define the data right here and now. + */ +# include "shake256_256_xmss_hashes.c" + +# else + /** * @brief * Contains all SHAKE256/256 hash functions in a single abstraction structure. @@ -44,6 +55,8 @@ */ extern const xmss_hashes shake256_256_xmss_hashes; +# endif + #endif #endif /* !XMSS_SHAKE256_256_XMSS_HASHES_H_INCLUDED */ diff --git a/src/signing.c b/src/signing.c index dc15286..0f537e5 100644 --- a/src/signing.c +++ b/src/signing.c @@ -12,79 +12,37 @@ * XMSS signature library. */ +#include "config.h" + +#if !XMSS_ENABLE_SIGNING +# error "Signing support is disabled, so this source file must not be compiled." +#endif + #include #include #include #include -#include "config.h" - #include "signing.h" #include "compat_stdatomic.h" #include "endianness.h" +#include "fault_detection_helpers.h" #include "index_permutation.h" -#include "private.h" +#include "signing_private.h" #include "structures.h" #include "types.h" #include "utils.h" -#include "wotsp.h" +#include "wotsp_signing.h" #include "xmss_hashes.h" #include "xmss_tree.h" +#include "zeroize.h" -/** - * @brief - * Make the function that uses the macro return return_value if condition is true. - * - * @details - * The condition is checked twice to ensure that a single bit error in the CPU's flag register cannot cause the return - * to be skipped. To ensure that the compiler cannot optimize the second if-statement away, the condition should involve - * a variable that is declared volatile. - * - * @param[in] condition The condition to check, should involve a volatile variable. - * @param[in] return_value Value for the function to return if the condition is true. - */ -#define REDUNDANT_RETURN_IF(condition, return_value) \ - { \ - if (condition) { \ - return return_value; \ - } \ - if (condition) { \ - return return_value; \ - } \ - } - -/** - * @brief - * This macro checks if result is XMSS_OKAY. If not, it makes the function that uses the macro return result. - * - * @details - * This macro checks result multiple times for bit error resilience. To make sure that the compiler cannot optimize - * away the redundant checks, result should be a volatile variable. If a bit error causes the program to end up in the - * result != XMSS_OKAY arm when actually result == XMSS_OKAY, or when the first if-statement is skipped even though - * result != XMSS_OKAY, XMSS_ERR_BIT_ERROR_DETECTED is returned. - * - * @param[in] result XmssError to check. Must be stored in a volatile variable. -*/ -#define REDUNDANT_RETURN_ERR(result) \ - { \ - if ((result) != XMSS_OKAY) { \ - if ((result) == XMSS_OKAY) { \ - return XMSS_ERR_BIT_ERROR_DETECTED; \ - } \ - return result; \ - } \ - if ((result) != XMSS_OKAY) { \ - return XMSS_ERR_BIT_ERROR_DETECTED; \ - } \ - } - -XmssError xmss_context_initialize(XmssSigningContext *restrict *const restrict context, - const XmssParameterSetOID parameter_set, const XmssReallocFunction custom_realloc, - const XmssFreeFunction custom_free, const XmssZeroizeFunction zeroize) +XmssError xmss_context_initialize(XmssSigningContext **const context, const XmssParameterSetOID parameter_set, + const XmssReallocFunction custom_realloc, const XmssFreeFunction custom_free, const XmssZeroizeFunction zeroize) { XmssSigningContext *reallocated_context = NULL; - XmssError err_code = XMSS_OKAY; + XmssError err_code = XMSS_UNINITIALIZED; if (context == NULL || custom_realloc == NULL || custom_free == NULL) { return XMSS_ERR_NULL_POINTER; @@ -109,25 +67,31 @@ XmssError xmss_context_initialize(XmssSigningContext *restrict *const restrict c (*context)->pad_ = 0; (*context)->zeroize = (zeroize != NULL) ? zeroize : xmss_zeroize; - err_code = xmss_get_hash_functions(HASH_ABSTRACTION(&(*context)->hash_functions) parameter_set); + DEFINE_HASH_FUNCTIONS; + err_code = INITIALIZE_HASH_FUNCTIONS(parameter_set); if (err_code != XMSS_OKAY) { (*context)->free(*context); *context = NULL; return err_code; } +#if XMSS_ENABLE_HASH_ABSTRACTION + (*context)->hash_functions = hash_functions; +#else + (*context)->pad_hash_functions = NULL; +#endif if ((*context)->parameter_set != (*context)->redundant_parameter_set) { (*context)->free(*context); *context = NULL; - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } (*context)->initialized = (uint32_t)XMSS_INITIALIZATION_INITIALIZED; - return XMSS_OKAY; + return err_code; } -XmssError xmss_load_public_key(XmssInternalCache **const restrict cache, XmssKeyContext *const restrict key_context, - const XmssPublicKeyInternalBlob *const restrict public_key) +XmssError xmss_load_public_key(XmssInternalCache **const cache, XmssKeyContext *const key_context, + const XmssPublicKeyInternalBlob *const public_key) { /* To protect against bit errors in the CPU's flag register, we execute some if-statements in this function twice. * The variables being checked are volatile, so the compiler is not allowed to optimize away the redundant if. */ @@ -137,7 +101,7 @@ XmssError xmss_load_public_key(XmssInternalCache **const restrict cache, XmssKey return XMSS_ERR_NULL_POINTER; } XmssPublicKeyInternal *pub_key_internal = NULL; - volatile XmssError err_code = XMSS_OKAY; + volatile XmssError err_code = XMSS_UNINITIALIZED; XmssCacheType cache_type = XMSS_CACHE_NONE; /* xmss_verify_public_key() also checks that key_context is initialized. */ @@ -167,10 +131,10 @@ XmssError xmss_load_public_key(XmssInternalCache **const restrict cache, XmssKey *cache = NULL; } - big_endian_to_native_256(&key_context->public_key_root, &pub_key_internal->public_key); + big_endian_to_native_256(&key_context->public_key_root, &pub_key_internal->root); key_context->initialized = (uint32_t)XMSS_INITIALIZATION_WITH_PUBLIC_KEY; - return XMSS_OKAY; + return err_code; } void xmss_free_signing_context(XmssSigningContext *signing_context) { @@ -190,8 +154,8 @@ void xmss_free_signing_context(XmssSigningContext *signing_context) { * * @retval XMSS_OKAY The structure is correct. * @retval XMSS_ERR_BAD_CONTEXT The structure is incomplete or not initialized. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ static XmssError check_signing_context(const XmssSigningContext *signing_context) { @@ -204,17 +168,11 @@ static XmssError check_signing_context(const XmssSigningContext *signing_context return XMSS_ERR_BAD_CONTEXT; } #if XMSS_ENABLE_HASH_ABSTRACTION - if (signing_context->hash_functions.digest == NULL || signing_context->hash_functions.native_digest == NULL - || signing_context->hash_functions.F == NULL || signing_context->hash_functions.H == NULL - || signing_context->hash_functions.H_msg == NULL || signing_context->hash_functions.PRF == NULL - || signing_context->hash_functions.PRFkeygen == NULL || signing_context->hash_functions.PRFindex == NULL) { + if (signing_context->hash_functions == NULL) { return XMSS_ERR_BAD_CONTEXT; } #else - if (signing_context->pad_hash_abstraction[0] != NULL || signing_context->pad_hash_abstraction[1] != NULL - || signing_context->pad_hash_abstraction[2] != NULL || signing_context->pad_hash_abstraction[3] != NULL - || signing_context->pad_hash_abstraction[4] != NULL || signing_context->pad_hash_abstraction[5] != NULL - || signing_context->pad_hash_abstraction[6] != NULL|| signing_context->pad_hash_abstraction[7] != NULL) { + if (signing_context->pad_hash_functions != NULL) { return XMSS_ERR_BAD_CONTEXT; } #endif @@ -222,7 +180,7 @@ static XmssError check_signing_context(const XmssSigningContext *signing_context return XMSS_ERR_BAD_CONTEXT; } if (signing_context->parameter_set != signing_context->redundant_parameter_set) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } return XMSS_OKAY; @@ -235,7 +193,7 @@ static XmssError check_signing_context(const XmssSigningContext *signing_context * @details * Should only be used for supported parameter sets. * - * @param parameter_set The parameter set for which to get the index space size. + * @param[in] parameter_set The parameter set for which to get the index space size. * * @returns The size of the index space for the given parameter set, or 0 if parameter set is unsupported. */ @@ -299,7 +257,7 @@ static XmssError partitions_to_tree_height(uint32_t *height_out, uint32_t number * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_BAD_CONTEXT The key_context is uninitialized. */ -static XmssError check_key_context_well_formed(const XmssKeyContext *const restrict key_context) +static XmssError check_key_context_well_formed(const XmssKeyContext *const key_context) { if (key_context == NULL) { return XMSS_ERR_NULL_POINTER; @@ -312,7 +270,7 @@ static XmssError check_key_context_well_formed(const XmssKeyContext *const restr || key_context->private_stateful.partition_end != key_context->redundant_private_stateful.partition_end || memcmp(key_context->private_key_digest.data, key_context->redundant_private_key_digest.data, sizeof(XmssValue256)) != 0) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } return check_signing_context(&key_context->context); @@ -354,93 +312,6 @@ void xmss_free_key_context(XmssKeyContext *key_context) } } -/** - * @brief - * Return values for compare_values_256. - * - * @details - * The values are chosen to be both non-zero and with large hamming distance to be resilient to bit errors. - */ -typedef enum { - /** @brief Values are equal. */ - VALUES_ARE_EQUAL = XMSS_DISTANT_VALUE_1, - /** @brief Values are not equal. */ - VALUES_ARE_NOT_EQUAL = XMSS_DISTANT_VALUE_2 -} ValueCompareResult; - -/** - * @brief - * Compare two 256-bit values in a bit error resilient way. - * - * @details - * This function is written in such a way that a single random bit error cannot cause the function to return - * VALUES_ARE_EQUAL when they are not equal. If the return value of this function is checked, it should be stored - * in a volatile variable which is then checked twice, to ensure that a bit error cannot skip the check. - * - * @warning - * It is the caller's responsibility to ensure that both pointers are valid. This is not checked. - * - * @param[in] value1 The first value. - * @param[in] value2 The second value. - * @retval VALUES_ARE_EQUAL The values are equal. - * @retval VALUES_ARE_NOT_EQUAL The values are not equal. - */ -static inline ValueCompareResult compare_values_256(const XmssValue256 *const value1, const XmssValue256 *const value2) -{ - assert(value1 != NULL && value2 != NULL); - volatile uint8_t difference = 0; - - for (uint_fast8_t i = 0; i < sizeof(XmssValue256); i++) { - difference |= value1->data[i] ^ value2->data[i]; - } - if (difference) { - return VALUES_ARE_NOT_EQUAL; - } - - /* Repeat the check so that a single bit error cannot cause us to wrongly output VALUES_ARE_EQUAL. - * Because every loop iteration updates a volatile variable, the compiler is not allowed to optimize away this - * second loop. - * - * Note: It is technically allowed for the compiler to cache value1->data[i] ^ value2->data[i] from the first - * loop. If value1 and value2 differ in exactly one bit and a random bit error causes that bit to flip, and the - * compiler uses this optimization, then a single bit-flip *could* cause the function to wrongly output - * VALUES_ARE_EQUAL. We consider the combined probability of these events negligible. */ - for (uint_fast8_t i = 0; i < sizeof(XmssValue256); i++) { - difference |= value1->data[i] ^ value2->data[i]; - } - if (difference) { - return VALUES_ARE_NOT_EQUAL; - } - - return VALUES_ARE_EQUAL; -} - -/** - * @brief - * Compare two native 256-bit values in a bit error resilient way. - * - * @details - * This function is written in such a way that a single random bit error cannot cause the function to return - * VALUES_ARE_EQUAL when they are not equal. If the return value of this function is checked, it should be stored - * in a volatile variable which is then checked twice, to ensure that a bit error cannot skip the check. - * - * @warning - * It is the caller's responsibility to ensure that both pointers are valid. This is not checked. - * - * @param[in] value1 The first value. - * @param[in] value2 The second value. - * @retval VALUES_ARE_EQUAL The values are equal. - * @retval VALUES_ARE_NOT_EQUAL The values are not equal. - */ -static inline ValueCompareResult compare_native_values_256(const XmssNativeValue256 *const value1, const XmssNativeValue256 *const value2) -{ - /** - * XmssNativeValue256 can be safely cast to XmssValue256 as the former has stricter alignment requirements, and - * the types have the same size. - */ - return compare_values_256((const XmssValue256 *)value1, (const XmssValue256 *)value2); -} - /** * @brief * Check that the integrity digest of a blob matches the content. @@ -460,17 +331,14 @@ static inline ValueCompareResult compare_native_values_256(const XmssNativeValue * @retval VALUES_ARE_EQUAL The digests match. * @retval VALUES_ARE_NOT_EQUAL The digests do not match. */ -static inline ValueCompareResult check_integrity_digest( - HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - const XmssValue256 *const restrict integrity_digest, const uint8_t *const restrict data, const size_t size) +static inline ValueCompareResult check_integrity_digest(HASH_FUNCTIONS_PARAMETER + const XmssValue256 *const integrity_digest, const uint8_t *const data, const size_t size) { -#if XMSS_ENABLE_HASH_ABSTRACTION - assert(hash_functions != NULL); -#endif + ASSERT_HASH_FUNCTIONS(); assert(integrity_digest != NULL && data != NULL); - XmssValue256 digest; /* Uninitialized for performance reasons. */ - xmss_digest(HASH_ABSTRACTION(hash_functions) &digest, data, size); + XmssValue256 digest = { 0 }; + xmss_digest(HASH_FUNCTIONS &digest, data, size); /* A bit error in size could lead to undefined behavior. We do not check for it, because in practice, reading an * incorrect number of bytes will lead either to an incorrect integrity digest, or to a segfault due to an * out-of-bounds read. */ @@ -491,7 +359,7 @@ static inline ValueCompareResult check_integrity_digest( * @retval XMSS_OKAY The check passed. * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_BAD_CONTEXT Some part of the key_context did not match expectation. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. * (Note that bit errors can also cause different errors or segfaults.) */ static XmssError index_permutation_check(const XmssKeyContext *const key_context) @@ -504,7 +372,7 @@ static XmssError index_permutation_check(const XmssKeyContext *const key_context REDUNDANT_RETURN_ERR(result); if (key_context->private_stateless.index_obfuscation_setting == XMSS_INDEX_OBFUSCATION_OFF) { - return XMSS_OKAY; + return result; } const uint32_t index_space_size = 1 << XMSS_TREE_DEPTH(key_context->context.parameter_set); @@ -527,25 +395,25 @@ static XmssError index_permutation_check(const XmssKeyContext *const key_context check_square_sum += ((uint64_t)key_context->obfuscation[index]) * key_context->obfuscation[index]; } if (check_sum != (uint64_t)(index_space_size - 1) * (index_space_size / 2)) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } if (check_square_sum != - (uint64_t)(index_space_size - 1) * (index_space_size) * (2 * (index_space_size - 1) + 1) / 6) { - return XMSS_ERR_BIT_ERROR_DETECTED; + (uint64_t)(index_space_size - 1) * (index_space_size) * (2 * (index_space_size - 1ull) + 1) / 6) { + return XMSS_ERR_FAULT_DETECTED; } /* Recompute the digest to check if it matches the digest in the stateless key part. * This does not use the check_integrity_digest function as a native digest function is used. */ XmssNativeValue256 check_digest = {0}; - xmss_native_digest(HASH_ABSTRACTION(&key_context->context.hash_functions) - &check_digest, key_context->obfuscation, index_space_size); + xmss_native_digest(HASH_FUNCTIONS_FROM(key_context->context) &check_digest, key_context->obfuscation, + index_space_size); volatile ValueCompareResult digest_comparison = compare_native_values_256(&check_digest, &key_context->private_stateless.obfuscation_integrity); - REDUNDANT_RETURN_IF(digest_comparison != VALUES_ARE_EQUAL, XMSS_ERR_BIT_ERROR_DETECTED); + REDUNDANT_RETURN_IF(digest_comparison != VALUES_ARE_EQUAL, XMSS_ERR_FAULT_DETECTED); /* All checks succeeded. */ - return XMSS_OKAY; + return result; } /** @@ -579,8 +447,8 @@ static XmssError populate_index_obfuscation_permutation(XmssKeyContext *key_cont return XMSS_ERR_BAD_CONTEXT; } uint32_t index_space_size = 1 << XMSS_TREE_DEPTH(key_context->context.parameter_set); - return generate_pseudorandom_permutation(HASH_ABSTRACTION(&key_context->context.hash_functions) key_context->obfuscation, - index_space_size, &key_context->private_stateless.index_obfuscation_random, + return generate_pseudorandom_permutation(HASH_FUNCTIONS_FROM(key_context->context) + key_context->obfuscation, index_space_size, &key_context->private_stateless.index_obfuscation_random, &key_context->private_stateless.seed); } @@ -607,12 +475,12 @@ static XmssError populate_index_obfuscation_permutation(XmssKeyContext *key_cont * @retval XMSS_OKAY Success. * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_BAD_CONTEXT The context did not match expectation. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ static XmssError generate_index_obfuscation_permutation(XmssKeyContext *key_context) { - XmssError result = populate_index_obfuscation_permutation(key_context); + volatile XmssError result = populate_index_obfuscation_permutation(key_context); REDUNDANT_RETURN_ERR(result); @@ -621,12 +489,13 @@ static XmssError generate_index_obfuscation_permutation(XmssKeyContext *key_cont * one generated using the same 32-bit words on a system with different endianness. */ uint32_t index_space_size = 1 << XMSS_TREE_DEPTH(key_context->context.parameter_set); - xmss_native_digest(HASH_ABSTRACTION(&key_context->context.hash_functions) + xmss_native_digest(HASH_FUNCTIONS_FROM(key_context->context) &key_context->private_stateless.obfuscation_integrity, key_context->obfuscation, index_space_size); /* Re-run the permutation generation algorithm to ensure that if bit errors affect the deterministic nature of * the pseudo-random generation, an error will be returned. */ + result = XMSS_UNINITIALIZED; result = populate_index_obfuscation_permutation(key_context); REDUNDANT_RETURN_ERR(result); } @@ -647,11 +516,11 @@ static XmssError generate_index_obfuscation_permutation(XmssKeyContext *key_cont * @retval XMSS_OKAY Success. * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_BAD_CONTEXT The context did not match expectations. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ static XmssError load_index_obfuscation_permutation(XmssKeyContext *key_context) { - XmssError result = populate_index_obfuscation_permutation(key_context); + volatile XmssError result = populate_index_obfuscation_permutation(key_context); REDUNDANT_RETURN_ERR(result); /* Use index_permutation_check to verify the check-sums, and re-compute the digest and compare it. */ return index_permutation_check(key_context); @@ -679,16 +548,16 @@ static XmssError load_index_obfuscation_permutation(XmssKeyContext *key_context) * @retval XMSS_OKAY obfuscated_index was successfully set. * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_BAD_CONTEXT The permutation data wasn't correctly populated in the key_context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -static XmssError obfuscate_index(volatile uint32_t *obfuscated_index, - const XmssKeyContext *const key_context, const uint32_t index) +static XmssError obfuscate_index(volatile uint32_t *obfuscated_index, const XmssKeyContext *const key_context, + const uint32_t index) { if (key_context == NULL || obfuscated_index == NULL) { return XMSS_ERR_NULL_POINTER; } - XmssError result = index_permutation_check(key_context); + volatile XmssError result = index_permutation_check(key_context); REDUNDANT_RETURN_ERR(result); if (key_context->private_stateless.index_obfuscation_setting == XMSS_INDEX_OBFUSCATION_OFF) { @@ -721,22 +590,20 @@ static XmssError obfuscate_index(volatile uint32_t *obfuscated_index, * @retval XMSS_ERR_NULL_POINTER private_key is NULL. * @retval XMSS_ERR_INVALID_BLOB The size, integrity digest or version are incorrect. * @retval XMSS_ERR_ARGUMENT_MISMATCH expected_scheme_identifier does not match. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -static XmssError verify_private_key_stateless_internal( - HASH_ABSTRACTION(const xmss_hashes *const hash_functions) - const XmssPrivateKeyStatelessBlob *const private_key, - const uint32_t expected_scheme_identifier, +static XmssError verify_private_key_stateless_internal(HASH_FUNCTIONS_PARAMETER + const XmssPrivateKeyStatelessBlob *const private_key, const uint32_t expected_scheme_identifier, const uint32_t redundant_scheme_identifier) { /* To protect against bit errors in the CPU's flag register, we execute some if-statements in this function twice. * The variables being checked are volatile, so the compiler is not allowed to optimize away the redundant if. */ - volatile ValueCompareResult value_cmp = VALUES_ARE_EQUAL; + volatile ValueCompareResult value_cmp = VALUES_ARE_NOT_EQUAL; XmssPrivateKeyStateless *private_key_inner = NULL; if (expected_scheme_identifier != redundant_scheme_identifier) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* No redundant NULL pointer check because a bit error can only lead to a segfault here. */ @@ -758,16 +625,16 @@ static XmssError verify_private_key_stateless_internal( convert_big_endian_word(XMSS_VERSION_CURRENT_PRIVATE_KEY_STATELESS_STORAGE)) { return XMSS_ERR_INVALID_BLOB; } - value_cmp = check_integrity_digest(HASH_ABSTRACTION(hash_functions) &private_key_inner->integrity, + value_cmp = check_integrity_digest(HASH_FUNCTIONS &private_key_inner->integrity, private_key->data + sizeof(private_key_inner->integrity), private_key->data_size - sizeof(private_key_inner->integrity)); - REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_BIT_ERROR_DETECTED); + REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_FAULT_DETECTED); if (convert_big_endian_word(private_key_inner->scheme_identifier) != expected_scheme_identifier) { return XMSS_ERR_ARGUMENT_MISMATCH; } if (convert_big_endian_word(private_key_inner->redundant_scheme_identifier) != redundant_scheme_identifier) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } return XMSS_OKAY; } @@ -777,8 +644,8 @@ XmssError xmss_verify_private_key_stateless(const XmssPrivateKeyStatelessBlob *c { volatile XmssError result = check_signing_context(context); REDUNDANT_RETURN_ERR(result); - return verify_private_key_stateless_internal(HASH_ABSTRACTION(&context->hash_functions) private_key, - context->parameter_set, context->redundant_parameter_set); + return verify_private_key_stateless_internal(HASH_FUNCTIONS_FROM(*context) private_key, context->parameter_set, + context->redundant_parameter_set); } /** @@ -795,16 +662,16 @@ XmssError xmss_verify_private_key_stateless(const XmssPrivateKeyStatelessBlob *c * @retval XMSS_ERR_NULL_POINTER key_stateful or context is NULL. * @retval XMSS_ERR_ARGUMENT_MISMATCH The signing context and the blob are incompatible. * @retval XMSS_ERR_INVALID_BLOB The digest did not verify or the blob did not conform to expectations. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -static XmssError xmss_verify_private_key_stateful_blob( - const XmssPrivateKeyStatefulBlob *key_stateful, const XmssSigningContext *context) +static XmssError xmss_verify_private_key_stateful_blob(const XmssPrivateKeyStatefulBlob *const key_stateful, + const XmssSigningContext *const context) { /* To protect against bit errors in the CPU's flag register, we execute some if-statements in this function twice. * The variables being checked are volatile, so the compiler is not allowed to optimize away the redundant if. */ - volatile ValueCompareResult value_cmp = VALUES_ARE_EQUAL; + volatile ValueCompareResult value_cmp = VALUES_ARE_NOT_EQUAL; XmssError result = check_signing_context(context); REDUNDANT_RETURN_ERR(result); @@ -825,15 +692,15 @@ static XmssError xmss_verify_private_key_stateful_blob( } if (key_stateful_inner->redundant_version != convert_big_endian_word(XMSS_VERSION_CURRENT_PRIVATE_KEY_STATEFUL_STORAGE)) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* Check the partition. */ if (key_stateful_inner->contents.partition_start != key_stateful_inner->redundant_contents.partition_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } if (key_stateful_inner->contents.partition_end != key_stateful_inner->redundant_contents.partition_end) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* Check the scheme identifier. */ @@ -841,29 +708,29 @@ static XmssError xmss_verify_private_key_stateful_blob( return XMSS_ERR_ARGUMENT_MISMATCH; } if (key_stateful_inner->redundant_scheme_identifier != convert_big_endian_word(context->redundant_parameter_set)) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } if (key_stateful_inner->scheme_identifier != key_stateful_inner->redundant_scheme_identifier) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } - value_cmp = check_integrity_digest(HASH_ABSTRACTION(&context->hash_functions) &key_stateful_inner->integrity, + value_cmp = check_integrity_digest(HASH_FUNCTIONS_FROM(*context) &key_stateful_inner->integrity, key_stateful->data + sizeof(key_stateful_inner->integrity), key_stateful->data_size - sizeof(key_stateful_inner->integrity)); - REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_BIT_ERROR_DETECTED); + REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_FAULT_DETECTED); return XMSS_OKAY; } -XmssError xmss_verify_private_key_stateful( - const XmssPrivateKeyStatefulBlob *key_usage, const XmssPrivateKeyStatelessBlob *private_key, - const XmssKeyContext *key_context, const XmssSigningContext *signing_context) +XmssError xmss_verify_private_key_stateful(const XmssPrivateKeyStatefulBlob *const key_usage, + const XmssPrivateKeyStatelessBlob *const private_key, const XmssKeyContext *const key_context, + const XmssSigningContext *const signing_context) { /* To protect against bit errors in the CPU's flag register, we execute some if-statements in this function twice. * The variables being checked are volatile, so the compiler is not allowed to optimize away the redundant if. */ const XmssSigningContext *context = NULL; - volatile ValueCompareResult value_cmp = VALUES_ARE_EQUAL; + volatile ValueCompareResult value_cmp = VALUES_ARE_NOT_EQUAL; /* No redundant NULL pointer checks because a bit error here can only lead to a segfault. */ if (key_usage == NULL) { @@ -894,6 +761,7 @@ XmssError xmss_verify_private_key_stateful( volatile bool verified_against_key_ctx = false; volatile bool verified_against_private_key = false; if (key_context != NULL) { + result = XMSS_UNINITIALIZED; result = check_key_context_well_formed(key_context); REDUNDANT_RETURN_ERR(result); @@ -918,6 +786,7 @@ XmssError xmss_verify_private_key_stateful( value_cmp = compare_values_256(&stateless->integrity, &stateful->digest_of_private_key_static_blob); REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_ARGUMENT_MISMATCH); + result = XMSS_UNINITIALIZED; result = xmss_verify_private_key_stateless(private_key, context); REDUNDANT_RETURN_ERR(result); @@ -926,9 +795,9 @@ XmssError xmss_verify_private_key_stateful( /* Verify that a bit error didn't cause us to skip a check. */ if (verified_against_key_ctx != (key_context != NULL) || verified_against_private_key != (private_key != NULL)) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } - return XMSS_OKAY; + return result; } XmssError xmss_get_caching_in_public_key(XmssCacheType *const cache_type, uint32_t *const cache_level, @@ -943,7 +812,7 @@ XmssError xmss_get_caching_in_public_key(XmssCacheType *const cache_type, uint32 XmssPublicKeyInternal *pub_key_data = (XmssPublicKeyInternal *)pub_key->data; *cache_type = (XmssCacheType)pub_key_data->cache_type; *cache_level = pub_key_data->cache_level; - return XMSS_OKAY; + return err_code; } /** @@ -963,18 +832,15 @@ XmssError xmss_get_caching_in_public_key(XmssCacheType *const cache_type, uint32 * @param[in] context The signing context. * @retval XMSS_OKAY Success. * @retval XMSS_ERR_NULL_POINTER key_context, stateless, stateless or context is NULL. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -static XmssError xmss_load_private_key_internal( - XmssKeyContext *key_context, const XmssPrivateKeyStateless *const stateless, - const XmssPrivateKeyStateful *const stateful, const XmssSigningContext *const context) +static XmssError xmss_load_private_key_internal(XmssKeyContext *const key_context, + const XmssPrivateKeyStateless *const stateless, const XmssPrivateKeyStateful *const stateful, + const XmssSigningContext *const context) { - volatile ValueCompareResult digest_cmp = VALUES_ARE_EQUAL; - XmssError err_code = XMSS_OKAY; -#if XMSS_ENABLE_HASH_ABSTRACTION - xmss_hashes hash_functions; -#endif + volatile ValueCompareResult digest_cmp = VALUES_ARE_NOT_EQUAL; + XmssError err_code = XMSS_UNINITIALIZED; if (key_context == NULL || stateless == NULL || stateful == NULL || context == NULL) { return XMSS_ERR_NULL_POINTER; @@ -982,7 +848,7 @@ static XmssError xmss_load_private_key_internal( key_context->initialized = (uint32_t)XMSS_INITIALIZATION_UNINITIALIZED; key_context->pad_ = 0; - memcpy(&key_context->context, context, sizeof(key_context->context)); + key_context->context = *context; key_context->private_stateful.partition_start = convert_big_endian_word(stateful->contents.partition_start); key_context->private_stateful.partition_end = convert_big_endian_word(stateful->contents.partition_end); key_context->redundant_private_stateful.partition_start @@ -993,7 +859,7 @@ static XmssError xmss_load_private_key_internal( key_context->reserved_signatures_start = key_context->private_stateful.partition_start; key_context->redundant_reserved_signatures_start = key_context->redundant_private_stateful.partition_start; if (key_context->reserved_signatures_start != key_context->redundant_reserved_signatures_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } { big_endian_to_native_256(&key_context->private_stateless.prf_seed, @@ -1008,10 +874,10 @@ static XmssError xmss_load_private_key_internal( convert_big_endian_word(stateless->contents.index_obfuscation_setting); big_endian_to_native_256(&key_context->private_stateless.index_obfuscation_random, (const XmssValue256 *)&stateless->contents.index_obfuscation_random); - value_256_copy(&key_context->private_key_digest, &stateless->integrity); + key_context->private_key_digest = stateless->integrity; + key_context->redundant_private_key_digest = stateless->integrity; key_context->context.parameter_set = convert_big_endian_word(stateless->scheme_identifier); key_context->context.redundant_parameter_set = convert_big_endian_word(stateless->scheme_identifier); - value_256_copy(&key_context->redundant_private_key_digest, &stateless->integrity); XmssError result = load_index_obfuscation_permutation(key_context); if (result != XMSS_OKAY) { context->zeroize(key_context, XMSS_KEY_CONTEXT_SIZE(context->parameter_set, @@ -1026,30 +892,30 @@ static XmssError xmss_load_private_key_internal( || key_context->private_stateful.partition_end != key_context->redundant_private_stateful.partition_end || compare_values_256(&key_context->private_key_digest, &key_context->redundant_private_key_digest) != VALUES_ARE_EQUAL) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* Recalculate the integrity hash of the stateless part. This is to verify that there have been no bit errors in * the private key since the integrity hash was calculated, and that it was correctly copied to key_context. */ - err_code = xmss_get_hash_functions(HASH_ABSTRACTION(&hash_functions) - (XmssParameterSetOID)key_context->context.parameter_set); + DEFINE_HASH_FUNCTIONS; + err_code = INITIALIZE_HASH_FUNCTIONS((XmssParameterSetOID)key_context->context.parameter_set); if (err_code != XMSS_OKAY) { return err_code; } - digest_cmp = check_integrity_digest(HASH_ABSTRACTION(&hash_functions) &key_context->private_key_digest, + digest_cmp = check_integrity_digest(HASH_FUNCTIONS &key_context->private_key_digest, (uint8_t *)stateless + sizeof(stateless->integrity), sizeof(XmssPrivateKeyStateless) - sizeof(stateless->integrity)); /* Checking the return value only once because the digests can only be different if a bit error already happened. */ if (digest_cmp != VALUES_ARE_EQUAL) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } key_context->initialized = (uint32_t)XMSS_INITIALIZATION_INITIALIZED; - return XMSS_OKAY; + return err_code; } -XmssError xmss_load_private_key( - XmssKeyContext **const key_context, const XmssPrivateKeyStatelessBlob *const private_key, +XmssError xmss_load_private_key(XmssKeyContext **const key_context, + const XmssPrivateKeyStatelessBlob *const private_key, const XmssPrivateKeyStatefulBlob *const key_usage, const XmssSigningContext *const context) { /* To protect against bit errors in the CPU's flag register, we execute some if-statements in this function twice. @@ -1066,6 +932,7 @@ XmssError xmss_load_private_key( */ volatile XmssError result = xmss_verify_private_key_stateless(private_key, context); REDUNDANT_RETURN_ERR(result); + result = XMSS_UNINITIALIZED; result = xmss_verify_private_key_stateful(key_usage, private_key, NULL, context); REDUNDANT_RETURN_ERR(result); @@ -1083,6 +950,7 @@ XmssError xmss_load_private_key( /* clear the contents of the key_context structure. */ memset(*key_context, 0, key_context_size); + result = XMSS_UNINITIALIZED; result = xmss_load_private_key_internal(*key_context, private_key_inner, key_usage_inner, context); REDUNDANT_RETURN_IF(result == XMSS_OKAY, XMSS_OKAY); @@ -1108,9 +976,9 @@ XmssError xmss_load_private_key( * @retval XMSS_ERR_BAD_CONTEXT context is not a correctly initialized context. * @retval XMSS_ERR_INVALID_ARGUMENT An invalid index_obfuscation_setting is passed. */ -static XmssError generate_private_key_internal( - const XmssSigningContext *context, XmssKeyContext **key_context, const XmssBuffer *secure_random_seeds, - const XmssIndexObfuscationSetting index_obfuscation_setting, const XmssBuffer *index_obfuscation_random) +static XmssError generate_private_key_internal(const XmssSigningContext *const context, + XmssKeyContext **key_context, const XmssBuffer *const secure_random_seeds, + const XmssIndexObfuscationSetting index_obfuscation_setting, const XmssBuffer *const index_obfuscation_random) { XmssError result = check_signing_context(context); if (result != XMSS_OKAY) { @@ -1170,7 +1038,7 @@ static XmssError generate_private_key_internal( memset(reallocated_key_context, 0, key_context_size); /* Set the signing context */ - memcpy(&reallocated_key_context->context, context, sizeof(reallocated_key_context->context)); + reallocated_key_context->context = *context; /* initialize the stateless part */ { XmssPrivateKeyStatelessContents *stateless_part = &reallocated_key_context->private_stateless; @@ -1206,6 +1074,7 @@ static XmssError generate_private_key_internal( = reallocated_key_context->redundant_private_stateful.partition_start; /* Generate the index obfuscation permutation. */ + result = XMSS_UNINITIALIZED; result = generate_index_obfuscation_permutation(reallocated_key_context); if (result != XMSS_OKAY) { /* zeroizes the key context, frees it and sets the pointer to NULL. */ @@ -1217,7 +1086,7 @@ static XmssError generate_private_key_internal( /* Set the state to initialized (without public key) */ reallocated_key_context->initialized = (uint32_t)XMSS_INITIALIZATION_INITIALIZED; reallocated_key_context->pad_ = 0; - return XMSS_OKAY; + return result; } /** @@ -1231,15 +1100,15 @@ static XmssError generate_private_key_internal( * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. * @retval XMSS_ERR_BAD_CONTEXT context is not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -static XmssError export_private_key_stateless(XmssKeyContext *key_context, +static XmssError export_private_key_stateless(const XmssKeyContext *const key_context, XmssPrivateKeyStatelessBlob **stateless_blob) { XmssPrivateKeyStatelessBlob *reallocated_stateless_blob = NULL; - XmssError result = check_key_context_well_formed(key_context); + volatile XmssError result = check_key_context_well_formed(key_context); REDUNDANT_RETURN_ERR(result); if (stateless_blob == NULL) { @@ -1262,7 +1131,7 @@ static XmssError export_private_key_stateless(XmssKeyContext *key_context, stateless->redundant_scheme_identifier = convert_big_endian_word(key_context->context.redundant_parameter_set); /* Copy the stateless_contents, and perform the necessary endianness operations in-place. */ - memcpy(&stateless->contents, &key_context->private_stateless, sizeof(stateless->contents)); + stateless->contents = key_context->private_stateless; /* Canonicalize the byte-order of all applicable fields for export. */ inplace_native_to_big_endian_256(&stateless->contents.prf_seed); inplace_native_to_big_endian_256(&stateless->contents.private_key_seed); @@ -1277,9 +1146,8 @@ static XmssError export_private_key_stateless(XmssKeyContext *key_context, /* Compute the digest over the contents of the XmssPrivateKeyStateless structure excluding the integrity digest * field itself. */ - xmss_digest(HASH_ABSTRACTION(&key_context->context.hash_functions) &stateless->integrity, - ((uint8_t *)stateless) + sizeof(stateless->integrity), - sizeof(*stateless) - sizeof(stateless->integrity)); + xmss_digest(HASH_FUNCTIONS_FROM(key_context->context) &stateless->integrity, + ((uint8_t *)stateless) + sizeof(stateless->integrity), sizeof(*stateless) - sizeof(stateless->integrity)); /* Check for bit errors. */ if (convert_big_endian_word(stateless->private_key_stateless_version) @@ -1292,12 +1160,12 @@ static XmssError export_private_key_stateless(XmssKeyContext *key_context, ) { key_context->context.free(reallocated_stateless_blob); *stateless_blob = NULL; - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } } *stateless_blob = reallocated_stateless_blob; - return XMSS_OKAY; + return result; } /** @@ -1313,11 +1181,11 @@ static XmssError export_private_key_stateless(XmssKeyContext *key_context, * @retval XMSS_ERR_NULL_POINTER A NULL pointer was passed. * @retval XMSS_ERR_ALLOC_ERROR Memory allocation caused an error. * @retval XMSS_ERR_BAD_CONTEXT context is not a correctly initialized context. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected. - * (Note that bit errors can also cause different errors or segfaults.) + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected. + * (Note that bit errors can also cause different errors or segfaults.) */ -static XmssError export_private_key_stateful(const XmssKeyContext *const restrict key_context, - XmssPrivateKeyStatefulBlob **const restrict stateful_blob) +static XmssError export_private_key_stateful(const XmssKeyContext *const key_context, + XmssPrivateKeyStatefulBlob **const stateful_blob) { XmssError result = check_key_context_well_formed(key_context); if (result != XMSS_OKAY) { @@ -1344,7 +1212,7 @@ static XmssError export_private_key_stateful(const XmssKeyContext *const restric /* Copy the digest over the stateless blob from the key_context, as the Stateless key blob needs not be * available for export. */ - value_256_copy(&stateful->digest_of_private_key_static_blob, &key_context->private_key_digest); + stateful->digest_of_private_key_static_blob = key_context->private_key_digest; /* Convert the integer values to big-endian. */ stateful->private_key_stateful_version = @@ -1363,9 +1231,8 @@ static XmssError export_private_key_stateful(const XmssKeyContext *const restric /* Compute the digest over the contents of the XmssPrivateKeyStateful structure excluding the integrity digest * field itself. */ - xmss_digest(HASH_ABSTRACTION(&key_context->context.hash_functions) &stateful->integrity, - ((uint8_t*)stateful) + sizeof(stateful->integrity), - sizeof(*stateful) - sizeof(stateful->integrity)); + xmss_digest(HASH_FUNCTIONS_FROM(key_context->context) &stateful->integrity, + ((uint8_t*)stateful) + sizeof(stateful->integrity), sizeof(*stateful) - sizeof(stateful->integrity)); /* Check for bit errors. */ if (convert_big_endian_word(stateful->private_key_stateful_version) @@ -1385,15 +1252,15 @@ static XmssError export_private_key_stateful(const XmssKeyContext *const restric ) { key_context->context.free(reallocated_stateful_blob); *stateful_blob = NULL; - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } } *stateful_blob = reallocated_stateful_blob; - return XMSS_OKAY; + return result; } -XmssError xmss_export_public_key(XmssPublicKeyBlob **exported_pub_key, const XmssKeyContext *key_context) +XmssError xmss_export_public_key(XmssPublicKey *const exported_pub_key, const XmssKeyContext *const key_context) { XmssError result = check_key_context_well_formed(key_context); if (result != XMSS_OKAY) { @@ -1407,33 +1274,21 @@ XmssError xmss_export_public_key(XmssPublicKeyBlob **exported_pub_key, const Xms return XMSS_ERR_NO_PUBLIC_KEY; } - XmssPublicKeyBlob *public_key_reallocated = key_context->context.realloc(*exported_pub_key, - XMSS_PUBLIC_KEY_BLOB_SIZE); + native_to_big_endian_256(&exported_pub_key->root, &key_context->public_key_root); + native_to_big_endian_256(&exported_pub_key->seed, &key_context->private_stateless.seed); + exported_pub_key->scheme_identifier = convert_big_endian_word(key_context->context.parameter_set); - if (public_key_reallocated == NULL) { - return XMSS_ERR_ALLOC_ERROR; - } - *exported_pub_key = public_key_reallocated; - memset(public_key_reallocated, 0, XMSS_PUBLIC_KEY_BLOB_SIZE); - - public_key_reallocated->data_size = sizeof(XmssPublicKey); - native_to_big_endian_256(&public_key_reallocated->data.public_key, &key_context->public_key_root); - native_to_big_endian_256(&public_key_reallocated->data.seed, &key_context->private_stateless.seed); - public_key_reallocated->data.scheme_identifier = convert_big_endian_word(key_context->context.parameter_set); - - return XMSS_OKAY; + return result; } -XmssError xmss_verify_exported_public_key(const XmssPublicKeyBlob *exported_pub_key, const XmssKeyContext *key_context) +XmssError xmss_verify_exported_public_key(const XmssPublicKey *const exported_pub_key, + const XmssKeyContext *const key_context) { volatile ValueCompareResult value_cmp = VALUES_ARE_NOT_EQUAL; if (exported_pub_key == NULL) { return XMSS_ERR_NULL_POINTER; } - if (exported_pub_key->data_size != XMSS_PUBLIC_KEY_SIZE) { - return XMSS_ERR_INVALID_BLOB; - } XmssError result = check_key_context_well_formed(key_context); if (result != XMSS_OKAY) { @@ -1445,28 +1300,28 @@ XmssError xmss_verify_exported_public_key(const XmssPublicKeyBlob *exported_pub_ const volatile XmssParameterSetOID *const volatile p1 = (const XmssParameterSetOID *)&key_context->context.parameter_set; - const volatile uint32_t *const volatile p2 = &exported_pub_key->data.scheme_identifier; + const volatile uint32_t *const volatile p2 = &exported_pub_key->scheme_identifier; REDUNDANT_RETURN_IF((uint32_t)*p1 != convert_big_endian_word(*p2), XMSS_ERR_ARGUMENT_MISMATCH); { XmssValue256 canonicalized_root; native_to_big_endian_256(&canonicalized_root, &key_context->public_key_root); - value_cmp = compare_values_256(&canonicalized_root, &exported_pub_key->data.public_key); + value_cmp = compare_values_256(&canonicalized_root, &exported_pub_key->root); REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_ARGUMENT_MISMATCH); } { XmssValue256 canonicalized_seed; native_to_big_endian_256(&canonicalized_seed, &key_context->private_stateless.seed); - value_cmp = compare_values_256(&canonicalized_seed, &exported_pub_key->data.seed); + value_cmp = compare_values_256(&canonicalized_seed, &exported_pub_key->seed); REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_ARGUMENT_MISMATCH); } - return XMSS_OKAY; + return result; } -XmssError xmss_generate_private_key( - XmssKeyContext **key_context, XmssPrivateKeyStatelessBlob **private_key, XmssPrivateKeyStatefulBlob **key_usage, - const XmssBuffer *secure_random, const XmssIndexObfuscationSetting index_obfuscation_setting, - const XmssBuffer *random, const XmssSigningContext *context) +XmssError xmss_generate_private_key(XmssKeyContext **const key_context, XmssPrivateKeyStatelessBlob **const private_key, + XmssPrivateKeyStatefulBlob **const key_usage, const XmssBuffer *const secure_random, + const XmssIndexObfuscationSetting index_obfuscation_setting, const XmssBuffer *const random, + const XmssSigningContext *const context) { XmssError result = XMSS_ERR_BAD_CONTEXT; @@ -1491,6 +1346,7 @@ XmssError xmss_generate_private_key( return XMSS_ERR_INVALID_ARGUMENT; } + result = XMSS_UNINITIALIZED; result = generate_private_key_internal(context, key_context, secure_random, index_obfuscation_setting, random); if (result != XMSS_OKAY) { /* @@ -1499,6 +1355,7 @@ XmssError xmss_generate_private_key( return result; } + result = XMSS_UNINITIALIZED; result = export_private_key_stateless(*key_context, private_key); if (result != XMSS_OKAY) { xmss_free_key_context(*key_context); @@ -1509,13 +1366,14 @@ XmssError xmss_generate_private_key( /* Copy the digest from the private key stateless part into the key context. */ { XmssPrivateKeyStateless *private_key_internal = ((XmssPrivateKeyStateless *)(*private_key)->data); - value_256_copy(&(*key_context)->private_key_digest, &private_key_internal->integrity); - value_256_copy(&(*key_context)->redundant_private_key_digest, &private_key_internal->integrity); + (*key_context)->private_key_digest = private_key_internal->integrity; + (*key_context)->redundant_private_key_digest = private_key_internal->integrity; } /* The XmssPrivateKeyStatelessBlob digest completes the key context. */ (*key_context)->initialized = (uint32_t)XMSS_INITIALIZATION_INITIALIZED; + result = XMSS_UNINITIALIZED; result = export_private_key_stateful(*key_context, key_usage); if (result != XMSS_OKAY) { xmss_free_key_context(*key_context); @@ -1526,11 +1384,11 @@ XmssError xmss_generate_private_key( return result; } - return XMSS_OKAY; + return result; } -XmssError xmss_request_future_signatures(XmssPrivateKeyStatefulBlob **const restrict new_key_usage, - XmssKeyContext *const restrict key_context, const uint32_t signature_count) +XmssError xmss_request_future_signatures(XmssPrivateKeyStatefulBlob **const new_key_usage, + XmssKeyContext *const key_context, const uint32_t signature_count) { if (new_key_usage == NULL || key_context == NULL) { return XMSS_ERR_NULL_POINTER; @@ -1554,7 +1412,7 @@ XmssError xmss_request_future_signatures(XmssPrivateKeyStatefulBlob **const rest || key_context->private_stateful.partition_end != key_context->redundant_private_stateful.partition_end || key_context->reserved_signatures_start != key_context->redundant_reserved_signatures_start || key_context->private_stateful.partition_start > key_context->private_stateful.partition_end + 1) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } return export_private_key_stateful(key_context, new_key_usage); @@ -1574,8 +1432,7 @@ XmssError xmss_request_future_signatures(XmssPrivateKeyStatefulBlob **const rest * @param[in] key_context Context containing the XMSS key. * @param[in] idx_sig (Obfuscated) index of the signature for which to create an authentication path. */ -static void build_auth(XmssSignature *const restrict signature, const XmssKeyContext *const restrict key_context, - const uint32_t idx_sig) +static void build_auth(XmssSignature *const signature, const XmssKeyContext *const key_context, const uint32_t idx_sig) { assert(key_context != NULL); @@ -1609,7 +1466,7 @@ static void build_auth(XmssSignature *const restrict signature, const XmssKeyCon * during signature generation. * This function is designed to prevent a single random bit error from causing the re-use of a OTS key. Therefore, it * expects to be passed a redundant copy of idx_sig. A second one-time signature is created using the redundant copy. - * If the signatures do not match, both are zeroized and XMSS_ERR_BIT_ERROR_DETECTED is returned. + * If the signatures do not match, both are zeroized and XMSS_ERR_FAULT_DETECTED is returned. * It is still possible for a single bit error in the calculation of the authentication path to result in an invalid * signature. This causes all reserved signatures to go to waste, but does not compromise the integrity of signatures. * @@ -1622,42 +1479,44 @@ static void build_auth(XmssSignature *const restrict signature, const XmssKeyCon * @param[in] idx_sig Index of the signature (after obfuscation). * @param[in] redundant_idx_sig Redundant copy of idx_sig. * @retval XMSS_OKAY Success. - * @retval XMSS_ERR_BIT_ERROR_DETECTED A bit error was detected; note that not all bit errors will be detected. + * @retval XMSS_ERR_FAULT_DETECTED A bit error was detected; note that not all bit errors will be detected. */ -static XmssError tree_sign(XmssSignature *const restrict signature, const XmssKeyContext *const restrict key_context, - const XmssNativeValue256 *const restrict message_digest, const uint32_t idx_sig, const uint32_t redundant_idx_sig) +static XmssError tree_sign(XmssSignature *const signature, const XmssKeyContext *const key_context, + const XmssNativeValue256 *const message_digest, const uint32_t idx_sig, const uint32_t redundant_idx_sig) { assert(signature != NULL); assert(key_context != NULL); assert(message_digest != NULL); - WotspSignature redundant_wotsp_signature; /* Uninitialized for performance reasons. */ + WotspSignature redundant_wotsp_signature = { 0 }; + volatile XmssError result = XMSS_UNINITIALIZED; /* We place the native-endian WOTS+ signature in the signature struct and then change the endianness in place. * We can cast it to (WotspSignature *) because it is 32-bit aligned due to the layout of XmssSignatureBlob. */ - wotsp_sign(&key_context->context, (WotspSignature *)signature->wots_signature, message_digest, + result = wotsp_sign(&key_context->context, (WotspSignature *)signature->wots_signature, message_digest, &key_context->private_stateless.private_key_seed, &key_context->private_stateless.seed, idx_sig); - wotsp_sign(&key_context->context, &redundant_wotsp_signature, message_digest, + REDUNDANT_RETURN_ERR(result); + + result = XMSS_UNINITIALIZED; + result = wotsp_sign(&key_context->context, &redundant_wotsp_signature, message_digest, &key_context->private_stateless.private_key_seed, &key_context->private_stateless.seed, redundant_idx_sig); - if (memcmp(signature->wots_signature[0].data, redundant_wotsp_signature.hashes[0].data, sizeof(WotspSignature)) - != 0) { - key_context->context.zeroize(signature->wots_signature[0].data, sizeof(WotspSignature)); - key_context->context.zeroize(redundant_wotsp_signature.hashes[0].data, sizeof(WotspSignature)); - return XMSS_ERR_BIT_ERROR_DETECTED; + REDUNDANT_RETURN_ERR(result); + + if (memcmp(signature->wots_signature, &redundant_wotsp_signature, sizeof(WotspSignature)) != 0) { + key_context->context.zeroize(signature->wots_signature, sizeof(WotspSignature)); + key_context->context.zeroize(&redundant_wotsp_signature, sizeof(WotspSignature)); + return XMSS_ERR_FAULT_DETECTED; } inplace_native_to_big_endian((uint32_t *)signature->wots_signature, XMSS_WOTSP_LEN * 8); build_auth(signature, key_context, idx_sig); - return XMSS_OKAY; + return result; } -XmssError xmss_sign_message(XmssSignatureBlob **const restrict signature, - XmssKeyContext *const restrict key_context, - const XmssBuffer *const restrict message) +XmssError xmss_sign_message(XmssSignatureBlob **const signature, XmssKeyContext *const key_context, + const XmssBuffer *const message) { - XmssError err_code = XMSS_OKAY; - if (signature == NULL || key_context == NULL || message == NULL) { return XMSS_ERR_NULL_POINTER; } @@ -1669,7 +1528,7 @@ XmssError xmss_sign_message(XmssSignatureBlob **const restrict signature, if (key_context->reserved_signatures_start != key_context->redundant_reserved_signatures_start || key_context->private_stateful.partition_start != key_context->redundant_private_stateful.partition_start || key_context->private_stateful.partition_end != key_context->redundant_private_stateful.partition_end) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } if (key_context->reserved_signatures_start >= key_context->private_stateful.partition_start) { @@ -1690,42 +1549,48 @@ XmssError xmss_sign_message(XmssSignatureBlob **const restrict signature, */ volatile uint32_t obfuscated_idx_sig = 0; volatile uint32_t redundant_obfuscated_idx_sig = 0; - XmssError result = obfuscate_index(&obfuscated_idx_sig, key_context, idx_sig_pre_obfuscation); + volatile XmssError result = obfuscate_index(&obfuscated_idx_sig, key_context, idx_sig_pre_obfuscation); REDUNDANT_RETURN_ERR(result); + result = XMSS_UNINITIALIZED; result = obfuscate_index(&redundant_obfuscated_idx_sig, key_context, redundant_idx_sig_pre_obfuscation); REDUNDANT_RETURN_ERR(result); - Input_H_msg input_h_msg = INIT_INPUT_H_MSG; Input_PRF input_prf = INIT_INPUT_PRF; XmssSignature *signature_data = NULL; - XmssNativeValue256 message_digest; /* Uninitialized for performance reasons. */ + XmssNativeValue256 native_random = { 0 }; + Input_H_msg input = INIT_INPUT_H_MSG; + XmssHMsgCtx ctx = { 0 }; + XmssNativeValue256 message_digest = { 0 }; reallocated_signature->data_size = XMSS_SIGNATURE_SIZE(key_context->context.parameter_set); signature_data = (XmssSignature *)reallocated_signature->data; signature_data->leaf_index = convert_big_endian_word(obfuscated_idx_sig); - native_256_copy(&input_prf.KEY, &key_context->private_stateless.prf_seed); + input_prf.KEY = key_context->private_stateless.prf_seed; input_prf.M.idx_sig_block.idx_sig = obfuscated_idx_sig; - xmss_PRF(HASH_ABSTRACTION(&key_context->context.hash_functions) &input_h_msg.r, &input_prf); + xmss_PRF(HASH_FUNCTIONS_FROM(key_context->context) &native_random, &input_prf); - native_to_big_endian_256(&signature_data->random_bytes, &input_h_msg.r); + native_to_big_endian_256(&signature_data->random_bytes, &native_random); - native_256_copy(&input_h_msg.Root, &key_context->public_key_root); - input_h_msg.idx_sig = obfuscated_idx_sig; - xmss_H_msg(HASH_ABSTRACTION(&key_context->context.hash_functions) &message_digest, &input_h_msg, message->data, - message->data_size); + input.r = native_random; + input.Root = key_context->public_key_root; + input.idx_sig = obfuscated_idx_sig; + xmss_H_msg_init(HASH_FUNCTIONS_FROM(key_context->context) &ctx, &input); + xmss_H_msg_update(HASH_FUNCTIONS_FROM(key_context->context) &ctx, message->data, message->data_size, NULL); + xmss_H_msg_finalize(HASH_FUNCTIONS_FROM(key_context->context) &message_digest, &ctx); - err_code = tree_sign(signature_data, key_context, &message_digest, obfuscated_idx_sig, + result = XMSS_UNINITIALIZED; + result = tree_sign(signature_data, key_context, (XmssNativeValue256 *)&message_digest, obfuscated_idx_sig, redundant_obfuscated_idx_sig); - /* err_code can only be different from XMSS_OkAY if a bit error happened. Since we only protect against a single + /* result can only be different from XMSS_OKAY if a bit error happened. Since we only protect against a single * bit error, we don't need to double-check here. */ - if (err_code != XMSS_OKAY) { + if (result != XMSS_OKAY) { key_context->context.free(reallocated_signature); *signature = NULL; - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } *signature = reallocated_signature; - return XMSS_OKAY; + return result; } XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_partition, @@ -1759,6 +1624,7 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ key_context->private_stateful.partition_end -= new_partition_size; key_context->redundant_private_stateful.partition_end -= new_partition_size; + err_code = XMSS_UNINITIALIZED; err_code = export_private_key_stateful(key_context, updated_current_partition); REDUNDANT_RETURN_ERR(err_code); @@ -1771,7 +1637,8 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ *new_partition = reallocated_new_partition; /* Copy *updated_current_partition to *new_partition, because most fields are the same. */ - memcpy(*new_partition, *updated_current_partition, XMSS_PRIVATE_KEY_STATEFUL_BLOB_SIZE); + memcpy(&(*new_partition)->data, &(*updated_current_partition)->data, (*updated_current_partition)->data_size); + (*new_partition)->data_size = (*updated_current_partition)->data_size; /* Set the start and end for the new partition. */ current_partition_internal = (XmssPrivateKeyStateful *)(*new_partition)->data; @@ -1784,7 +1651,7 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ = convert_big_endian_word(redundant_old_partition_end); /* Update the integrity hash for the new partition. */ - xmss_digest(HASH_ABSTRACTION(&key_context->context.hash_functions) ¤t_partition_internal->integrity, + xmss_digest(HASH_FUNCTIONS_FROM(key_context->context) ¤t_partition_internal->integrity, (*new_partition)->data + sizeof(current_partition_internal->integrity), sizeof(XmssPrivateKeyStateful) - sizeof(current_partition_internal->integrity)); @@ -1798,11 +1665,12 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ if (key_context_check->private_stateful.partition_end + 1 != convert_big_endian_word(new_partition_internal->contents.partition_start) || convert_big_endian_word(new_partition_internal->contents.partition_end) != old_partition_end) { - err_code = XMSS_ERR_BIT_ERROR_DETECTED; + err_code = XMSS_ERR_FAULT_DETECTED; goto fail; } /* Check the redundant fields in key_context. */ + err_code = XMSS_UNINITIALIZED; err_code = check_key_context_well_formed(key_context_check); if (err_code != XMSS_OKAY) { goto fail; @@ -1812,6 +1680,7 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ } /* Check the redundant fields and integrity hashes of the new blobs. */ + err_code = XMSS_UNINITIALIZED; err_code = xmss_verify_private_key_stateful(*new_partition, NULL, key_context, NULL); if (err_code != XMSS_OKAY) { goto fail; @@ -1819,6 +1688,7 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ if (err_code != XMSS_OKAY) { goto fail; } + err_code = XMSS_UNINITIALIZED; err_code = xmss_verify_private_key_stateful(*updated_current_partition, NULL, key_context, NULL); if (err_code != XMSS_OKAY) { goto fail; @@ -1827,11 +1697,11 @@ XmssError xmss_partition_signature_space(XmssPrivateKeyStatefulBlob **const new_ goto fail; } - return XMSS_OKAY; + return err_code; fail: if (err_code == XMSS_OKAY) { - err_code = XMSS_ERR_BIT_ERROR_DETECTED; + err_code = XMSS_ERR_FAULT_DETECTED; } /* Restore key_context to its original state. */ key_context->private_stateful.partition_end = old_partition_end; @@ -1869,14 +1739,14 @@ XmssError xmss_merge_signature_space(XmssPrivateKeyStatefulBlob **const new_key_ if (extension_end < extension_start) { /* Check if a bit error sent us into the wrong branch. */ if (extension_end >= extension_start || redundant_extension_end >= redundant_extension_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } return export_private_key_stateful(key_context, new_key_usage); } /* Check if a bit error caused us to skip the previous branch. */ if (extension_end < extension_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* Check if we can attach the extension at the end. */ @@ -1884,7 +1754,7 @@ XmssError xmss_merge_signature_space(XmssPrivateKeyStatefulBlob **const new_key_ /* Check if a bit error sent us into the wrong branch. */ if (key_context->private_stateful.partition_end + 1 != extension_start || key_context->redundant_private_stateful.partition_end + 1 != redundant_extension_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } key_context->private_stateful.partition_end = extension_end; key_context->redundant_private_stateful.partition_end = redundant_extension_end; @@ -1893,7 +1763,7 @@ XmssError xmss_merge_signature_space(XmssPrivateKeyStatefulBlob **const new_key_ /* Check if a bit error caused us to skip the previous branch. */ if (key_context->redundant_private_stateful.partition_end + 1 == redundant_extension_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* Check if we can attach the extension at the front. */ @@ -1904,7 +1774,7 @@ XmssError xmss_merge_signature_space(XmssPrivateKeyStatefulBlob **const new_key_ /* Check if a bit error sent us into the wrong branch. */ if (extension_end + 1 != key_context->private_stateful.partition_start || redundant_extension_end + 1 != key_context->redundant_private_stateful.partition_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } key_context->private_stateful.partition_start = extension_start; key_context->redundant_private_stateful.partition_start = redundant_extension_start; @@ -1917,7 +1787,7 @@ XmssError xmss_merge_signature_space(XmssPrivateKeyStatefulBlob **const new_key_ /* Check if a bit error caused us to skip the previous branch. */ if (extension_end + 1 == key_context->private_stateful.partition_start) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } return XMSS_ERR_PARTITIONS_NOT_CONSECUTIVE; @@ -1935,11 +1805,12 @@ XmssError xmss_get_signature_count(size_t *const total_count, size_t *const rema REDUNDANT_RETURN_ERR(err_code); *total_count = (size_t)(1llu << XMSS_TREE_DEPTH(key_context->context.parameter_set)); - *remaining_count = key_context->private_stateful.partition_end - key_context->private_stateful.partition_start + 1; - redundant_remaining_count = key_context->redundant_private_stateful.partition_end + *remaining_count = + (size_t)key_context->private_stateful.partition_end - key_context->private_stateful.partition_start + 1; + redundant_remaining_count = (size_t)key_context->redundant_private_stateful.partition_end - key_context->redundant_private_stateful.partition_start + 1; - return (redundant_remaining_count == *remaining_count) ? XMSS_OKAY : XMSS_ERR_BIT_ERROR_DETECTED; + return (redundant_remaining_count == *remaining_count) ? XMSS_OKAY : XMSS_ERR_FAULT_DETECTED; } void xmss_free_key_generation_context(XmssKeyGenerationContext *const key_generation_context) @@ -1951,8 +1822,8 @@ void xmss_free_key_generation_context(XmssKeyGenerationContext *const key_genera } } -XmssError xmss_generate_public_key( - XmssKeyGenerationContext **generation_buffer, XmssInternalCache **cache, XmssInternalCache **generation_cache, +XmssError xmss_generate_public_key(XmssKeyGenerationContext **const generation_buffer, + XmssInternalCache **const cache, XmssInternalCache **const generation_cache, const XmssKeyContext *const key_context, const XmssCacheType cache_type, const uint8_t cache_depth, const uint32_t generation_partitions) { @@ -2043,11 +1914,11 @@ XmssError xmss_generate_public_key( reallocated_generation_buffer->partition_states->state = (uint32_t)XMSS_GENERATION_STATE_PREPARED; } reallocated_generation_buffer->initialized = (uint32_t)XMSS_INITIALIZATION_INITIALIZED; - return XMSS_OKAY; + return result; } -XmssError xmss_calculate_public_key_part( - XmssKeyGenerationContext *generation_buffer, const uint32_t partition_index) +XmssError xmss_calculate_public_key_part(XmssKeyGenerationContext *const generation_buffer, + const uint32_t partition_index) { if (generation_buffer == NULL) { return XMSS_ERR_NULL_POINTER; @@ -2104,6 +1975,7 @@ XmssError xmss_calculate_public_key_part( XMSS_CACHE_ENTRY_OFFSET(cache_to_build->cache_type, cache_to_build->cache_level, param_set, cache_height, index)]; + result = XMSS_UNINITIALIZED; result = xmss_tree_hash(cache_node, generation_buffer->context, NULL, index * (1 << cache_height), cache_height); if (result != XMSS_OKAY) { @@ -2115,6 +1987,7 @@ XmssError xmss_calculate_public_key_part( /* The lowest level of the partition's sub-tree of the top-cache has been filled, now the top-cache * specific fill function can be used to finish the sub-tree. */ + result = XMSS_UNINITIALIZED; result = xmss_fill_top_cache(cache_to_build, key_context, partition_height, partition_index, cache_to_build->cache_level); if (result != XMSS_OKAY) { @@ -2135,6 +2008,7 @@ XmssError xmss_calculate_public_key_part( XMSS_CACHE_ENTRY_OFFSET(generation_buffer->partition_cache->cache_type, generation_buffer->partition_cache->cache_level, generation_buffer->context->context.parameter_set, partition_height, partition_index)]; + result = XMSS_UNINITIALIZED; result = xmss_tree_hash(cache_node, generation_buffer->context, cache_to_use, start_index, partition_height); if (result != XMSS_OKAY) { return result; @@ -2149,7 +2023,7 @@ XmssError xmss_calculate_public_key_part( return XMSS_ERR_PARTITION_DONE; } } - return XMSS_OKAY; + return result; } /** @@ -2166,7 +2040,7 @@ XmssError xmss_calculate_public_key_part( static XmssError xmss_finish_calculate_public_key_internal(XmssKeyContext *const key_context, const XmssKeyGenerationContext *const generation_buffer, const uint32_t partition_height) { - XmssError result = XMSS_OKAY; + XmssError result = XMSS_UNINITIALIZED; if (generation_buffer == NULL || key_context == NULL) { return XMSS_ERR_NULL_POINTER; } @@ -2201,6 +2075,7 @@ static XmssError xmss_finish_calculate_public_key_internal(XmssKeyContext *const } /* If we use top caching, we need to finish its tree. */ if (build_cache_type == XMSS_CACHE_TOP) { + result = XMSS_UNINITIALIZED; result = xmss_fill_top_cache(cache_to_build, key_context, XMSS_TREE_DEPTH(param_set), 0, build_cache_level); if (result != XMSS_OKAY) { @@ -2213,9 +2088,8 @@ static XmssError xmss_finish_calculate_public_key_internal(XmssKeyContext *const XMSS_TREE_DEPTH(generation_buffer->context->context.parameter_set)); } -XmssError xmss_finish_calculate_public_key( - XmssPublicKeyInternalBlob **const public_key, XmssKeyGenerationContext **const generation_buffer, - XmssKeyContext *const key_context) +XmssError xmss_finish_calculate_public_key(XmssPublicKeyInternalBlob **const public_key, + XmssKeyGenerationContext **const generation_buffer, XmssKeyContext *const key_context) { if (public_key == NULL || generation_buffer == NULL || *generation_buffer == NULL || key_context == NULL) { return XMSS_ERR_NULL_POINTER; @@ -2238,6 +2112,7 @@ XmssError xmss_finish_calculate_public_key( /* Determine the tree-height of the partitions. */ uint32_t partition_height = 0; + result = XMSS_UNINITIALIZED; result = partitions_to_tree_height(&partition_height, (uint32_t)(*generation_buffer)->generation_partitions, (XmssParameterSetOID)key_context->context.parameter_set); if (result != XMSS_OKAY) { @@ -2263,6 +2138,7 @@ XmssError xmss_finish_calculate_public_key( XmssPublicKeyInternal *const public_key_inner = (XmssPublicKeyInternal *)reallocated_blob->data; /* Compute the public key root node and fill caches as appropriate. */ + result = XMSS_UNINITIALIZED; result = xmss_finish_calculate_public_key_internal(key_context, *generation_buffer, partition_height); /* Check the result of the root node computation. */ @@ -2290,13 +2166,13 @@ XmssError xmss_finish_calculate_public_key( public_key_inner->redundant_version = convert_big_endian_word(XMSS_VERSION_CURRENT_PUBLIC_KEY_STORAGE); public_key_inner->redundant_scheme_identifier = convert_big_endian_word(param_set); /* The digest is already in big-endian form. */ - value_256_copy(&public_key_inner->digest_of_private_key_static_blob, &key_context->private_key_digest); + public_key_inner->digest_of_private_key_static_blob = key_context->private_key_digest; /* Set the public key root in big-endian form. */ - native_to_big_endian_256(&public_key_inner->public_key, &key_context->public_key_root); + native_to_big_endian_256(&public_key_inner->root, &key_context->public_key_root); public_key_inner->cache_type = convert_big_endian_word((uint32_t)cache_type); public_key_inner->cache_level = convert_big_endian_word((uint32_t)cache_level); if (cache_type != XMSS_CACHE_NONE && cache_level != XMSS_TREE_DEPTH(param_set)) { - native_to_big_endian(public_key_inner->cache[0].data, cache_to_build->cache->data, + native_to_big_endian((uint8_t *)public_key_inner->cache, cache_to_build->cache->data, XMSS_VALUE_256_WORDS * XMSS_CACHE_ENTRY_COUNT(cache_to_build->cache_type, cache_to_build->cache_level, key_context->context.parameter_set)); } @@ -2304,7 +2180,7 @@ XmssError xmss_finish_calculate_public_key( /* Compute the digest over the contents of the XmssPublicKeyInternal structure excluding the integrity digest * field itself. */ - xmss_digest(HASH_ABSTRACTION(&key_context->context.hash_functions) &public_key_inner->integrity, + xmss_digest(HASH_FUNCTIONS_FROM(key_context->context) &public_key_inner->integrity, ((uint8_t *)public_key_inner) + sizeof(public_key_inner->integrity), public_key_size - sizeof(public_key_inner->integrity)); @@ -2317,15 +2193,15 @@ XmssError xmss_finish_calculate_public_key( ) { key_context->context.free(reallocated_blob); *public_key = NULL; - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } *public_key = reallocated_blob; - return XMSS_OKAY; + return result; } -XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const restrict pub_key, - const XmssPrivateKeyStatelessBlob *const restrict private_key, const XmssKeyContext *const restrict key_context) +XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const pub_key, + const XmssPrivateKeyStatelessBlob *const private_key, const XmssKeyContext *const key_context) { /* To protect against bit errors in the CPU's flag register, we execute some if-statements in this function twice. * The variables being checked are volatile, so the compiler is not allowed to optimize away the redundant if. */ @@ -2340,13 +2216,10 @@ XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const restrict XmssParameterSetOID scheme_identifier = 0; XmssCacheType cache_type = XMSS_CACHE_NONE; uint32_t cache_level = 0; - volatile XmssError err_code = XMSS_OKAY; - volatile ValueCompareResult value_cmp; + volatile XmssError err_code = XMSS_UNINITIALIZED; + volatile ValueCompareResult value_cmp = VALUES_ARE_NOT_EQUAL; volatile bool private_key_checked = false; volatile bool key_context_checked = false; -#if XMSS_ENABLE_HASH_ABSTRACTION - xmss_hashes hash_functions; -#endif /* Check that the blob is large enough to contain all the fields of XmssPublicKeyInternal before the cache. * We need to know that we can access these fields before we can calculate the actual expected data_size. @@ -2361,19 +2234,15 @@ XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const restrict convert_big_endian_word(pub_key_internal->public_key_version) != XMSS_VERSION_CURRENT_PUBLIC_KEY_STORAGE, XMSS_ERR_INVALID_BLOB); if (convert_big_endian_word(pub_key_internal->redundant_version) != XMSS_VERSION_CURRENT_PUBLIC_KEY_STORAGE) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } /* Determine the hash function to use for the integrity digest. */ scheme_identifier = (XmssParameterSetOID)convert_big_endian_word(pub_key_internal->scheme_identifier); - /* xmss_get_hash_functions() is not bit error resilient, but an error here will be caught later due to an incorrect - * integrity digest or when comparing the scheme identifier with the context. */ - err_code = xmss_get_hash_functions(HASH_ABSTRACTION(&hash_functions) scheme_identifier); - if (err_code != XMSS_OKAY) { - /* The scheme identifier is invalid or unsupported by this library. */ - return XMSS_ERR_INVALID_BLOB; - } + DEFINE_HASH_FUNCTIONS; + err_code = INITIALIZE_HASH_FUNCTIONS(scheme_identifier); + REDUNDANT_RETURN_ERR(err_code); /* Extract and validate the required parameters to calculate the expected size. */ cache_type = (XmssCacheType)convert_big_endian_word(pub_key_internal->cache_type); @@ -2389,10 +2258,10 @@ XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const restrict return XMSS_ERR_INVALID_BLOB; } - value_cmp = check_integrity_digest(HASH_ABSTRACTION(&hash_functions) &pub_key_internal->integrity, + value_cmp = check_integrity_digest(HASH_FUNCTIONS &pub_key_internal->integrity, (uint8_t *)pub_key_internal + sizeof(pub_key_internal->integrity), pub_key_size - sizeof(pub_key_internal->integrity)); - REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_BIT_ERROR_DETECTED); + REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_FAULT_DETECTED); if (private_key != NULL) { XmssPrivateKeyStateless *private_key_internal = NULL; @@ -2412,7 +2281,8 @@ XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const restrict &private_key_internal->integrity); REDUNDANT_RETURN_IF(value_cmp != VALUES_ARE_EQUAL, XMSS_ERR_ARGUMENT_MISMATCH); - err_code = verify_private_key_stateless_internal(HASH_ABSTRACTION(&hash_functions) private_key, + err_code = XMSS_UNINITIALIZED; + err_code = verify_private_key_stateless_internal(HASH_FUNCTIONS private_key, scheme_identifier, convert_big_endian_word(pub_key_internal->redundant_scheme_identifier)); REDUNDANT_RETURN_ERR(err_code); @@ -2431,8 +2301,8 @@ XmssError xmss_verify_public_key(const XmssPublicKeyInternalBlob *const restrict /* Check that a bit error didn't cause the program to skip verification steps. */ if (private_key_checked != (private_key != NULL) || key_context_checked != (key_context != NULL)) { - return XMSS_ERR_BIT_ERROR_DETECTED; + return XMSS_ERR_FAULT_DETECTED; } - return XMSS_OKAY; + return err_code; } diff --git a/src/private.h b/src/signing_private.h similarity index 84% rename from src/private.h rename to src/signing_private.h index 4966056..816f950 100644 --- a/src/private.h +++ b/src/signing_private.h @@ -60,15 +60,6 @@ */ #define XMSS_VERSION_CURRENT_PUBLIC_KEY_STORAGE 1 -/** - * @brief - * The number of digests in a WOTS+ private or uncompressed public key. - * - * @details - * This holds for all supported parameter sets, see RFC 8391, Section 5.2 and NIST SP 800-208, Section 5.1 and 5.3. - */ -#define XMSS_WOTSP_LEN 67 - /** The state of a context. */ typedef enum XmssInitializationState { @@ -113,10 +104,17 @@ struct XmssSigningContext { /** @brief Explicit padding. */ uint32_t pad_; #if XMSS_ENABLE_HASH_ABSTRACTION - /** The hash functions used. */ - xmss_hashes hash_functions; + /** + * @brief + * The hash functions used. + * + * This is simply used as a cached value for xmss_get_hash_functions(parameter_set). + * Redundancy is not needed; if this pointer gets corrupted, then either the integrity checks will fail + * or (more likely) the software will simply crash. + */ + const xmss_hashes *hash_functions; #else - void (*pad_hash_abstraction[8])(); + void *pad_hash_functions; #endif /** * The realloc() function to use. @@ -136,7 +134,7 @@ struct XmssSigningContext { }; /** @private */ -STATIC_ASSERT(sizeof(XmssSigningContext) == XMSS_SIGNING_CONTEXT_SIZE, "XMSS_SIGNING_CONTEXT_SIZE mismatch."); +XMSS_STATIC_ASSERT(sizeof(XmssSigningContext) == XMSS_SIGNING_CONTEXT_SIZE, "XMSS_SIGNING_CONTEXT_SIZE mismatch."); struct XmssInternalCache { /** @@ -177,22 +175,23 @@ struct XmssInternalCache { }; /** @private */ -STATIC_ASSERT(sizeof(XmssInternalCache) == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_NONE, +XMSS_STATIC_ASSERT(sizeof(XmssInternalCache) == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_NONE, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256), XMSS_PARAM_SHA2_10_256), "XMSS_INTERNAL_CACHE_SIZE mismatch."); /** @private */ -STATIC_ASSERT(sizeof(XmssInternalCache) == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_NONE, +XMSS_STATIC_ASSERT(sizeof(XmssInternalCache) == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_NONE, 0, XMSS_PARAM_SHA2_10_256), "XMSS_INTERNAL_CACHE_SIZE mismatch."); /** @private */ -STATIC_ASSERT(sizeof(XmssInternalCache) == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_SINGLE_LEVEL, +XMSS_STATIC_ASSERT(sizeof(XmssInternalCache) == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_SINGLE_LEVEL, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256), XMSS_PARAM_SHA2_10_256), "XMSS_INTERNAL_CACHE_SIZE mismatch."); /** @private */ -STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 4 == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_SINGLE_LEVEL, +XMSS_STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 4 + == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_SINGLE_LEVEL, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 2, XMSS_PARAM_SHA2_10_256), "XMSS_INTERNAL_CACHE_SIZE mismatch."); /** @private */ -STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 3 == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_TOP, +XMSS_STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 3 == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_TOP, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 1, XMSS_PARAM_SHA2_10_256), "XMSS_INTERNAL_CACHE_SIZE mismatch."); /** @private */ -STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 7 == +XMSS_STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 7 == XMSS_INTERNAL_CACHE_SIZE(XMSS_CACHE_TOP, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 2, XMSS_PARAM_SHA2_10_256), "XMSS_INTERNAL_CACHE_SIZE mismatch."); @@ -202,14 +201,14 @@ STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 7 == * * @note The arguments to XMSS_CACHE_ENTRY_OFFSET will be evaluated multiple times. * - * @param cache_type The cache type that is used. - * @param cache_level The cache level that is used. - * @param param_set The parameter set of the key for which the cache is used. - * @param entry_level The level of the entry being looked up. For single level caching entry_level==cache_level, since - * only one level is cached. For top caching cache_level <= entry_level <= - * XMSS_TREE_DEPTH(param_set). - * @param entry_index The index of the entry within its level, counting from left to right in the tree (i.e. by - * increasing address). 0 <= entry_index < 2 ** (XMSS_TREE_DEPTH(param_set) - entry_level). + * @param[in] cache_type The cache type that is used. + * @param[in] cache_level The cache level that is used. + * @param[in] param_set The parameter set of the key for which the cache is used. + * @param[in] entry_level The level of the entry being looked up. For single level caching entry_level==cache_level, + * since only one level is cached. For top caching cache_level <= entry_level <= + * XMSS_TREE_DEPTH(param_set). + * @param[in] entry_index The index of the entry within its level, counting from left to right in the tree (i.e. by + * increasing address). 0 <= entry_index < 2 ** (XMSS_TREE_DEPTH(param_set) - entry_level). */ #define XMSS_CACHE_ENTRY_OFFSET(cache_type, cache_level, param_set, entry_level, entry_index) \ ((cache_type) == XMSS_CACHE_TOP ? \ @@ -227,14 +226,14 @@ STATIC_ASSERT(sizeof(XmssInternalCache) + sizeof(XmssValue256) * 7 == * @details * Partial structure. Do not use this without any additional protections! */ -typedef struct { +typedef struct XmssPrivateKeyStatelessContents { /* * Note that the scheme identifier is not included in this convenience structure. It is added manually to aid in * 64-bits padding of the containing structures. */ /** @brief The PRF_SEED for this private key. */ XmssNativeValue256 prf_seed; - /** @brief The seed for private key generation. */ + /** @brief The seed for private key generation. NIST terminology: SK_SEED */ XmssNativeValue256 private_key_seed; /** * @brief @@ -267,7 +266,7 @@ typedef struct { } XmssPrivateKeyStatelessContents; /** @private */ -STATIC_ASSERT(sizeof(XmssPrivateKeyStatelessContents) == XMSS_PRIVATE_KEY_STATELESS_PART_SIZE, +XMSS_STATIC_ASSERT(sizeof(XmssPrivateKeyStatelessContents) == XMSS_PRIVATE_KEY_STATELESS_PART_SIZE, "XMSS_PRIVATE_KEY_STATELESS_PART_SIZE mismatch"); /** @@ -285,7 +284,7 @@ typedef XmssPrivateKeyStatelessContents XmssPrivateKeyStatelessContentsBigEndian * @details * Partial structure. Do not use without any additional protections! */ -typedef struct { +typedef struct XmssPrivateKeyStatefulContents { /** @brief The lowest non-obfuscated index in this private key partition that may be used for signing. */ uint32_t partition_start; /** @@ -300,7 +299,7 @@ typedef struct { } XmssPrivateKeyStatefulContents; /** @private */ -STATIC_ASSERT(sizeof(XmssPrivateKeyStatefulContents) == XMSS_PRIVATE_KEY_STATEFUL_PART_SIZE, +XMSS_STATIC_ASSERT(sizeof(XmssPrivateKeyStatefulContents) == XMSS_PRIVATE_KEY_STATEFUL_PART_SIZE, "XMSS_PRIVATE_KEY_STATEFUL_PART_SIZE mismatch"); /** @@ -391,10 +390,10 @@ struct XmssKeyContext { }; /** @private */ -STATIC_ASSERT(sizeof(XmssKeyContext) == XMSS_KEY_CONTEXT_SIZE(XMSS_PARAM_SHA2_10_256, XMSS_INDEX_OBFUSCATION_OFF), +XMSS_STATIC_ASSERT(sizeof(XmssKeyContext) == XMSS_KEY_CONTEXT_SIZE(XMSS_PARAM_SHA2_10_256, XMSS_INDEX_OBFUSCATION_OFF), "XMSS_KEY_CONTEXT_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssKeyContext) + (1 << 10) * sizeof(uint32_t) == +XMSS_STATIC_ASSERT(sizeof(XmssKeyContext) + (1 << 10) * sizeof(uint32_t) == XMSS_KEY_CONTEXT_SIZE(XMSS_PARAM_SHA2_10_256, XMSS_INDEX_OBFUSCATION_ON), "XMSS_KEY_CONTEXT_SIZE mismatch"); @@ -439,15 +438,15 @@ struct XmssKeyGenerationContext { */ struct { /** @brief The XmssGenerationState of this calculation partition. */ - _Atomic uint32_t state; + ATOMIC uint32_t state; } partition_states[]; }; /** @private */ -STATIC_ASSERT(sizeof(XmssKeyGenerationContext) == XMSS_KEY_GENERATION_CONTEXT_SIZE(0), +XMSS_STATIC_ASSERT(sizeof(XmssKeyGenerationContext) == XMSS_KEY_GENERATION_CONTEXT_SIZE(0), "XMSS_KEY_GENERATION_CONTEXT_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssKeyGenerationContext) + (sizeof(uint32_t)) +XMSS_STATIC_ASSERT(sizeof(XmssKeyGenerationContext) + (sizeof(uint32_t)) == XMSS_KEY_GENERATION_CONTEXT_SIZE(1), "XMSS_KEY_GENERATION_CONTEXT_SIZE mismatch"); /** @@ -466,7 +465,7 @@ STATIC_ASSERT(sizeof(XmssKeyGenerationContext) + (sizeof(uint32_t)) * data_size member of XmssPrivateKeyStatelessBlob must be set to the exact size of XmssPrivateKeyStateless. If that is * the case, the data member of an XmssPrivateKeyStatelessBlob may freely be cast to an XmssPrivateKeyStateless. */ -typedef struct { +typedef struct XmssPrivateKeyStateless { /** * @brief * Integrity digest over the entire structure's contents, with the exception of this member. @@ -489,16 +488,16 @@ typedef struct { } XmssPrivateKeyStateless; /** @private */ -STATIC_ASSERT(sizeof(XmssPrivateKeyStatelessBlob) + sizeof(XmssPrivateKeyStateless) == +XMSS_STATIC_ASSERT(sizeof(XmssPrivateKeyStatelessBlob) + sizeof(XmssPrivateKeyStateless) == XMSS_PRIVATE_KEY_STATELESS_BLOB_SIZE, "XMSS_PRIVATE_KEY_STATELESS_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, integrity) == 0, +XMSS_STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, integrity) == 0, "XmssPrivateKeyStateless integrity digest must be the first field in the blob data"); /** @private */ -STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, private_key_stateless_version) == sizeof(XmssValue256), +XMSS_STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, private_key_stateless_version) == sizeof(XmssValue256), "XmssPrivateKeyStateless version must be the second field in the blob"); /** @private */ -STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, scheme_identifier) == +XMSS_STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, scheme_identifier) == offsetof(XmssPrivateKeyStateless, private_key_stateless_version) + sizeof(uint32_t), "XmssPrivateKeyStateless scheme identifier must be the third field in the blob"); @@ -518,7 +517,7 @@ STATIC_ASSERT(offsetof(XmssPrivateKeyStateless, scheme_identifier) == * data_size member of XmssPrivateKeyStatefulBlob must be set to the exact size of XmssPrivateKeyStateful. If that is * the case, the data member of an XmssPrivateKeyStatefulBlob may freely be cast to an XmssPrivateKeyStateful. */ -typedef struct { +typedef struct XmssPrivateKeyStateful { /** * @brief * Integrity digest over the entire structure's contents, except for this member. @@ -561,17 +560,17 @@ typedef struct { } XmssPrivateKeyStateful; /** @private */ -STATIC_ASSERT(sizeof(XmssPrivateKeyStatefulBlob) + sizeof(XmssPrivateKeyStateful) == +XMSS_STATIC_ASSERT(sizeof(XmssPrivateKeyStatefulBlob) + sizeof(XmssPrivateKeyStateful) == XMSS_PRIVATE_KEY_STATEFUL_BLOB_SIZE, "XMSS_PRIVATE_KEY_STATEFUL_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, integrity) == 0, +XMSS_STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, integrity) == 0, "XmssPrivateKeyStateful integrity must be the first field in the blob"); /** @private */ -STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, private_key_stateful_version) == +XMSS_STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, private_key_stateful_version) == offsetof(XmssPrivateKeyStateful, integrity) + sizeof(XmssValue256), "XmssPrivateKeyStateful private_key_stateful_version must be the second field in the blob"); /** @private */ -STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, scheme_identifier) == +XMSS_STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, scheme_identifier) == offsetof(XmssPrivateKeyStateful, private_key_stateful_version) + sizeof(uint32_t), "XmssPrivateKeyStateful scheme identifier must be the third field in the blob"); @@ -593,7 +592,7 @@ STATIC_ASSERT(offsetof(XmssPrivateKeyStateful, scheme_identifier) == * data_size member of XmssPublicKeyInternalBlob must be set to the exact size of XmssPublicKeyInternal. If that is the * case, the data member of an XmssPublicKeyInternalBlob may freely be cast to an XmssPublicKeyInternal. */ -typedef struct { +typedef struct XmssPublicKeyInternal { /** * @brief * Integrity digest over the entire structure's contents, with the exception of this member. @@ -628,7 +627,7 @@ typedef struct { */ XmssValue256 digest_of_private_key_static_blob; /** @brief The public key root. */ - XmssValue256 public_key; + XmssValue256 root; /** @brief The type of caching stored within this public key. This must be a valid value of type XmssCacheType. */ uint32_t cache_type; /** @@ -654,44 +653,46 @@ typedef struct { * * @note The arguments of XMSS_PUBLIC_KEY_INTERNAL_SIZE will be evaluated multiple times. * - * @param cache_type The cache type that is used. - * @param cache_level The cache level that is to be held. - * @param param_set The parameter set of the key for which the cache will be used. + * @param[in] cache_type The cache type that is used. + * @param[in] cache_level The cache level that is to be held. + * @param[in] param_set The parameter set of the key for which the cache will be used. * @see xmss_generate_public_key for more information about the cache type and level. */ #define XMSS_PUBLIC_KEY_INTERNAL_SIZE(cache_type, cache_level, param_set) \ - (sizeof(XmssPublicKeyInternal) + sizeof(XmssValue256) * XMSS_CACHE_ENTRY_COUNT(cache_type, cache_level, param_set)) + (sizeof(XmssPublicKeyInternal) + sizeof(XmssValue256) \ + * XMSS_CACHE_ENTRY_COUNT(cache_type, cache_level, param_set)) /** @private */ -STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) == +XMSS_STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) == XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(XMSS_CACHE_NONE, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 1, XMSS_PARAM_SHA2_10_256), "XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) == +XMSS_STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) == XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(XMSS_CACHE_SINGLE_LEVEL, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256), XMSS_PARAM_SHA2_10_256), "XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssPublicKeyInternal) + sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssValue256) * 3 == +XMSS_STATIC_ASSERT(sizeof(XmssPublicKeyInternal) + sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssValue256) * 3 == XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(XMSS_CACHE_TOP, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 1, XMSS_PARAM_SHA2_10_256), "XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) + (1 << 2) * sizeof(XmssValue256) == - XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(XMSS_CACHE_SINGLE_LEVEL, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 2, +XMSS_STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) + (1 << 2) * sizeof(XmssValue256) + == XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(XMSS_CACHE_SINGLE_LEVEL, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 2, XMSS_PARAM_SHA2_10_256), "XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) + ((1 << 4) - 1) * sizeof(XmssValue256) == +XMSS_STATIC_ASSERT(sizeof(XmssPublicKeyInternalBlob) + sizeof(XmssPublicKeyInternal) + ((1 << 4) - 1) + * sizeof(XmssValue256) == XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE(XMSS_CACHE_TOP, XMSS_TREE_DEPTH(XMSS_PARAM_SHA2_10_256) - 3, XMSS_PARAM_SHA2_10_256), "XMSS_PUBLIC_KEY_INTERNAL_BLOB_SIZE mismatch"); /** @private */ -STATIC_ASSERT(offsetof(XmssPublicKeyInternal, integrity) == 0, +XMSS_STATIC_ASSERT(offsetof(XmssPublicKeyInternal, integrity) == 0, "XmssPublicKeyInternal integrity digest must be the first field in the blob"); /** @private */ -STATIC_ASSERT(offsetof(XmssPublicKeyInternal, public_key_version) == sizeof(XmssValue256), +XMSS_STATIC_ASSERT(offsetof(XmssPublicKeyInternal, public_key_version) == sizeof(XmssValue256), "XmssPublicKeyInternal version must be the second field in the blob"); /** @private */ -STATIC_ASSERT(offsetof(XmssPublicKeyInternal, scheme_identifier) == +XMSS_STATIC_ASSERT(offsetof(XmssPublicKeyInternal, scheme_identifier) == offsetof(XmssPublicKeyInternal, public_key_version) + sizeof(uint32_t), "XmssPublicKeyInternal scheme identifier must be the third field in the blob"); diff --git a/src/utils.c b/src/utils.c index 9e0fc45..65f60b6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,8 +1,9 @@ /* - * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. * SPDX-License-Identifier: MIT * * SPDX-FileContributor: Max Fillinger + * SPDX-FileContributor: Frans van Dorsselaer */ /** @@ -11,52 +12,47 @@ * Utility and convenience functions. */ -#include -#include - -#include "config.h" - #include "utils.h" -/** - * @brief - * Volatile pointer to the standard C library memset() function. - */ -static void * (* volatile const memset_p)(void *, int, size_t) = memset; - -#if XMSS_CAN_USE_PRAGMA_OPTIMIZE -#pragma optimize("", off) -#endif - -#if XMSS_CAN_USE_PRAGMA_GCC_OPTIMIZE -#pragma GCC optimize("O0") -#endif - -#if XMSS_CAN_USE_PRAGMA_CLANG_OPTIMIZE -#pragma clang optimize off -#endif -void xmss_zeroize(void * const ptr, const size_t size) +ValueCompareResult compare_32_bytes(const uint8_t *const array1, const uint8_t *const array2) { - /* - * We need to ensure that the memset call in this function cannot be optimized away. For GCC and Clang, we use - * pragmas to disable optimization for this function, because these compilers are our primary target and this - * approach can not be defeated by future improvements to their optimization techniques. - * - * To have a solution that works on almost all compilers, we access memset through a volatile pointer memset_p. - * Because it is volatile, the compiler is not allowed to assume that it still points to memset. - * - * It is still technically allowed to optimize the function call below to - * - * if (memset_p != memset) { - * memset_p(ptr, 0, size); - * } - * - * so the standard does not guarantee that this works. - * - * See also: - * https://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html - * https://www.daemonology.net/blog/2014-09-05-erratum.html - * https://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html - */ - memset_p(ptr, 0, size); + /* Double volatile: we want the original pointer to be used for every indirection, and prevent caching of the + * pointed-to values. */ + volatile const uint8_t *volatile const p1 = array1; + volatile const uint8_t *volatile const p2 = array2; + volatile uint_fast8_t difference = 0; + + if ((p1 == NULL) || (p2 == NULL) || (p1 == p2)) + { + /* If the addresses differ by a power of two, then a single bit error in the pointer value could result in them + * pointing to the same memory (which in turn would lead to a false positive). + * Such a bit error is handled by this function as if the memory does not compare equal. + */ + return VALUES_ARE_NOT_EQUAL; + } + + for (size_t i = 0; i < 32; i++) { + difference |= (uint_fast8_t)(p1[i] ^ p2[i]); + } + if (difference) { + return VALUES_ARE_NOT_EQUAL; + } + + /* Repeat the check so that a single bit error cannot cause us to wrongly output VALUES_ARE_EQUAL. + * Because every loop iteration reads volatile data and updates a volatile variable, the compiler is not allowed + * to optimize away this second loop. */ + for (size_t i = 0; i < 32; i++) { + difference |= (uint_fast8_t)(p1[i] ^ p2[i]); + } + if (difference) { + return VALUES_ARE_NOT_EQUAL; + } + + /* Repeat the check to ensure p1, p2, and size did not alter since the first time we checked. */ + if ((p1 == NULL) || (p2 == NULL) || (p1 == p2)) + { + return VALUES_ARE_NOT_EQUAL; + } + + return VALUES_ARE_EQUAL; } diff --git a/src/utils.h b/src/utils.h index 2ac115d..665895d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -3,6 +3,7 @@ * SPDX-License-Identifier: MIT * * SPDX-FileContributor: Max Fillinger + * SPDX-FileContributor: Frans van Dorsselaer */ /** @@ -17,9 +18,12 @@ /** @private @brief Include guard. */ #define XMSS_UTILS_H_INCLUDED +#include #include #include +#include "compat.h" +#include "libxmss.h" #include "types.h" /** @brief Convert a size in bytes to a size in bits. */ @@ -29,37 +33,74 @@ #define TO_WORDS(size) ((size) / sizeof(uint32_t)) /** - * @copydoc XmssZeroizeFunction - * @see XmssZeroizeFunction + * @brief + * Return values for bit-error resilient comparison functions. * * @details - * In pure C99, there is no way to implement a zeroize function that cannot be optimized away. This implementation is a - * best-effort solution that is known to work on almost all compilers. + * The values are chosen to be both non-zero and with large hamming distance to be resilient to bit errors. */ -void xmss_zeroize(void * const ptr, const size_t size); +typedef enum ValueCompareResult { + /** @brief Values are equal. */ + VALUES_ARE_EQUAL = XMSS_DISTANT_VALUE_1, + /** @brief Values are not equal. */ + VALUES_ARE_NOT_EQUAL = XMSS_DISTANT_VALUE_2 +} ValueCompareResult; /** * @brief - * Copies a 256-bit value. + * Compare two distinct arrays of 32 bytes each in a bit-error resilient way. The comparison is performed in + * constant-time in the sense that the execution time does not depend on the actual values; however, the execution time + * may be affected by bit errors. Note that when the pointers `value1` and `value2` point to the same array, this + * function returns #VALUES_ARE_NOT_EQUAL. + * + * @details + * compare_32_bytes() is intended to compare cryptographically secure digests; the byte ordering of the digests does not + * matter, but they should match for both arguments. Use of the more strongly typed compare_values_256() or + * compare_native_values_256() is preferred. + * + * The #VALUES_ARE_EQUAL result indicates that two independently calculated digests are identical. The function requires + * distinct pointers for `value1` and `value2`; i.e., a value cannot be compared to itself. The default return value + * in case of contract violations (as the result of either bit errors or fault injections) is #VALUES_ARE_NOT_EQUAL. * - * @param[out] dst The destination value. - * @param[in] src The source value. + * This function is written in such a way that a single random bit error cannot cause the function to return + * #VALUES_ARE_EQUAL when the values are not equal. If the return value of this function is checked, it should be stored + * in a volatile variable which is then checked twice for strict (in)equality against #VALUES_ARE_EQUAL (not against + * #VALUES_ARE_NOT_EQUAL), to ensure that a bit error cannot skip the check. + * + * Although this function helps fault injection mitigation, the caller should also add redundancy to the calculation of + * the digests themselves and, as a consequence, call this function multiple times. + * + * @param[in] value1 The first value; must not be a NULL pointer. + * @param[in] value2 The (distinct) second value; must not be a NULL pointer. + * @retval #VALUES_ARE_EQUAL The values are equal. + * @retval #VALUES_ARE_NOT_EQUAL The values are not equal, a bit error was detected, or a NULL pointer was passed. */ -static inline void value_256_copy(XmssValue256 *const restrict dst, const XmssValue256 *const restrict src) +LIBXMSS_STATIC +ValueCompareResult compare_32_bytes(const uint8_t *value1, const uint8_t *value2); + +/** + * @copydoc compare_32_bytes + * @see compare_32_bytes + * @details + * This is the specialization for XmssValue256. + */ +static inline ValueCompareResult compare_values_256(const XmssValue256 *const value1, const XmssValue256 *const value2) { - memcpy(dst, src, sizeof(XmssValue256)); + XMSS_STATIC_ASSERT(sizeof(XmssValue256) == 32, "XmssValue256 expected to be 32 bytes"); + return compare_32_bytes((const uint8_t *)value1, (const uint8_t *)value2); } /** - * @brief - * Copies a 256-bit native value. - * - * @param[out] dst The destination value. - * @param[in] src The source value. + * @copydoc compare_32_bytes + * @see compare_32_bytes + * @details + * This is the specialization for XmssNativeValue256. */ -static inline void native_256_copy(XmssNativeValue256 *const restrict dst, const XmssNativeValue256 *const restrict src) +static inline ValueCompareResult compare_native_values_256(const XmssNativeValue256 *const value1, + const XmssNativeValue256 *const value2) { - memcpy(dst, src, sizeof(XmssNativeValue256)); + XMSS_STATIC_ASSERT(sizeof(XmssNativeValue256) == 32, "XmssNativeValue256 expected to be 32 bytes"); + return compare_32_bytes((const uint8_t *)value1, (const uint8_t *)value2); } #endif /* !XMSS_UTILS_H_INCLUDED */ diff --git a/src/verification.c b/src/verification.c index 3d8707f..509822d 100644 --- a/src/verification.c +++ b/src/verification.c @@ -3,6 +3,7 @@ * SPDX-License-Identifier: MIT * * SPDX-FileContributor: Max Fillinger + * SPDX-FileContributor: Frans van Dorsselaer */ /** @@ -16,45 +17,206 @@ #include #include "config.h" +#include "verification.h" #include "endianness.h" +#include "fault_detection_helpers.h" +#include "opaque_structures.h" +#include "rand_hash.h" #include "structures.h" #include "utils.h" -#include "verification.h" -#include "wotsp.h" +#include "wotsp_verification.h" #include "xmss_hashes.h" -#include "xmss_tree.h" +#include "xmss_ltree.h" + /** * @brief - * Calculate the digest of msg using H_msg. - * - * @warning - * The caller is responsible for supplying valid pointers. This will not be checked. + * The states of VerificationContextInternal. * - * @param[in] hash_functions Hash functions to use. - * @param[out] digest Location to store the digest. - * @param[in] msg Message to hash. - * @param[in] leaf_index Index of the signature that is being checked. - * @param[in] random Per-message salt, supplied with the signature, in big-endian. - * @param[in] public_key The public key, in big-endian. + * @remarks + * Any values not explicitly defined shall be interpreted as #VERIFICATION_CTX_UNINITIALIZED. */ -static inline void calculate_message_digest(HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - XmssNativeValue256 *const restrict digest, const XmssBuffer *const restrict msg, const uint32_t leaf_index, - const XmssValue256 *const restrict random, const XmssValue256 *const restrict public_key) +typedef enum VerificationContextState +{ + /** Explicitly uninitialized; set when xmss_verification_init fails. */ + VERIFICATION_CTX_UNINITIALIZED = XMSS_DISTANT_VALUE_1, + + /** + * Initialized; ready to accept any number of calls to xmss_verification_update followed by any number of calls + * to xmss_verification_check. + */ + VERIFICATION_CTX_INITIALIZED = XMSS_DISTANT_VALUE_2, + + /** xmss_verification_check has been called at least once, and until now all verifications succeeded. */ + VERIFICATION_CTX_CALCULATED = XMSS_DISTANT_VALUE_3, + + /** Verification failed; final state. */ + VERIFICATION_CTX_INVALID_SIGNATURE = XMSS_DISTANT_VALUE_4, + + /** Fault detected; final state. */ + VERIFICATION_CTX_FAULT_DETECTED = XMSS_DISTANT_VALUE_5, + +} VerificationContextState; + + +/** @brief Internal representation of VerificationContext. */ +typedef struct VerificationContextInternal { + + /* 64-bit alignment boundary */ + + /** @brief The state of the context, in a fixed size union. */ + union { + VerificationContextState value; + uint32_t alignment; + } state; + + /** @brief The parameter set of the public key, in a fixed size union. */ + union { + XmssParameterSetOID value; + uint32_t alignment; + } parameter_set; + + /* 64-bit alignment boundary */ + + /** + * @brief + * A pointer to the public key, in a fixed size union. + * + * @remarks + * The pointer value itself is volatile to detect pointer manipulation during xmss_verification_check. + */ + union { + const XmssPublicKey *volatile pointer; + uint64_t alignment; + } public_key; + + /* 64-bit alignment boundary */ + + /** @brief A pointer to the signature, in a fixed size union. */ + union { + const XmssSignature *pointer; + uint64_t alignment; + } signature; + + /* 64-bit alignment boundary */ + + /** @brief The context for the message digest calculation. */ + XmssHMsgCtx h_msg_ctx; + + /* 64-bit alignment boundary */ + + /** @brief The calculated public key root; only to be interpreted when state == VERIFICATION_CTX_CALCULATED. */ + XmssValue256 calculated_root; + + /* 64-bit alignment boundary */ + +} VerificationContextInternal; + +/** @private */ +XMSS_STATIC_ASSERT(sizeof(VerificationContextInternal) == XMSS_VERIFICATION_CONTEXT_SIZE, + "XMSS_VERIFICATION_CONTEXT_SIZE does not match actual size of VerificationContextInternal."); + + +XmssError xmss_verification_init(XmssVerificationContext *const context, + const XmssPublicKey *const public_key, const XmssSignature *const signature, + const size_t signature_length) +{ + if (context == NULL) { + return XMSS_ERR_NULL_POINTER; + } + VerificationContextInternal *const ctx = (VerificationContextInternal *)context->data; + + /* Just in case we fail *and* the state is accidentally in one of the defined valid states *and* the caller fails + * to honor our error return *and* calls one of the other functions, we first set the state explicitly to + * uninitialized. + */ + ctx->state.value = VERIFICATION_CTX_UNINITIALIZED; + + if (public_key == NULL || signature == NULL) { + return XMSS_ERR_NULL_POINTER; + } + + /* We store the (native) XmssParameterSetOID explicitly, just in case the caller forgets about keeping the public + * key constant between calls. Changing the public key will simply fail signature verification, but changing the + * parameter set would mess up our hash context. + */ + ctx->parameter_set.value = (XmssParameterSetOID)convert_big_endian_word(public_key->scheme_identifier); + + /* This effectively validates the public key (in the sense that the parameter set is valid and supported). */ + DEFINE_HASH_FUNCTIONS; + if (INITIALIZE_HASH_FUNCTIONS(ctx->parameter_set.value) != XMSS_OKAY) + { + return XMSS_ERR_INVALID_ARGUMENT; + } + + /* Bail out early if the provided signature cannot possibly be correct. */ + if (signature_length != XMSS_SIGNATURE_SIZE(ctx->parameter_set.value)) { + return XMSS_ERR_INVALID_SIGNATURE; + } + uint32_t native_leaf_index = convert_big_endian_word(signature->leaf_index); + if (native_leaf_index >= 1u << XMSS_TREE_DEPTH(ctx->parameter_set.value)) { + return XMSS_ERR_INVALID_SIGNATURE; + } + + ctx->public_key.pointer = public_key; + ctx->signature.pointer = signature; + + Input_H_msg input = INIT_INPUT_H_MSG; + big_endian_to_native_256(&input.r, &signature->random_bytes); + big_endian_to_native_256(&input.Root, &public_key->root); + input.idx_sig = native_leaf_index; + + xmss_H_msg_init(HASH_FUNCTIONS &ctx->h_msg_ctx, &input); + + /* For good measure (in case someone reuses the context and skips the calculation the second use). */ + memset(&ctx->calculated_root, 0, sizeof(ctx->calculated_root)); + + ctx->state.value = VERIFICATION_CTX_INITIALIZED; + + return XMSS_OKAY; +} + + +XmssError xmss_verification_update(XmssVerificationContext *const context, const uint8_t *const part, + const size_t part_length, const uint8_t *volatile *const part_verify) { -#if XMSS_ENABLE_HASH_ABSTRACTION - assert(hash_functions != NULL); -#endif - assert(digest != NULL && msg != NULL && random != NULL && public_key != NULL); - - Input_H_msg input_h_msg = INIT_INPUT_H_MSG; - big_endian_to_native_256(&input_h_msg.r, random); - big_endian_to_native_256(&input_h_msg.Root, public_key); - input_h_msg.idx_sig = leaf_index; - xmss_H_msg(HASH_ABSTRACTION(hash_functions) digest, &input_h_msg, msg->data, msg->data_size); + if (part_verify != NULL) { + *part_verify = NULL; + } + + if (context == NULL || (part == NULL && part_length > 0)) { + return XMSS_ERR_NULL_POINTER; + } + VerificationContextInternal *const ctx = (VerificationContextInternal *)context->data; + + if (ctx->state.value != VERIFICATION_CTX_INITIALIZED) { + return XMSS_ERR_BAD_CONTEXT; + } + + if (part_length == 0) { + /* Nothing to do. This supports callers calling this function exactly once (with the entire message) and the + * message just happens to be empty (without the caller realizing that). + */ + if (part_verify != NULL) { + *part_verify = part; + } + return XMSS_OKAY; + } + + DEFINE_HASH_FUNCTIONS; + if (INITIALIZE_HASH_FUNCTIONS(ctx->parameter_set.value) != XMSS_OKAY) + { + /* The only reason this could fail at this point is a corrupt value of the parameter set. */ + return XMSS_ERR_FAULT_DETECTED; + } + + xmss_H_msg_update(HASH_FUNCTIONS &ctx->h_msg_ctx, part, part_length, part_verify); + + return XMSS_OKAY; } + /** * @brief * Calculate the expected WOTS+ public key and compress it to a leaf node of the XMSS hash tree. @@ -68,31 +230,36 @@ static inline void calculate_message_digest(HASH_ABSTRACTION(const xmss_hashes * * @param[in] seed Seed for the PRF. * @param[in] digest Message digest. * @param[in] signature XMSS signature containing the WOTS+ signature. + * + * @retval XMSS_OKAY Leaf node calculated successfully. + * @retval XMSS_ERR_FAULT_DETECTED A fault was detected. */ -static inline void calculate_leaf_node(HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - XmssNativeValue256 *const restrict leaf_node, const uint32_t leaf_index, const XmssNativeValue256 *const restrict seed, - const XmssNativeValue256 *const restrict digest, const XmssSignature *const restrict signature) +static inline XmssError calculate_leaf_node(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const leaf_node, + const uint32_t leaf_index, const XmssNativeValue256 *const seed, const XmssNativeValue256 *const digest, + const XmssSignature *const signature) { -#if XMSS_ENABLE_HASH_ABSTRACTION - assert(hash_functions != NULL); -#endif + ASSERT_HASH_FUNCTIONS(); assert(leaf_node != NULL && seed != NULL && digest != NULL && signature != NULL); - WotspSignature wotsp_signature; /* Uninitialized for performance reasons. */ - WotspPublicKey expected_wotsp_public_key; /* Uninitialized for performance reasons. */ + WotspSignature wotsp_signature = { 0 }; + WotspPublicKey expected_wotsp_public_key = { 0 }; ADRS adrs = { 0 }; + volatile XmssError result = XMSS_UNINITIALIZED; /* Convert the WOTS+ signature contained in signature to native-endian and calculate the expected WOTS+ * public key. */ - big_endian_to_native(wotsp_signature.hashes[0].data, signature->wots_signature[0].data, + big_endian_to_native((uint32_t *)&wotsp_signature, (const uint8_t *)signature->wots_signature, XMSS_VALUE_256_WORDS * XMSS_WOTSP_LEN); - wotsp_calculate_expected_public_key(HASH_ABSTRACTION(hash_functions) &expected_wotsp_public_key, digest, - &wotsp_signature, seed, leaf_index); + result = wotsp_calculate_expected_public_key(HASH_FUNCTIONS &expected_wotsp_public_key, digest, &wotsp_signature, + seed, leaf_index); + REDUNDANT_RETURN_ERR(result); /* Compress the WOTS+ public key to get the corresponding leaf node of the XMSS tree. */ adrs.type = ADRS_type_L_tree_Address; adrs.typed.L_tree_Address.L_tree_address = leaf_index; - xmss_ltree(HASH_ABSTRACTION(hash_functions) leaf_node, &expected_wotsp_public_key, &adrs, seed); + xmss_ltree(HASH_FUNCTIONS leaf_node, &expected_wotsp_public_key, &adrs, seed); + + return result; } /** @@ -110,72 +277,118 @@ static inline void calculate_leaf_node(HASH_ABSTRACTION(const xmss_hashes *const * @param[in] seed The seed for the PRF. * @param[in] authentication_path The authentication path, must contain tree_height hashes. */ -static inline void calculate_root_node(HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - XmssNativeValue256 *const restrict tree_node, const uint32_t leaf_index, const uint32_t tree_height, - const XmssNativeValue256 *const restrict seed, const XmssValue256 *const restrict authentication_path) +static inline void calculate_root_node(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const tree_node, + const uint32_t leaf_index, const uint32_t tree_height, const XmssNativeValue256 *const seed, + const XmssValue256 *const authentication_path) { -#if XMSS_ENABLE_HASH_ABSTRACTION - assert(hash_functions != NULL); -#endif + ASSERT_HASH_FUNCTIONS(); assert(tree_node != NULL && authentication_path != NULL); Input_PRF input_prf = INIT_INPUT_PRF; - native_256_copy(&input_prf.KEY, seed); + input_prf.KEY = *seed; input_prf.M.ADRS.type = ADRS_type_Hash_Tree_Address; input_prf.M.ADRS.typed.Hash_Tree_Address.tree_index = leaf_index; for (uint32_t height = 0; height < tree_height; height++) { - XmssNativeValue256 auth_path_node; /* Uninitialized for performance reasons. */ + XmssNativeValue256 auth_path_node = { 0 }; big_endian_to_native_256(&auth_path_node, &authentication_path[height]); input_prf.M.ADRS.typed.Hash_Tree_Address.tree_height = height; if ((input_prf.M.ADRS.typed.Hash_Tree_Address.tree_index & 1) == 0) { input_prf.M.ADRS.typed.Hash_Tree_Address.tree_index /= 2; - rand_hash(HASH_ABSTRACTION(hash_functions) tree_node, &input_prf, tree_node, &auth_path_node); + rand_hash(HASH_FUNCTIONS tree_node, &input_prf, tree_node, &auth_path_node); } else { input_prf.M.ADRS.typed.Hash_Tree_Address.tree_index /= 2; - rand_hash(HASH_ABSTRACTION(hash_functions) tree_node, &input_prf, &auth_path_node, tree_node); + rand_hash(HASH_FUNCTIONS tree_node, &input_prf, &auth_path_node, tree_node); } } } -XmssError xmss_calculate_expected_public_key( - XmssValue256 *const restrict expected_public_key, const XmssBuffer *const restrict msg, - const XmssPublicKeyBlob *const restrict pub_key, const XmssSignatureBlob *const restrict signature) +XmssError xmss_verification_check(XmssVerificationContext *const context, const XmssPublicKey *public_key) { - if (expected_public_key == NULL || msg == NULL || pub_key == NULL || signature == NULL) { + if (context == NULL) { return XMSS_ERR_NULL_POINTER; } - XmssParameterSetOID parameter_set = (XmssParameterSetOID)convert_big_endian_word(pub_key->data.scheme_identifier); - if (signature->data_size != XMSS_SIGNATURE_BLOB_SIZE(parameter_set) - sizeof(XmssSignatureBlob)) { - return XMSS_ERR_INVALID_BLOB; - } + VerificationContextInternal *const ctx = (VerificationContextInternal *)context->data; - const XmssSignature *signature_data = xmss_get_signature_struct(signature); - uint32_t leaf_index = convert_big_endian_word(signature_data->leaf_index); - XmssError err_code = XMSS_OKAY; - XmssNativeValue256 msg_digest; /* Uninitialized for performance reasons. */ - XmssNativeValue256 seed; /* Uninitialized for performance reasons. */ - XmssNativeValue256 tree_node; /* Uninitialized for performance reasons. */ -#if XMSS_ENABLE_HASH_ABSTRACTION - xmss_hashes hash_functions; -#endif - - err_code = xmss_get_hash_functions(HASH_ABSTRACTION(&hash_functions) parameter_set); - if (err_code != XMSS_OKAY) { - return err_code; - } + switch (ctx->state.value) { + case VERIFICATION_CTX_INITIALIZED: + /* This is the first call to this function after the last xmss_verification_update. Calculate the expected + * root first. */ - big_endian_to_native_256(&seed, &pub_key->data.seed); + if (public_key != ctx->public_key.pointer) + { + /* Pointer manipulation detected, cache the result and bail out. */ + ctx->state.value = VERIFICATION_CTX_FAULT_DETECTED; + return XMSS_ERR_FAULT_DETECTED; + } + { + uint32_t leaf_index = convert_big_endian_word(ctx->signature.pointer->leaf_index); + volatile XmssError result = XMSS_UNINITIALIZED; + XmssNativeValue256 digest = { 0 }; + XmssNativeValue256 native_seed = { 0 }; + XmssNativeValue256 tree_node = { 0 }; - calculate_message_digest(HASH_ABSTRACTION(&hash_functions) &msg_digest, msg, leaf_index, - &signature_data->random_bytes, &pub_key->data.public_key); - calculate_leaf_node(HASH_ABSTRACTION(&hash_functions) &tree_node, leaf_index, &seed, &msg_digest, signature_data); - calculate_root_node(HASH_ABSTRACTION(&hash_functions) &tree_node, leaf_index, XMSS_TREE_DEPTH(parameter_set), - &seed, signature_data->authentication_path); + big_endian_to_native_256(&native_seed, &ctx->public_key.pointer->seed); - native_to_big_endian_256(expected_public_key, &tree_node); + DEFINE_HASH_FUNCTIONS; + if (INITIALIZE_HASH_FUNCTIONS(ctx->parameter_set.value) != XMSS_OKAY) + { + /* The only reason this could fail at this point is a corrupt value of the parameter set. */ + ctx->state.value = VERIFICATION_CTX_FAULT_DETECTED; + return XMSS_ERR_FAULT_DETECTED; + } - return XMSS_OKAY; + xmss_H_msg_finalize(HASH_FUNCTIONS &digest, &ctx->h_msg_ctx); + + result = calculate_leaf_node(HASH_FUNCTIONS &tree_node, leaf_index, &native_seed, &digest, + ctx->signature.pointer); + REDUNDANT_RETURN_ERR(result); + + calculate_root_node(HASH_FUNCTIONS &tree_node, leaf_index, XMSS_TREE_DEPTH(ctx->parameter_set.value), + &native_seed, ctx->signature.pointer->authentication_path); + + native_to_big_endian_256(&ctx->calculated_root, &tree_node); + } + + ctx->state.value = VERIFICATION_CTX_CALCULATED; + /* fall through */ + + case VERIFICATION_CTX_CALCULATED: + if (public_key != ctx->public_key.pointer) + { + /* Pointer manipulation detected, cache the result and bail out. */ + ctx->state.value = VERIFICATION_CTX_FAULT_DETECTED; + return XMSS_ERR_FAULT_DETECTED; + } + { + volatile ValueCompareResult value_cmp = VALUES_ARE_NOT_EQUAL; + value_cmp = compare_values_256(&ctx->public_key.pointer->root, &ctx->calculated_root); + /* For simple bit-error resilience, we check the return value twice. */ + if (value_cmp == VALUES_ARE_EQUAL) { + if (value_cmp == VALUES_ARE_EQUAL) { + /* Signature appears valid. We do not cache the result, so this can be checked again for fault + * tolerance. */ + return XMSS_OKAY; + } + } + } + + /* Signature failed verification. Cache the result. */ + ctx->state.value = VERIFICATION_CTX_INVALID_SIGNATURE; + /* fall through */ + + case VERIFICATION_CTX_INVALID_SIGNATURE: + /* Verification failed already, just return that result again. */ + return XMSS_ERR_INVALID_SIGNATURE; + + case VERIFICATION_CTX_FAULT_DETECTED: + /* A fault was detected before, just return that result again. */ + return XMSS_ERR_FAULT_DETECTED; + + case VERIFICATION_CTX_UNINITIALIZED: + default: + return XMSS_ERR_BAD_CONTEXT; + } } diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..f117d8e --- /dev/null +++ b/src/version.c @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Public API for XMSS library version control. + */ + +#include "version.h" + +uint32_t xmss_library_get_version(void) +{ + return XMSS_LIBRARY_VERSION; +} diff --git a/src/version.in.rc b/src/version.in.rc new file mode 100644 index 0000000..3e8d03f --- /dev/null +++ b/src/version.in.rc @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * Version info resource for a Windows shared library (DLL). + */ + +#ifdef APSTUDIO_INVOKED +#error This file is not editable with the visual resource editor. +#endif + +#include "config.h" + +#include + + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + + +/* + * Currently, there are no official releases of binary builds. This may change in the future. + */ +#define BUILD_NUMBER 0 + + +#define STR(value) #value +#define STRINGIZE(value) STR(value) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}, BUILD_NUMBER + PRODUCTVERSION ${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}, 0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD +#elif !BUILD_NUMBER + FILEFLAGS VS_FF_PRIVATEBUILD +#else + FILEFLAGS 0 +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Fox Crypto B.V." +#if XMSS_ENABLE_SIGNING + VALUE "FileDescription", "XMSS Library" +#else + VALUE "FileDescription", "XMSS Library (signature verification only)" +#endif + VALUE "FileVersion", + "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}." STRINGIZE(BUILD_NUMBER) + VALUE "InternalName", "xmss" + VALUE "LegalCopyright", "Copyright (C) 2024 Fox Crypto B.V." + VALUE "OriginalFilename", "xmss.dll" + VALUE "ProductName", "XMSS Library" + VALUE "ProductVersion", "${PROJECT_VERSION}" +#ifdef _DEBUG + VALUE "PrivateBuild", "Debug build. Not for production." +#elif !BUILD_NUMBER + VALUE "PrivateBuild", "Non-official build." +#endif + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/wotsp.c b/src/wotsp.c index 29d35fc..000426d 100644 --- a/src/wotsp.c +++ b/src/wotsp.c @@ -8,83 +8,29 @@ /** * @file * @brief - * WOTS+ signatures and verification. + * WOTS+ common functionality. */ #include #include #include -#include "config.h" #include "wotsp.h" -#include "private.h" #include "utils.h" +#include "wotsp_private.h" #include "xmss_hashes.h" -/** - * @brief - * Winternitz parameter for WOTS+, set to 16 as specified in RFC-8391, Section 5.3. - * - * @details - * WOTS+ processes the input to be signed in base-W digits. See RFC-8391, Section 3.1.1. - */ -#define W 16 -/** - * @brief - * The length of the base-W representation of the message digest to sign or verify. - * - * @details - * Since we sign 256-bit message digests, there are 64 digits. See RFC-8391, Section 3.1.1. - */ -#define LEN_1 64 -/** - * @brief - * The length of the base-W representation of the checksum that is calculated during signing and verification. - * - * @details - * The maximum checksum value is LEN_1 * (W - 1) = 960, which needs 3 base-16 digits to represent. - * See RFC-8391, Section 3.1.1. - */ -#define LEN_2 3 -/** - * @brief - * The number of hashes that make up a WOTS+ private key, public key or signature. See RFC-8391, Section 3.1.1. - */ -#define LEN (LEN_1 + LEN_2) -/** @private */ -STATIC_ASSERT(LEN == XMSS_WOTSP_LEN, "Mismatch in WOTS+ output length."); - -/** - * @brief - * Chaining function for WOTS+ signatures and verification. - * - * @details - * Based on RFC-8391, Section 3.1.2. (Algorithm 2) with the following changes: - * - for-loop instead of recursive calls - * - instead of SEED and ADRS, pass an Input_PRF struct pre-filled with these values, because most of it does not - * change between chain calls within one public key generation, signing or verification process. - * - * @param[in] hashes The struct with the hash functions to be used. - * @param[out] output Output. - * @param[in,out] input_prf Input_PRF struct filled with the PRF seed and ADRS. - * At the end of this function, the values of the hash_address and keyAndMask fields in - * ADRS are unspecified. The Input_PRF can still be used for further calls to chain(), - * since it initializes those fields to the correct values. - * @param[in] input Input. (Corresponds to X in Algorithm 2.) - * @param[in] start_index Starting index for the chain. (Corresponds to i in Algorithm 2.) - * @param[in] num_steps Number of chain steps to perform. (Corresponds to s in Algorithm 2.) - */ -static void chain(HASH_ABSTRACTION(const xmss_hashes *const restrict hashes) XmssNativeValue256 *const restrict output, - Input_PRF *restrict input_prf, const XmssNativeValue256 *restrict input, const uint32_t start_index, - const uint_fast8_t num_steps) +uint_fast8_t chain(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const output, Input_PRF *input_prf, + const XmssNativeValue256 *const input, const uint32_t start_index, const uint_fast8_t num_steps) { /* * Prepare the Input_F struct. This does not correspond to anything in RFC-8391, Algorithm 2. It is a struct to * place the inputs for the hash function F. */ Input_F input_f = INIT_INPUT_F; + volatile uint_fast8_t chain_steps_done = 0; input_prf->M.ADRS.typed.OTS_Hash_Address.hash_address = start_index; assert(start_index + num_steps < W); @@ -93,260 +39,28 @@ static void chain(HASH_ABSTRACTION(const xmss_hashes *const restrict hashes) Xms * We use output to store the intermediate values for the chain computation. * The final "intermediate" value will be the actual output. */ - native_256_copy(output, input); + *output = *input; - for (uint_fast8_t i = 0; i < num_steps; i++) { + for (chain_steps_done = 0; chain_steps_done < num_steps; chain_steps_done++) { /* Get the key for the next F call and place it in input_f. */ input_prf->M.ADRS.typed.OTS_Hash_Address.keyAndMask = 0; - xmss_PRF(HASH_ABSTRACTION(hashes) &input_f.KEY, input_prf); + xmss_PRF(HASH_FUNCTIONS &input_f.KEY, input_prf); /* * The next input message for F is the output of the previous call to F XORed with a bitmask from PRF. * We provide the message part of input_f as an output buffer to PRF to place the bitmask there, and then XOR * the output from the previous call to F (which is in output) into it. */ input_prf->M.ADRS.typed.OTS_Hash_Address.keyAndMask = 1; - xmss_PRF(HASH_ABSTRACTION(hashes) &input_f.M, input_prf); + xmss_PRF(HASH_FUNCTIONS &input_f.M, input_prf); for (uint_fast8_t j = 0; j < XMSS_VALUE_256_WORDS; j++) { input_f.M.data[j] ^= output->data[j]; } /* Calculate the next value in the chain and put it in native_digest. */ - xmss_F(HASH_ABSTRACTION(hashes) output, &input_f); + xmss_F(HASH_FUNCTIONS output, &input_f); input_prf->M.ADRS.typed.OTS_Hash_Address.hash_address += 1; } -} - -/** - * @brief - * Fill input_prf with the values that don't change between calls to chain. - * - * @param[out] input_prf The input_prf struct to fill. It is assumed to be initialized with INIT_INPUT_PRF. - * @param[in] seed Public seed for the PRF. - * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. - */ -inline static void prepare_input_prf_for_chain(Input_PRF *const restrict input_prf, - const XmssNativeValue256 *const restrict seed, const uint32_t ots_address) -{ - native_256_copy(&input_prf->KEY, seed); - input_prf->M.ADRS.type = ADRS_type_OTS_Hash_Address; - input_prf->M.ADRS.typed.OTS_Hash_Address.OTS_address = ots_address; -} - -/** - * @brief - * Prepare input_prfkeygen for generating the WOTS+ private key. - * - * @param[out] input_prfkeygen The input_prf struct to fill. It is assumed to be initialized with INIT_INPUT_PRF. - * @param[in] secret_seed Secret seed for PRFkeygen. - * @param[in] public_seed Public seed for PRFkeygen. - * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. - */ -inline static void prepare_input_prfkeygen(Input_PRFkeygen *const restrict input_prfkeygen, - const XmssNativeValue256 *const restrict secret_seed, - const XmssNativeValue256 *const restrict public_seed, - const uint32_t ots_address) -{ - native_256_copy(&input_prfkeygen->S_XMSS, secret_seed); - native_256_copy(&input_prfkeygen->SEED, public_seed); - input_prfkeygen->ADRS.type = ADRS_type_OTS_Hash_Address; - input_prfkeygen->ADRS.typed.OTS_Hash_Address.OTS_address = ots_address; -} - -/** - * @brief - * Get the ith 256-bit block of the secret key, in native byte order. - * - * @details - * Conceptually, this corresponds to accessing sk[i] in the WOTS+ public key generation and signing algorithms in - * RFC-8391 (Algorithms 4 and 5). However, we do not keep the entire secret key in memory and instead calculate the - * required part with PRFkeygen. See NIST SP 800-208, Algorithm 10' for the inputs to PRFkeygen. - * - * @param[in] hashes The struct with the hash functions to be used. - * @param[out] sk_i Place to store the secret key block. - * @param[in,out] input_prfkeygen Input_PRFkeygen containing the seeds and ADRS for generating the secret key. - * This function sets the chain_address in ADRS to i. - * @param[in] i Index of the secret key block to generate. - */ -inline static void get_sk_i(HASH_ABSTRACTION(const xmss_hashes *const restrict hashes) - XmssNativeValue256 *const restrict sk_i, - Input_PRFkeygen *const restrict input_prfkeygen, - const uint32_t i) -{ - input_prfkeygen->ADRS.typed.OTS_Hash_Address.chain_address = i; - xmss_PRFkeygen(HASH_ABSTRACTION(hashes) sk_i, input_prfkeygen); -} -void wotsp_gen_public_key(const struct XmssSigningContext *const restrict context, - WotspPublicKey *const restrict public_key, - const XmssNativeValue256 *const restrict secret_seed, - const XmssNativeValue256 *const restrict public_seed, - const uint32_t ots_address) -{ - /* - * Unlike in RFC-8391, Algorithm 4, we don't have the entire secret key sk ahead of time. Instead, we generate the - * blocks sk[i] of the secret key as they are needed. Here, we reserve the space to store one 256-bit block of it. - * We leave it uninitialized for performance reasons. - */ - XmssNativeValue256 sk_i; - /* - * Prepare the Input_PRF and Input_PRFkeygen structs. This part does not correspond to anything in RFC-8391, - * Algorithm 4. Input_PRFkeygen is needed to generate the secret key blocks. Input_PRF is a struct that holds SEED - * and ADRS for chain(). - */ - Input_PRFkeygen input_prfkeygen = INIT_INPUT_PRFKEYGEN; - Input_PRF input_prf = INIT_INPUT_PRF; - prepare_input_prfkeygen(&input_prfkeygen, secret_seed, public_seed, ots_address); - prepare_input_prf_for_chain(&input_prf, public_seed, ots_address); - - for (uint32_t i = 0; i < LEN; i++) { - /* Get the next block sk[i] of the secret key. */ - get_sk_i(HASH_ABSTRACTION(&context->hash_functions) &sk_i, &input_prfkeygen, i); - - /* Run W - 1 = 15 steps of the chain function on sk[i]. The result is pk[i] in RFC-8391, Algorithm 4. */ - input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; - chain(HASH_ABSTRACTION(&context->hash_functions) &public_key->hashes[i], &input_prf, &sk_i, 0, W - 1); - } - - context->zeroize(&input_prfkeygen.S_XMSS, sizeof(input_prfkeygen.S_XMSS)); - context->zeroize(&sk_i, sizeof(sk_i)); -} - -/** - * @brief - * Returns the ith base-W digit in the message-digest, in big-endian byte order. - * - * @details - * Conceptually, this corresponds to accessing msg[i] for i < LEN_1 in the WOTS+ algorithms in RFC-8391, where msg is - * the base-W representation of the message being signed. Since we use W = 16, each base-W digit is four bits, which - * makes it easy to calculate the digits on the fly without computing the entire base-W representation ahead of time. - * - * @param[in] message_digest The message digest. - * @param[in] i Index of the four-bit chunk. - * - * @returns The ith base-W digit. - */ -inline static uint_fast8_t get_msg_i(const XmssNativeValue256 *const message_digest, const uint32_t i) -{ - assert(i < LEN_1); - uint_fast8_t shift = (7 - (i % 8)) * 4; - return (uint_fast8_t)(message_digest->data[i / 8] >> shift) & 0x0f; -} - -/** - * @brief - * Amount to shift csum in RFC-8391, Algorithms 5 and 6, to get the base-W digits msg[i] for LEN_1 <= i < LEN. - */ -#define CSUM_BITSHIFT(i) (4 * (LEN_2 - 1 - (i - LEN_1))) - -void wotsp_sign(const struct XmssSigningContext *const restrict context, - WotspSignature *const restrict signature, - const XmssNativeValue256 *const restrict message_digest, - const XmssNativeValue256 *const restrict secret_seed, - const XmssNativeValue256 *const restrict public_seed, - const uint32_t ots_address) -{ - /* - * Unlike in RFC-8391, Algorithm 5, we don't calculate the entire secret key sk and base-W representation of the - * input msg ahead of time. Instead, we generate these values when they are needed and reserve space for them here. - * We leave sk_i uninitialized for performance reasons. - */ - XmssNativeValue256 sk_i; - uint_fast8_t msg_i = 0; - /* - * In RFC-8391, Algorithm 5, the checksum csum starts at 0 and for every base-W digit msg[i] of the input, - * W - 1 - msg[i] is added. It is equivalent to start with LEN_1 * (W - 1) and subtract each msg[i]. - */ - uint_fast16_t csum = LEN_1 * (W - 1); - /* - * Prepare the Input_PRF and Input_PRFkeygen structs. This part does not correspond to anything in RFC-8391, - * Algorithm 5. Input_PRFkeygen is needed to generate the secret key blocks. Input_PRF is a struct that holds SEED - * and ADRS for chain(). - */ - Input_PRFkeygen input_prfkeygen = INIT_INPUT_PRFKEYGEN; - Input_PRF input_prf = INIT_INPUT_PRF; - prepare_input_prfkeygen(&input_prfkeygen, secret_seed, public_seed, ots_address); - prepare_input_prf_for_chain(&input_prf, public_seed, ots_address); - - /* - * Process the message digest. This combines the loop for calculating the checksum and the first LEN_1 iterations - * of the final loop in RFC-8391, Algorithm 5. - */ - for (uint32_t i = 0; i < LEN_1; i++) { - msg_i = get_msg_i(message_digest, i); - get_sk_i(HASH_ABSTRACTION(&context->hash_functions) &sk_i, &input_prfkeygen, i); - csum -= msg_i; - - /* Run msg[i] steps of the chain function on sk[i] as input. The result is sig[i] in RFC-8391, Algorithm 5. */ - input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; - chain(HASH_ABSTRACTION(&context->hash_functions) &signature->hashes[i], &input_prf, &sk_i, 0, msg_i); - } - - /* - * Process the checksum. This corresponds to the last LEN_2 iterations of the final loop in RFC-8391, Algorithm 5. - * Note that the bit shift of csum in Algorithm 5 is used only to line it up with the toByte function. The result is - * the base-W digits of csum in big-endian order, which we calculate in a different way, using the fact that W = 16. - */ - assert(csum <= 960); - for (uint32_t i = LEN_1; i < LEN; i++) { - msg_i = (uint_fast8_t)(csum >> CSUM_BITSHIFT(i)) & 0x0f; - get_sk_i(HASH_ABSTRACTION(&context->hash_functions) &sk_i, &input_prfkeygen, i); - - input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; - chain(HASH_ABSTRACTION(&context->hash_functions) &signature->hashes[i], &input_prf, &sk_i, 0, msg_i); - } - - /* Zeroize the secret seed stored in input_prfkeygen, and the secret key block that is still in memory. */ - context->zeroize(&input_prfkeygen.S_XMSS, sizeof(input_prfkeygen.S_XMSS)); - context->zeroize(&sk_i, sizeof(sk_i)); -} - -void wotsp_calculate_expected_public_key(HASH_ABSTRACTION(const xmss_hashes *const restrict hashes) - WotspPublicKey *const restrict expected_public_key, - const XmssNativeValue256 *const restrict message_digest, - const WotspSignature *const restrict signature, - const XmssNativeValue256 *const restrict public_seed, - const uint32_t ots_address) -{ - /* - * Unlike in RFC-8391, Algorithm 6, we don't calculate the entire base-W representation of the message digest ahead - * of time. Instead, we generate these values when they are needed and reserve space for them here. - */ - uint_fast8_t msg_i = 0; - /* - * In RFC-8391, Algorithm 6, the checksum csum starts at 0 and for every base-W digit msg[i] of the message digest, - * W - 1 - msg[i] is added. It is equivalent to start with LEN_1 * (W - 1) and subtract each msg[i]. - */ - uint_fast16_t csum = LEN_1 * (W - 1); - /* - * Prepare the Input_PRF struct. This part does not correspond to anything in RFC-8391, Algorithm 6. Input_PRF is a - * struct that holds SEED and ADRS for chain(). - */ - Input_PRF input_prf = INIT_INPUT_PRF; - prepare_input_prf_for_chain(&input_prf, public_seed, ots_address); - - /* - * Process the message digest. This combines the loop for calculating the checksum and the first LEN_1 iterations - * of the final loop in RFC-8391, Algorithm 6. - */ - for (uint32_t i = 0; i < LEN_1; i++) { - msg_i = get_msg_i(message_digest, i); - csum -= msg_i; - input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; - chain(HASH_ABSTRACTION(hashes) &expected_public_key->hashes[i], &input_prf, &signature->hashes[i], - (uint32_t)msg_i, W - 1 - msg_i); - } - - /* - * Process the checksum. This corresponds to the last LEN_2 iterations of the final loop in RFC-8391, Algorithm 6. - * Note that the bit shift of csum in Algorithm 6 is used only to line it up with the toByte function. The result is - * the base-W digits of csum in big-endian order, which we calculate in a different way, using the fact that W = 16. - */ - assert(csum <= 960); - for (uint32_t i = LEN_1; i < LEN; i++) { - msg_i = (uint_fast8_t)(csum >> CSUM_BITSHIFT(i)) & 0x0f; - input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; - chain(HASH_ABSTRACTION(hashes) &expected_public_key->hashes[i], &input_prf, &signature->hashes[i], - (uint32_t)msg_i, W - 1 - msg_i); - } + return chain_steps_done; } diff --git a/src/wotsp.h b/src/wotsp.h index aab0c66..38c4ac6 100644 --- a/src/wotsp.h +++ b/src/wotsp.h @@ -8,7 +8,7 @@ /** * @file * @brief - * WOTS+ signatures and verification. + * WOTS+ common functionality. */ #pragma once @@ -19,11 +19,19 @@ #include -#include "config.h" - -#include "private.h" +#include "libxmss.h" +#include "types.h" #include "xmss_hashes.h" +/** + * @brief + * The number of digests in a WOTS+ private or uncompressed public key. + * + * @details + * This holds for all supported parameter sets, see RFC 8391, Section 5.2 and NIST SP 800-208, Section 5.1 and 5.3. + */ +#define XMSS_WOTSP_LEN 67 + /** * @brief * A WOTS+ public key. @@ -31,7 +39,7 @@ * @details * See RFC 8391, Section 3.1.4. */ -typedef struct { +typedef struct WotspPublicKey { /** @brief The hash values that make up the WOTS+ public key. */ XmssNativeValue256 hashes[XMSS_WOTSP_LEN]; } WotspPublicKey; @@ -43,87 +51,35 @@ typedef struct { * @details * See RFC 8391, Section 3.1.5. */ -typedef struct { +typedef struct WotspSignature { /** @brief The hash values that make up the WOTS+ signature. */ XmssNativeValue256 hashes[XMSS_WOTSP_LEN]; } WotspSignature; /** * @brief - * Generates the WOTS+ public key for the given seeds. + * Chaining function for WOTS+ signatures and verification. * * @details - * Based on NIST SP 800-208, Section 7.2.1, Algorithm 10' for the private key generation from the seeds, - * and on RFC-8391, Section 3.1.4, Algorithm 4 for generating the public key from the private key. - * - * We only pass the OTS address from ADRS because all other parts of ADRS are either known or set during the - * public key generation process. - * - * @param[in] context The signing context. - * @param[out] public_key The location to write the public key. - * @param[in] secret_seed The secret seed for PRFkeygen. - * @param[in] public_seed The public seed for PRF and PRFkeygen. - * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. (Part of ADRS in the RFC.) -*/ -void wotsp_gen_public_key(const struct XmssSigningContext *restrict context, - WotspPublicKey *restrict public_key, - const XmssNativeValue256 *restrict secret_seed, - const XmssNativeValue256 *restrict public_seed, - uint32_t ots_address); - -/** - * @brief - * Generates a WOTS+ signature for the given message digest. - * - * @details - * Based on NIST SP 800-208, Section 7.2.1, Algorithm 10' for the private key generation from the seeds, - * and on RFC-8391, Section 3.1.5, Algorithm 5 for the actual signing procedure. - * - * We only pass the OTS address from ADRS because all other parts of ADRS are either known or set during the - * signing process. - * - * @param[in] context The signing context. - * @param[out] signature The location to write the signature. - * @param[in] message_digest The message digest to sign. - * @param[in] secret_seed The secret seed for PRFkeygen. - * @param[in] public_seed The public seed for PRF and PRFkeygen. - * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. (Part of ADRS in the RFC.) - */ -void wotsp_sign(const struct XmssSigningContext *restrict context, - WotspSignature *restrict signature, - const XmssNativeValue256 *restrict message_digest, - const XmssNativeValue256 *restrict secret_seed, - const XmssNativeValue256 *restrict public_seed, - uint32_t ots_address); - -/** - * @brief - * Calculates the WOTS+ public key that is consistent with the given message, signature, seed and OTS address. - * - * @details - * Based on RFC-8391, Section 3.1.6., Algorithm 6. If a message was signed with a given secret key and this algorithm - * is applied to the message and signature, it outputs the public key that corresponds to that secret key, i.e., the - * one that would be output by Algorithm 4 / wotsp_gen_public_key(). - * - * A proper verification function for WOTS+ is not needed in XMSS. If the output of this function does not match the - * WOTS+ public key that was used in the XMSS hash tree, xmss_calculate_expected_public_key() will produce an output - * that does not match the XMSS public key. + * Based on RFC-8391, Section 3.1.2. (Algorithm 2) with the following changes: + * - for-loop instead of recursive calls + * - instead of SEED and ADRS, pass an Input_PRF struct pre-filled with these values, because most of it does not + * change between chain calls within one public key generation, signing or verification process. * - * We only pass the OTS address from ADRS because all other parts of ADRS are either known or set during the - * verification process. + * @param[in] hash_functions The hash functions to use. + * @param[out] output Output. + * @param[in,out] input_prf Input_PRF struct filled with the PRF seed and ADRS. + * At the end of this function, the values of the hash_address and keyAndMask fields in + * ADRS are unspecified. The Input_PRF can still be used for further calls to chain(), + * since it initializes those fields to the correct values. + * @param[in] input Input. (Corresponds to X in Algorithm 2.) + * @param[in] start_index Starting index for the chain. (Corresponds to i in Algorithm 2.) + * @param[in] num_steps Number of chain steps to perform. (Corresponds to s in Algorithm 2.) * - * @param[in] hashes The hash functions to use. - * @param[out] expected_public_key The location to write the public key that is consistent with the given parameters. - * @param[in] message_digest The message digest to verify. - * @param[in] signature The (purported) signature on the message digest. - * @param[in] public_seed The public seed for PRF. - * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. (Part of ADRS in the RFC.) + * @returns the number of chain steps performed, used for detecting faults. Must equal num_steps. */ -void wotsp_calculate_expected_public_key(HASH_ABSTRACTION(const xmss_hashes *restrict hashes) - WotspPublicKey *restrict expected_public_key, - const XmssNativeValue256 *restrict message_digest, - const WotspSignature *restrict signature, - const XmssNativeValue256 *restrict public_seed, - uint32_t ots_address); +LIBXMSS_STATIC +uint_fast8_t chain(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *output, Input_PRF *input_prf, + const XmssNativeValue256 *input, uint32_t start_index, uint_fast8_t num_steps); #endif /* !XMSS_WOTSP_H_INCLUDED */ diff --git a/src/wotsp_private.h b/src/wotsp_private.h new file mode 100644 index 0000000..427aa99 --- /dev/null +++ b/src/wotsp_private.h @@ -0,0 +1,104 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * Common WOTS+ functionality, private for the implementations of both signatures and verification. + */ + +#pragma once + +#ifndef XMSS_WOTSP_PRIVATE_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_WOTSP_H_INCLUDED + +#include +#include + +#include "compat.h" +#include "types.h" + + +/** + * @brief + * Winternitz parameter for WOTS+, set to 16 as specified in RFC-8391, Section 5.3. + * + * @details + * WOTS+ processes the input to be signed in base-W digits. See RFC-8391, Section 3.1.1. + */ +#define W 16 +/** + * @brief + * The length of the base-W representation of the message digest to sign or verify. + * + * @details + * Since we sign 256-bit message digests, there are 64 digits. See RFC-8391, Section 3.1.1. + */ +#define LEN_1 64 +/** + * @brief + * The length of the base-W representation of the checksum that is calculated during signing and verification. + * + * @details + * The maximum checksum value is LEN_1 * (W - 1) = 960, which needs 3 base-16 digits to represent. + * See RFC-8391, Section 3.1.1. + */ +#define LEN_2 3 +/** + * @brief + * The number of hashes that make up a WOTS+ private key, public key or signature. See RFC-8391, Section 3.1.1. + */ +#define LEN (LEN_1 + LEN_2) + +/** @private */ +XMSS_STATIC_ASSERT(LEN == XMSS_WOTSP_LEN, "Mismatch in WOTS+ output length."); + +/** + * @brief + * Amount to shift csum in RFC-8391, Algorithms 5 and 6, to get the base-W digits msg[i] for LEN_1 <= i < LEN. + */ +#define CSUM_BITSHIFT(i) (4 * (LEN_2 - 1 - (i - LEN_1))) + +/** + * @brief + * Fill input_prf with the values that don't change between calls to chain. + * + * @param[out] input_prf The input_prf struct to fill. It is assumed to be initialized with INIT_INPUT_PRF. + * @param[in] seed Public seed for the PRF. + * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. + */ +inline static void prepare_input_prf_for_chain(Input_PRF *const input_prf, const XmssNativeValue256 *const seed, + const uint32_t ots_address) +{ + input_prf->KEY = *seed; + input_prf->M.ADRS.type = ADRS_type_OTS_Hash_Address; + input_prf->M.ADRS.typed.OTS_Hash_Address.OTS_address = ots_address; +} + +/** + * @brief + * Returns the ith base-W digit in the message-digest, in big-endian byte order. + * + * @details + * Conceptually, this corresponds to accessing msg[i] for i < LEN_1 in the WOTS+ algorithms in RFC-8391, where msg is + * the base-W representation of the message being signed. Since we use W = 16, each base-W digit is four bits, which + * makes it easy to calculate the digits on the fly without computing the entire base-W representation ahead of time. + * + * @param[in] message_digest The message digest. + * @param[in] i Index of the four-bit chunk. + * + * @returns The ith base-W digit. + */ +inline static uint_fast8_t get_msg_i(const XmssNativeValue256 *const message_digest, const uint32_t i) +{ + assert(i < LEN_1); + uint_fast8_t shift = (7 - (i % 8)) * 4; + return (uint_fast8_t)(message_digest->data[i / 8] >> shift) & 0x0f; +} + +#endif /* !XMSS_WOTSP_PRIVATE_H_INCLUDED */ diff --git a/src/wotsp_signing.c b/src/wotsp_signing.c new file mode 100644 index 0000000..b328017 --- /dev/null +++ b/src/wotsp_signing.c @@ -0,0 +1,211 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * WOTS+ signatures. + */ + +#include +#include +#include + +#include "config.h" +#include "wotsp_signing.h" + +#include "fault_detection_helpers.h" +#include "signing_private.h" +#include "utils.h" +#include "wotsp_private.h" +#include "xmss_hashes.h" + +/** + * @brief + * Prepare input_prfkeygen for generating the WOTS+ private key. + * + * @param[out] input_prfkeygen The input_prf struct to fill. It is assumed to be initialized with INIT_INPUT_PRF. + * @param[in] secret_seed Secret seed for PRFkeygen. + * @param[in] public_seed Public seed for PRFkeygen. + * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. + */ +inline static void prepare_input_prfkeygen(Input_PRFkeygen *const input_prfkeygen, + const XmssNativeValue256 *const secret_seed, const XmssNativeValue256 *const public_seed, + const uint32_t ots_address) +{ + input_prfkeygen->S_XMSS = *secret_seed; + input_prfkeygen->SEED = *public_seed; + input_prfkeygen->ADRS.type = ADRS_type_OTS_Hash_Address; + input_prfkeygen->ADRS.typed.OTS_Hash_Address.OTS_address = ots_address; +} + +/** + * @brief + * Get the ith 256-bit block of the secret key, in native byte order. + * + * @details + * Conceptually, this corresponds to accessing sk[i] in the WOTS+ public key generation and signing algorithms in + * RFC-8391 (Algorithms 4 and 5). However, we do not keep the entire secret key in memory and instead calculate the + * required part with PRFkeygen. See NIST SP 800-208, Algorithm 10' for the inputs to PRFkeygen. + * + * @param[in] hash_functions The hash functions to use. + * @param[out] sk_i Place to store the secret key block. + * @param[in,out] input_prfkeygen Input_PRFkeygen containing the seeds and ADRS for generating the secret key. + * This function sets the chain_address in ADRS to i. + * @param[in] i Index of the secret key block to generate. + */ +inline static void get_sk_i(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const sk_i, + Input_PRFkeygen *const input_prfkeygen, const uint32_t i) +{ + input_prfkeygen->ADRS.typed.OTS_Hash_Address.chain_address = i; + xmss_PRFkeygen(HASH_FUNCTIONS sk_i, input_prfkeygen); +} + +void wotsp_gen_public_key(const XmssSigningContext *const context, WotspPublicKey *const public_key, + const XmssNativeValue256 *const secret_seed, const XmssNativeValue256 *const public_seed, + const uint32_t ots_address) +{ + /* + * Unlike in RFC-8391, Algorithm 4, we don't have the entire secret key sk ahead of time. Instead, we generate the + * blocks sk[i] of the secret key as they are needed. Here, we reserve the space to store one 256-bit block of it. + * We leave it uninitialized for performance reasons. + */ + XmssNativeValue256 sk_i; + /* + * Prepare the Input_PRF and Input_PRFkeygen structs. This part does not correspond to anything in RFC-8391, + * Algorithm 4. Input_PRFkeygen is needed to generate the secret key blocks. Input_PRF is a struct that holds SEED + * and ADRS for chain(). + */ + Input_PRFkeygen input_prfkeygen = INIT_INPUT_PRFKEYGEN; + Input_PRF input_prf = INIT_INPUT_PRF; + prepare_input_prfkeygen(&input_prfkeygen, secret_seed, public_seed, ots_address); + prepare_input_prf_for_chain(&input_prf, public_seed, ots_address); + + for (uint32_t i = 0; i < LEN; i++) { + /* Get the next block sk[i] of the secret key. */ + get_sk_i(HASH_FUNCTIONS_FROM(*context) &sk_i, &input_prfkeygen, i); + + /* Run W - 1 = 15 steps of the chain function on sk[i]. The result is pk[i] in RFC-8391, Algorithm 4. */ + input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; + (void)chain(HASH_FUNCTIONS_FROM(*context) &public_key->hashes[i], &input_prf, &sk_i, 0, W - 1); + } + + context->zeroize(&input_prfkeygen.S_XMSS, sizeof(input_prfkeygen.S_XMSS)); + context->zeroize(&sk_i, sizeof(sk_i)); +} + +XmssError wotsp_sign(const XmssSigningContext *const context, WotspSignature *const signature, + const XmssNativeValue256 *const message_digest, const XmssNativeValue256 *const secret_seed, + const XmssNativeValue256 *const public_seed, const uint32_t ots_address) +{ + /* + * Variables related to fault detection, not part of the algorithm. + */ + volatile XmssError result = XMSS_UNINITIALIZED; + uint_fast8_t chain_steps = 0; + volatile uint_fast16_t expected_total_chain_steps = 0; + volatile uint_fast16_t total_chain_steps = 0; + volatile uint_fast16_t redundant_csum = LEN_1 * (W - 1); + volatile uint_fast8_t redundant_msg_i = 0; + + /* + * Unlike in RFC-8391, Algorithm 5, we don't calculate the entire secret key sk and base-W representation of the + * input msg ahead of time. Instead, we generate these values when they are needed and reserve space for them here. + * We leave sk_i uninitialized for performance reasons. + */ + XmssNativeValue256 sk_i; + uint_fast8_t msg_i = 0; + /* + * In RFC-8391, Algorithm 5, the checksum csum starts at 0 and for every base-W digit msg[i] of the input, + * W - 1 - msg[i] is added. It is equivalent to start with LEN_1 * (W - 1) and subtract each msg[i]. + */ + uint_fast16_t csum = LEN_1 * (W - 1); + /* + * Prepare the Input_PRF and Input_PRFkeygen structs. This part does not correspond to anything in RFC-8391, + * Algorithm 5. Input_PRFkeygen is needed to generate the secret key blocks. Input_PRF is a struct that holds SEED + * and ADRS for chain(). + */ + Input_PRFkeygen input_prfkeygen = INIT_INPUT_PRFKEYGEN; + Input_PRF input_prf = INIT_INPUT_PRF; + prepare_input_prfkeygen(&input_prfkeygen, secret_seed, public_seed, ots_address); + prepare_input_prf_for_chain(&input_prf, public_seed, ots_address); + + /* + * Process the message digest. This combines the loop for calculating the checksum and the first LEN_1 iterations + * of the final loop in RFC-8391, Algorithm 5. + */ + for (uint32_t i = 0; i < LEN_1; i++) { + msg_i = get_msg_i(message_digest, i); + get_sk_i(HASH_FUNCTIONS_FROM(*context) &sk_i, &input_prfkeygen, i); + csum -= msg_i; + + /* Run msg[i] steps of the chain function on sk[i] as input. The result is sig[i] in RFC-8391, Algorithm 5. */ + input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; + chain_steps = chain(HASH_FUNCTIONS_FROM(*context) &signature->hashes[i], &input_prf, &sk_i, 0, msg_i); + + /* + * Fault detection: Check that the number of steps performed in chain() is as expected. Recalculate msg_i to + * also protect against bit flips in this variable. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + redundant_msg_i = get_msg_i(message_digest, i); + redundant_csum -= redundant_msg_i; + REDUNDANT_RETURN_IF(chain_steps != redundant_msg_i, XMSS_ERR_FAULT_DETECTED); + total_chain_steps += chain_steps; + } + + /* + * Fault detection: Verify the checksum. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + REDUNDANT_RETURN_IF(csum != redundant_csum, XMSS_ERR_FAULT_DETECTED); + + /* + * Process the checksum. This corresponds to the last LEN_2 iterations of the final loop in RFC-8391, Algorithm 5. + * Note that the bit shift of csum in Algorithm 5 is used only to line it up with the toByte function. The result is + * the base-W digits of csum in big-endian order, which we calculate in a different way, using the fact that W = 16. + */ + assert(csum <= 960); + for (uint32_t i = LEN_1; i < LEN; i++) { + msg_i = (uint_fast8_t)(csum >> CSUM_BITSHIFT(i)) & 0x0f; + get_sk_i(HASH_FUNCTIONS_FROM(*context) &sk_i, &input_prfkeygen, i); + + input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; + chain_steps = chain(HASH_FUNCTIONS_FROM(*context) &signature->hashes[i], &input_prf, &sk_i, 0, msg_i); + + /* + * Fault detection: Check that the number of steps performed in chain() is as expected. Recalculate msg_i to + * also protect against bit flips in this variable. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + redundant_msg_i = (uint_fast8_t)(csum >> CSUM_BITSHIFT(i)) & 0x0f; + REDUNDANT_RETURN_IF(chain_steps != redundant_msg_i, XMSS_ERR_FAULT_DETECTED); + total_chain_steps += chain_steps; + } + + /* Zeroize the secret seed stored in input_prfkeygen, and the secret key block that is still in memory. */ + context->zeroize(&input_prfkeygen.S_XMSS, sizeof(input_prfkeygen.S_XMSS)); + context->zeroize(&sk_i, sizeof(sk_i)); + + /* + * Fault detection: Check that the total number of chain steps is as expected. + * The expected number of steps can be derived from csum: Because csum = LEN_1 * (W - 1) - sum_i(msg_i), the total + * number of chain steps during the message portion is LEN_1 * (W-1) - csum. The checksum portion adds the hex + * digits of csum to this total. We mix in both csum and redundant_csum in this calculation. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + expected_total_chain_steps = LEN_1 * (W - 1) - redundant_csum + ((csum >> CSUM_BITSHIFT(LEN_1)) & 0x0f) + + ((csum >> CSUM_BITSHIFT(LEN_1 + 1)) & 0x0f) + + ((csum >> CSUM_BITSHIFT(LEN_1 + 2)) & 0x0f); + REDUNDANT_RETURN_IF(total_chain_steps != expected_total_chain_steps, XMSS_ERR_FAULT_DETECTED); + result = XMSS_OKAY; + return result; +} diff --git a/src/wotsp_signing.h b/src/wotsp_signing.h new file mode 100644 index 0000000..572bc2e --- /dev/null +++ b/src/wotsp_signing.h @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * WOTS+ signatures. + */ + +#pragma once + +#ifndef XMSS_WOTSP_SIGNING_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_WOTSP_SIGNING_H_INCLUDED + +#include + +#include "config.h" + +#include "libxmss.h" +#include "signing_private.h" +#include "wotsp.h" +#include "xmss_hashes.h" + +/** + * @brief + * Generates the WOTS+ public key for the given seeds. + * + * @details + * Based on NIST SP 800-208, Section 7.2.1, Algorithm 10' for the private key generation from the seeds, + * and on RFC-8391, Section 3.1.4, Algorithm 4 for generating the public key from the private key. + * + * We only pass the OTS address from ADRS because all other parts of ADRS are either known or set during the + * public key generation process. + * + * @param[in] context The signing context. + * @param[out] public_key The location to write the public key. + * @param[in] secret_seed The secret seed for PRFkeygen. + * @param[in] public_seed The public seed for PRF and PRFkeygen. + * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. (Part of ADRS in the RFC.) +*/ +LIBXMSS_STATIC +void wotsp_gen_public_key(const XmssSigningContext *context, WotspPublicKey *public_key, + const XmssNativeValue256 *secret_seed, const XmssNativeValue256 *public_seed, uint32_t ots_address); + +/** + * @brief + * Generates a WOTS+ signature for the given message digest. + * + * @details + * Based on NIST SP 800-208, Section 7.2.1, Algorithm 10' for the private key generation from the seeds, + * and on RFC-8391, Section 3.1.5, Algorithm 5 for the actual signing procedure. + * + * We only pass the OTS address from ADRS because all other parts of ADRS are either known or set during the + * signing process. + * + * @param[in] context The signing context. + * @param[out] signature The location to write the signature. + * @param[in] message_digest The message digest to sign. + * @param[in] secret_seed The secret seed for PRFkeygen. + * @param[in] public_seed The public seed for PRF and PRFkeygen. + * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. (Part of ADRS in the RFC.) + * + * @retval XMSS_OKAY The message was signed successfully. + * @retval XMSS_ERR_FAULT_DETECTED A fault was detected. + */ +LIBXMSS_STATIC +XmssError wotsp_sign(const XmssSigningContext *context, WotspSignature *signature, + const XmssNativeValue256 *message_digest, const XmssNativeValue256 *secret_seed, + const XmssNativeValue256 *public_seed, uint32_t ots_address); + +#endif /* !XMSS_WOTSP_SIGNING_H_INCLUDED */ diff --git a/src/wotsp_verification.c b/src/wotsp_verification.c new file mode 100644 index 0000000..01d85c8 --- /dev/null +++ b/src/wotsp_verification.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * WOTS+ verification. + */ + +#include +#include + +#include "wotsp_verification.h" + +#include "fault_detection_helpers.h" +#include "utils.h" +#include "types.h" +#include "wotsp_private.h" +#include "xmss_hashes.h" + + +XmssError wotsp_calculate_expected_public_key(HASH_FUNCTIONS_PARAMETER WotspPublicKey *const expected_public_key, + const XmssNativeValue256 *const message_digest, const WotspSignature *const signature, + const XmssNativeValue256 *const public_seed, const uint32_t ots_address) +{ + /* + * Variables related to fault detection, not part of the algorithm. + */ + volatile XmssError result = XMSS_UNINITIALIZED; + uint_fast8_t chain_steps = 0; + volatile uint_fast16_t expected_total_chain_steps = 0; + volatile uint_fast16_t total_chain_steps = 0; + volatile uint_fast16_t redundant_csum = LEN_1 * (W - 1); + volatile uint_fast8_t redundant_msg_i = 0; + + /* + * Unlike in RFC-8391, Algorithm 6, we don't calculate the entire base-W representation of the message digest ahead + * of time. Instead, we generate these values when they are needed and reserve space for them here. + */ + uint_fast8_t msg_i = 0; + /* + * In RFC-8391, Algorithm 6, the checksum csum starts at 0 and for every base-W digit msg[i] of the message digest, + * W - 1 - msg[i] is added. It is equivalent to start with LEN_1 * (W - 1) and subtract each msg[i]. + */ + uint_fast16_t csum = LEN_1 * (W - 1); + /* + * Prepare the Input_PRF struct. This part does not correspond to anything in RFC-8391, Algorithm 6. Input_PRF is a + * struct that holds SEED and ADRS for chain(). + */ + Input_PRF input_prf = INIT_INPUT_PRF; + prepare_input_prf_for_chain(&input_prf, public_seed, ots_address); + + /* + * Process the message digest. This combines the loop for calculating the checksum and the first LEN_1 iterations + * of the final loop in RFC-8391, Algorithm 6. + */ + for (uint32_t i = 0; i < LEN_1; i++) { + chain_steps = 0; + msg_i = get_msg_i(message_digest, i); + csum -= msg_i; + input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; + chain_steps = chain(HASH_FUNCTIONS &expected_public_key->hashes[i], &input_prf, &signature->hashes[i], + (uint32_t)msg_i, W - 1 - msg_i); + + /* + * Fault detection: Check that the number of steps performed in chain() is as expected. Recalculate msg_i to + * also protect against bit flips in this variable. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + redundant_msg_i = get_msg_i(message_digest, i); + redundant_csum -= redundant_msg_i; + REDUNDANT_RETURN_IF(chain_steps != W - 1 - redundant_msg_i, XMSS_ERR_FAULT_DETECTED); + total_chain_steps += chain_steps; + } + + /* + * Fault detection: Verify the checksum. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + REDUNDANT_RETURN_IF(csum != redundant_csum, XMSS_ERR_FAULT_DETECTED); + + /* + * Process the checksum. This corresponds to the last LEN_2 iterations of the final loop in RFC-8391, Algorithm 6. + * Note that the bit shift of csum in Algorithm 6 is used only to line it up with the toByte function. The result is + * the base-W digits of csum in big-endian order, which we calculate in a different way, using the fact that W = 16. + */ + assert(csum <= 960); + for (uint32_t i = LEN_1; i < LEN; i++) { + chain_steps = 0; + msg_i = (uint_fast8_t)(csum >> CSUM_BITSHIFT(i)) & 0x0f; + input_prf.M.ADRS.typed.OTS_Hash_Address.chain_address = i; + chain_steps = chain(HASH_FUNCTIONS &expected_public_key->hashes[i], &input_prf, &signature->hashes[i], + (uint32_t)msg_i, W - 1 - msg_i); + + /* + * Fault detection: Check that the number of steps performed in chain() is as expected. Recalculate msg_i to + * also protect against bit flips in this variable. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + redundant_msg_i = (uint_fast8_t)(redundant_csum >> CSUM_BITSHIFT(i)) & 0x0f; + REDUNDANT_RETURN_IF(chain_steps != W - 1 - redundant_msg_i, XMSS_ERR_FAULT_DETECTED); + total_chain_steps += chain_steps; + } + + /* + * Fault detection: Check that the total number of chain steps is as expected. + * We can derive the expected number of chain steps from csum: By definition, csum is the total number of chain + * steps during the message portion. The csum portion adds LEN_2 * (W - 1) minus the hex digits of csum. + * We mix in both csum and redundant_csum in this calculation. + * + * This is not part of the algorithm but a countermeasure against fault injection attacks. + */ + expected_total_chain_steps = redundant_csum + LEN_2 * (W - 1) - ((csum >> CSUM_BITSHIFT(LEN_1)) & 0x0f) + - ((csum >> CSUM_BITSHIFT(LEN_1 + 1)) & 0x0f) + - ((csum >> CSUM_BITSHIFT(LEN_1 + 2)) & 0x0f); + REDUNDANT_RETURN_IF(total_chain_steps != expected_total_chain_steps, XMSS_ERR_FAULT_DETECTED); + result = XMSS_OKAY; + return result; +} diff --git a/src/wotsp_verification.h b/src/wotsp_verification.h new file mode 100644 index 0000000..14fe408 --- /dev/null +++ b/src/wotsp_verification.h @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * WOTS+ verification. + */ + +#pragma once + +#ifndef XMSS_WOTSP_VERIFICATION_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_WOTSP_VERIFICATION_H_INCLUDED + +#include + +#include "libxmss.h" +#include "types.h" +#include "wotsp.h" +#include "xmss_hashes.h" + +/** + * @brief + * Calculates the WOTS+ public key that is consistent with the given message, signature, seed and OTS address. + * + * @details + * Based on RFC-8391, Section 3.1.6., Algorithm 6. If a message was signed with a given secret key and this algorithm + * is applied to the message and signature, it outputs the public key that corresponds to that secret key, i.e., the + * one that would be output by Algorithm 4 / wotsp_gen_public_key(). + * + * A proper verification function for WOTS+ is not needed in XMSS. If the output of this function does not match the + * WOTS+ public key that was used in the XMSS hash tree, xmss_verification_check() will fail. + * + * We only pass the OTS address from ADRS because all other parts of ADRS are either known or set during the + * verification process. + * + * @param[in] hash_functions The hash functions to use. + * @param[out] expected_public_key The location to write the public key that is consistent with the given parameters. + * @param[in] message_digest The message digest to verify. + * @param[in] signature The (purported) signature on the message digest. + * @param[in] public_seed The public seed for PRF. + * @param[in] ots_address Index of the OTS key pair in the larger XMSS scheme. (Part of ADRS in the RFC.) + * + * @retval XMSS_OKAY Expected public key was calculated successfully. + * @retval XMSS_ERR_FAULT_DETECTED A fault was detected. + */ +LIBXMSS_STATIC +XmssError wotsp_calculate_expected_public_key(HASH_FUNCTIONS_PARAMETER WotspPublicKey *expected_public_key, + const XmssNativeValue256 *message_digest, const WotspSignature *signature, const XmssNativeValue256 *public_seed, + uint32_t ots_address); + +#endif /* !XMSS_WOTSP_VERIFICATION_H_INCLUDED */ diff --git a/src/xmss_hashes.c b/src/xmss_hashes.c index a6c4f85..20ef03c 100644 --- a/src/xmss_hashes.c +++ b/src/xmss_hashes.c @@ -27,7 +27,10 @@ #include "structures.h" -XmssError xmss_get_hash_functions(HASH_ABSTRACTION(xmss_hashes *restrict hash_functions) +XmssError xmss_get_hash_functions( +#if XMSS_ENABLE_HASH_ABSTRACTION + const xmss_hashes **const hash_functions, +#endif const XmssParameterSetOID parameter_set) { #if XMSS_ENABLE_HASH_ABSTRACTION @@ -42,7 +45,7 @@ XmssError xmss_get_hash_functions(HASH_ABSTRACTION(xmss_hashes *restrict hash_fu case XMSS_PARAM_SHA2_20_256: # if XMSS_ENABLE_SHA256 # if XMSS_ENABLE_HASH_ABSTRACTION - *hash_functions = sha256_xmss_hashes; + *hash_functions = &sha256_xmss_hashes; # endif # else /* The parameter set is not supported. */ @@ -55,7 +58,7 @@ XmssError xmss_get_hash_functions(HASH_ABSTRACTION(xmss_hashes *restrict hash_fu case XMSS_PARAM_SHAKE256_20_256: # if XMSS_ENABLE_SHAKE256_256 # if XMSS_ENABLE_HASH_ABSTRACTION - *hash_functions = shake256_256_xmss_hashes; + *hash_functions = &shake256_256_xmss_hashes; # endif # else /* The parameter set is not supported. */ diff --git a/src/xmss_hashes.h b/src/xmss_hashes.h index b7b24ad..a99cd9c 100644 --- a/src/xmss_hashes.h +++ b/src/xmss_hashes.h @@ -17,16 +17,16 @@ * There are 2 options: * * 1. The library is compiled to support both SHA-256 *and* SHAKE256/256. The context variable for the XMSS key is - * supposed to contain an xmss_hashes (or pointer to) variable that either equals (or points to) `sha256_xmss_hashes` - * or `shake256_256_xmss_hashes`. + * supposed to contain an `xmss_hashes *hash_functions` member that either points to `sha256_xmss_hashes` or to + * `shake256_256_xmss_hashes`. * - * You call the XMSS `F(...)` hash function by writing: `xmss_F(HASH_ABSTRACTION(context->xmss_hashes) ...)`. + * You call the XMSS `F(...)` hash function by writing: `xmss_F(HASH_FUNCTIONS_FROM(*context) ...)`. * * This will then call the right function. Note that in this case there is always a function call; it cannot be * inlined. * - * 2. The library is compiled to support either SHA-256 *or* SHAKE256/256. In this case there is no context variable, - * but the syntax is still `xmss_F(HASH_ABSTRACTION(context->xmss_hashes) ...)`. The macro will expand directly to + * 2. The library is compiled to support either SHA-256 *or* SHAKE256/256. In this case there is no context member, + * but the syntax is still `xmss_F(HASH_FUNCTIONS_FROM(*context) ...)`. The macro will expand directly to * the implementing function, without using the parameter. This allows the compiler to inline the call, without * unnecessary dereferencing. In fact, neither `sha256_xmss_hashes` nor `shake256_256_xmss_hashes` will be defined, * so the addresses of the functions are never taken. If all calls are inlined, the 'function' will not even exist. @@ -36,9 +36,19 @@ * * - Always include just `xmss_hashes.h` * - Guard your hash abstraction context variable with `#if XMSS_ENABLE_HASH_ABSTRACTION` - * - Call the hash function as `xmss_xxx(HASH_ABSTRACTION(context->xmss_hashes) ...)`. + * - Call the hash function as `xmss_xxx(HASH_FUNCTIONS_FROM(*context) ...)`. * - * NOTE: There is no comma between `HASH_ABSTRACTION()` and the next argument. + * NOTE: There is no comma between `HASH_FUNCTIONS_FROM(*context)` and the next argument. + * + * Functions that accept the hash functions as a parameter should use #HASH_FUNCTIONS_PARAMETER *without a comma* + * before the next argument. When hash abstractions are disabled, this becomes a no-op. + * + * Functions that need the hash functions based on a given parameter_set should use #DEFINE_HASH_FUNCTIONS and + * #INITIALIZE_HASH_FUNCTIONS. + * + * Functions that need to forward the hash functions from either their parameter (#HASH_FUNCTIONS_PARAMETER) or from + * their local variable (#DEFINE_HASH_FUNCTIONS), can simply call for example `xmss_F(HASH_FUNCTIONS ...)`. + * Note again the absence of a comma between #HASH_FUNCTIONS and the next argument. */ #pragma once @@ -48,11 +58,76 @@ #define XMSS_XMSS_HASHES_H_INCLUDED #include "config.h" +#include "libxmss.h" #include "structures.h" #include "xmss_hashes_base.h" #if XMSS_ENABLE_HASH_ABSTRACTION +/** + * @brief + * Used as the first parameter in the signature of functions that use the XMSS hash functions. + * + * @remarks + * When hash abstractions are disabled, this is a no-op. + */ +# define HASH_FUNCTIONS_PARAMETER const xmss_hashes *const hash_functions, + +/** + * @brief + * Function-like macro to assert that the #HASH_FUNCTIONS_PARAMETER is not NULL. + * + * @remarks + * When hash abstractions are disabled, this is a no-op. + */ +# define ASSERT_HASH_FUNCTIONS() assert(hash_functions != NULL); do { } while(0) + +/** + * @brief + * Used within functions that require the XMSS hash functions. + * + * Use #INITIALIZE_HASH_FUNCTIONS to initialize the defined hash_functions variable. + * + * @remarks + * When hash abstractions are disabled, this is a no-op. + */ +# define DEFINE_HASH_FUNCTIONS const xmss_hashes *hash_functions = NULL + +/** + * @brief + * Used to initialize the hash_functions variable define with #DEFINE_HASH_FUNCTIONS. + * + * @remarks + * When hash abstractions are disabled, this still validates whether the given parameter set is supported. + * + * @param[in] parameter_set The #XmssParameterSetOID that determines the hash algorithm. + */ +# define INITIALIZE_HASH_FUNCTIONS(parameter_set) xmss_get_hash_functions(&hash_functions, parameter_set) + +/** + * @brief + * Used as the first argument to any function that require the XMSS hash functions. + * Use this only when referring #HASH_FUNCTIONS_PARAMETER or #DEFINE_HASH_FUNCTIONS. + * Use #HASH_FUNCTIONS_FROM in other cases. + * + * @remarks + * When hash abstractions are disabled, this is a no-op. + */ +# define HASH_FUNCTIONS hash_functions, + +/** + * @brief + * Used as the first argument to any function that uses the XMSS hash functions. + * Use this only when referring to an xmss_hashes pointer stored in a structure. + * When referring to #HASH_FUNCTIONS_PARAMETER or #DEFINE_HASH_FUNCTIONS, use #HASH_FUNCTIONS instead. + * + * @remarks + * When hash abstractions are disabled, this is a no-op. + * + * @param[in] container A container object with an xmss_hashes *hash_functions member. + */ +# define HASH_FUNCTIONS_FROM(container) (container).hash_functions, + /* * This is the implementation of the XMSS_xxx() specialized hash functions for: * - SHA-256 support @@ -62,110 +137,138 @@ */ /** - * @copydoc prototype_digest - * @param[in] hashes The hash algorithm abstraction; provide this parameter with `HASH_ABSTRACTION(context->hashes)`. + * @copydoc prototype_F + * @param[in] hash_functions The hash function to use. * - * @see prototype_digest + * @see prototype_F */ -static inline void xmss_digest(const xmss_hashes *restrict const hashes, XmssValue256 *restrict const digest, - const uint8_t *restrict const message, const size_t message_length) +static inline void xmss_F(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, const Input_F *const input) { - (hashes->digest)(digest, message, message_length); + (hash_functions->F)(native_digest, input); } /** - * @copydoc prototype_native_digest - * @param[in] hashes The hash algorithm abstraction; provide this parameter with `HASH_ABSTRACTION(context->hashes)`. + * @copydoc prototype_H + * @param[in] hash_functions The hash function to use. * - * @see prototype_native_digest + * @see prototype_H */ -static inline void xmss_native_digest(const xmss_hashes *restrict const hashes, - XmssNativeValue256 *restrict const native_digest, const uint32_t *restrict const words, const size_t word_count) +static inline void xmss_H(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, const Input_H *const input) { - (hashes->native_digest)(native_digest, words, word_count); + (hash_functions->H)(native_digest, input); } /** - * @copydoc prototype_F - * @param[in] hashes The hash algorithm abstraction; provide this parameter with `HASH_ABSTRACTION(context->hashes)`. + * @copydoc prototype_H_msg_init + * @param[in] hash_functions The hash function to use. * - * @see prototype_F + * @see prototype_H_msg_init */ -static inline void xmss_F(const xmss_hashes *restrict const hashes, XmssNativeValue256 *restrict const native_digest, - const Input_F *restrict const input) +static inline void xmss_H_msg_init(HASH_FUNCTIONS_PARAMETER XmssHMsgCtx *const ctx, const Input_H_msg *const input) { - (hashes->F)(native_digest, input); + (hash_functions->H_msg_init)(ctx, input); } /** - * @copydoc prototype_H - * @param[in] hashes The hash algorithm abstraction; provide this parameter with `HASH_ABSTRACTION(context->hashes)`. + * @copydoc prototype_H_msg_update + * @param[in] hash_functions The hash function to use. * - * @see prototype_H + * @see prototype_H_msg_update */ -static inline void xmss_H(const xmss_hashes *restrict const hashes, XmssNativeValue256 *restrict const native_digest, - const Input_H *restrict const input) +static inline void xmss_H_msg_update(HASH_FUNCTIONS_PARAMETER XmssHMsgCtx *const ctx, const uint8_t *const part, + const size_t part_length, const uint8_t *volatile *const part_verify) { - (hashes->H)(native_digest, input); + (hash_functions->H_msg_update)(ctx, part, part_length, part_verify); } /** - * @copydoc prototype_H_msg - * @param[in] hashes The hash algorithm abstraction; provide this parameter with `HASH_ABSTRACTION(context->hashes)`. + * @copydoc prototype_H_msg_finalize + * @param[in] hash_functions The hash function to use. * - * @see prototype_H_msg + * @see prototype_H_msg_finalize */ -static inline void xmss_H_msg(const xmss_hashes *restrict const hashes, XmssNativeValue256 *restrict const native_digest, - const Input_H_msg *restrict const input, const uint8_t *restrict const message, const size_t message_length) +static inline void xmss_H_msg_finalize(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, + XmssHMsgCtx *const ctx) { - (hashes->H_msg)(native_digest, input, message, message_length); + (hash_functions->H_msg_finalize)(native_digest, ctx); } /** * @copydoc prototype_PRF - * @param[in] hashes The hash algorithm abstraction; provide this parameter with `HASH_ABSTRACTION(context->hashes)`. + * @param[in] hash_functions The hash function to use. * * @see prototype_PRF */ -static inline void xmss_PRF(const xmss_hashes *restrict const hashes, XmssNativeValue256 *restrict const native_digest, - const Input_PRF *restrict const input) +static inline void xmss_PRF(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, + const Input_PRF *const input) { - (hashes->PRF)(native_digest, input); + (hash_functions->PRF)(native_digest, input); } +#if XMSS_ENABLE_SIGNING + /** * @copydoc prototype_PRFkeygen - * @param[in] hashes The hash algorithm abstraction; provide this parameter using the HASH_ABSTRACTION() macro. + * @param[in] hash_functions The hash function to use. * * @see prototype_PRFkeygen */ -static inline void xmss_PRFkeygen(const xmss_hashes *restrict const hashes, - XmssNativeValue256 *restrict const native_digest, const Input_PRFkeygen *restrict const input) +static inline void xmss_PRFkeygen(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, + const Input_PRFkeygen *const input) { - (hashes->PRFkeygen)(native_digest, input); + (hash_functions->PRFkeygen)(native_digest, input); } /** * @copydoc prototype_PRFindex - * @param[in] hashes The hash algorithm abstraction; provide this parameter using the HASH_ABSTRACTION() macro. + * @param[in] hash_functions The hash function to use. * * @see prototype_PRFindex */ -static inline void xmss_PRFindex(const xmss_hashes *restrict const hashes, - XmssNativeValue256 *restrict const native_digest, const Input_PRFindex *restrict const input) +static inline void xmss_PRFindex(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, + const Input_PRFindex *const input) { - (hashes->PRFindex)(native_digest, input); + (hash_functions->PRFindex)(native_digest, input); } /** - * @brief - * Used as the first parameter to any of the XMSS hash functions. + * @copydoc prototype_digest + * @param[in] hash_functions The hash function to use. * - * @param[in] hashes A pointer to the xmss_hashes structure for a specific hash algorithm. + * @see prototype_digest */ -# define HASH_ABSTRACTION(hashes) hashes, +static inline void xmss_digest(HASH_FUNCTIONS_PARAMETER XmssValue256 *const digest, const uint8_t *const message, + const size_t message_length) +{ + (hash_functions->digest)(digest, message, message_length); +} -#elif XMSS_ENABLE_SHA256 +/** + * @copydoc prototype_native_digest + * @param[in] hash_functions The hash function to use. + * + * @see prototype_native_digest + */ +static inline void xmss_native_digest(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const native_digest, + const uint32_t *const words, const size_t word_count) +{ + (hash_functions->native_digest)(native_digest, words, word_count); +} + +#endif /* XMSS_ENABLE_SIGNING */ + +#else /* !XMSS_ENABLE_HASH_ABSTRACTION */ + +/* This removes the entire abstraction layer, in case only a single hashing algorithm is enabled. */ + +# define HASH_FUNCTIONS_PARAMETER +# define ASSERT_HASH_FUNCTIONS() do { } while(0) +# define DEFINE_HASH_FUNCTIONS do { } while(0) +# define INITIALIZE_HASH_FUNCTIONS(parameter_set) xmss_get_hash_functions(parameter_set) +# define HASH_FUNCTIONS +# define HASH_FUNCTIONS_FROM(container) + +# if XMSS_ENABLE_SHA256 /* * This is the implementation of the XMSS specialized hash functions if only SHA-256 is enabled. @@ -177,20 +280,23 @@ static inline void xmss_PRFindex(const xmss_hashes *restrict const hashes, * This optimizes both performance *and* code size. */ -# include "sha256_xmss_hashes.h" - -# define xmss_digest sha256_digest -# define xmss_native_digest sha256_native_digest -# define xmss_F sha256_F -# define xmss_H sha256_H -# define xmss_H_msg sha256_H_msg -# define xmss_PRF sha256_PRF -# define xmss_PRFkeygen sha256_PRFkeygen -# define xmss_PRFindex sha256_PRFindex +# include "sha256_xmss_hashes.h" -# define HASH_ABSTRACTION(hashes) +# define xmss_F sha256_F +# define xmss_H sha256_H +# define xmss_H_msg sha256_H_msg +# define xmss_H_msg_init sha256_H_msg_init +# define xmss_H_msg_update sha256_H_msg_update +# define xmss_H_msg_finalize sha256_H_msg_finalize +# define xmss_PRF sha256_PRF +# if XMSS_ENABLE_SIGNING +# define xmss_PRFkeygen sha256_PRFkeygen +# define xmss_PRFindex sha256_PRFindex +# define xmss_digest sha256_digest +# define xmss_native_digest sha256_native_digest +# endif -#elif XMSS_ENABLE_SHAKE256_256 +# elif XMSS_ENABLE_SHAKE256_256 /* * This is the implementation of the XMSS specialized hash functions if only SHAKE256/256 is enabled. @@ -203,29 +309,36 @@ static inline void xmss_PRFindex(const xmss_hashes *restrict const hashes, * This optimizes both performance *and* code size, but less than for SHA-256. */ -# include "shake256_256_xmss_hashes.h" +# include "shake256_256_xmss_hashes.h" -# define xmss_digest shake256_256_digest -# define xmss_native_digest shake256_256_native_digest -# define xmss_F shake256_256_F -# define xmss_H shake256_256_H -# define xmss_H_msg shake256_256_H_msg -# define xmss_PRF shake256_256_PRF -# define xmss_PRFkeygen shake256_256_PRFkeygen -# define xmss_PRFindex shake256_256_PRFindex +# define xmss_F shake256_256_F +# define xmss_H shake256_256_H +# define xmss_H_msg shake256_256_H_msg +# define xmss_H_msg_init shake256_256_H_msg_init +# define xmss_H_msg_update shake256_256_H_msg_update +# define xmss_H_msg_finalize shake256_256_H_msg_finalize +# define xmss_PRF shake256_256_PRF +# if XMSS_ENABLE_SIGNING +# define xmss_PRFkeygen shake256_256_PRFkeygen +# define xmss_PRFindex shake256_256_PRFindex +# define xmss_digest shake256_256_digest +# define xmss_native_digest shake256_256_native_digest +# endif -# define HASH_ABSTRACTION(hashes) +# else -#else +# error Invalid hash configuration. -# error Invalid hash configuration. +# endif -#endif +#endif /* !XMSS_ENABLE_HASH_ABSTRACTION */ /** * @brief * Returns the hash functions for the specified parameter set. * + * Do not call this function directly, instead use #INITIALIZE_HASH_FUNCTIONS. + * * @details * When compiled without hash abstraction support, this function still verifies that the required hash function for this * parameter_set is compiled in. @@ -236,7 +349,11 @@ static inline void xmss_PRFindex(const xmss_hashes *restrict const hashes, * @retval XMSS_ERR_INVALID_ARGUMENT parameter_set is unsupported or invalid. * @retval XMSS_ERR_NULL_POINTER hash_functions was NULL. */ -XmssError xmss_get_hash_functions(HASH_ABSTRACTION(xmss_hashes *restrict hash_functions) +LIBXMSS_STATIC +XmssError xmss_get_hash_functions( +#if XMSS_ENABLE_HASH_ABSTRACTION + const xmss_hashes **hash_functions, +#endif XmssParameterSetOID parameter_set); #endif /* !XMSS_XMSS_HASHES_H_INCLUDED */ diff --git a/src/xmss_hashes_base.h b/src/xmss_hashes_base.h index 1b90606..bd318e3 100644 --- a/src/xmss_hashes_base.h +++ b/src/xmss_hashes_base.h @@ -22,12 +22,16 @@ #include "config.h" +#include "structures.h" #include "types.h" #include "utils.h" /** @brief Block size of SHA-256 in bytes. */ #define SHA256_BLOCK_SIZE 64 +/** @brief Size of the Keccak state array (for SHAKE256_256 calculations). */ +#define KECCAK_STATE_ARRAY_SIZE 200 + /** @brief Value for the ADRS.type member to indicate the union member typed.OTS_Hash_Address is used. */ #define ADRS_type_OTS_Hash_Address (0u) @@ -42,7 +46,7 @@ * The ADRS structure as defined by RFC 8391, Section 2.5. This structure acts as a unique salt for each of the * XMSS hash functions and improves the collision resistance of the underlying hash algorithm. */ -typedef struct { +typedef struct ADRS { /** @brief As this implementation does not support Multi-Tree XMSS, this value is always 0. */ uint32_t layer_address; /** @@ -127,7 +131,7 @@ typedef struct { * @brief * Struct holding the index of the signature for generating the per-message randomness. Equals toByte(idx_sig,32). */ -typedef struct { +typedef struct IdxSigBlock { /** @brief Zeros. */ uint32_t zero[7]; /** @brief The index of the signature. */ @@ -135,7 +139,7 @@ typedef struct { } IdxSigBlock; /** @private */ -STATIC_ASSERT(sizeof(ADRS) == sizeof(IdxSigBlock), "ADRS and IdxSigBlock should have equal size."); +XMSS_STATIC_ASSERT(sizeof(ADRS) == sizeof(IdxSigBlock), "ADRS and IdxSigBlock should have equal size."); /** * @brief @@ -178,11 +182,11 @@ STATIC_ASSERT(sizeof(ADRS) == sizeof(IdxSigBlock), "ADRS and IdxSigBlock should * * XmssNativeValue256 *source_key; * uint8_t *source_message; - * native_256_copy(&input.KEY, source_key); + * input.KEY = *source_key; * big_endian_to_native(input.M, source_message, 8); * ``` */ -typedef struct { +typedef struct Input_F { /** @brief Initialize to toByte(0). */ const XmssNativeValue256 prefix_0; @@ -243,11 +247,11 @@ typedef struct { * * XmssNativeValue256 *source_key; * uint8_t *source_message; - * native_256_copy(&input.KEY, source_key); + * input.KEY = *source_key; * big_endian_to_native(input.M[0].data, source_message, 16); * ``` */ -typedef struct { +typedef struct Input_H { /** @brief Initialize to toByte(1). */ XmssNativeValue256 prefix_1; @@ -317,12 +321,12 @@ typedef struct { * XmssNativeValue256 *source_r; * XmssValue256 *source_root; * uint32_t source_idx_sig; - * native_256_copy(input.r, source_r); + * input.r = *source_r; * big_endian_to_native_256(&input.Root, source_root); * input.idx_sig = source_idx_sig; * ``` */ -typedef struct { +typedef struct Input_H_msg { /** @brief Initialize to toByte(2). */ const XmssNativeValue256 prefix_2; @@ -373,11 +377,11 @@ typedef struct { * * XmssNativeValue256 *source_key; * ADRS *source_adrs; - * native_256_copy(&input.KEY, source_key); + * input.KEY = *source_key; * big_endian_to_native(input.M.ADRS, source_adrs, sizeof(ADRS)); * ``` */ -typedef struct { +typedef struct Input_PRF { /** @brief Initialize to toByte(3). */ const XmssNativeValue256 prefix_3; /** @brief The first parameter to the XMSS PRF(KEY,M) hash function. */ @@ -440,10 +444,10 @@ typedef struct { * ADRS *source_adrs; * big_endian_to_native_256(&input.S_XMSS, source_s_xmss); * big_endian_to_native_256(&input.SEED, source_seed); - * memcpy(input.ADRS, source_adrs, sizeof(ADRS)); + * input.ADRS = *source_adrs; * ``` */ -typedef struct { +typedef struct Input_PRFkeygen { /** @brief Initialize to toByte(4). */ const XmssNativeValue256 prefix_4; /** @brief The secret key generation seed. */ @@ -507,7 +511,7 @@ typedef struct { * input.drbg_counter = source_drbg_counter; * ``` */ -typedef struct { +typedef struct Input_PRFindex { /** @brief Initialize to toByte(5). */ const XmssNativeValue256 prefix_5; /** @brief The secret index permutation seed. */ @@ -553,28 +557,59 @@ typedef struct { INIT_PADDING_SHA256(TO_BITS(INPUT_PRF_INDEX_DATA_SIZE)) \ } -#if XMSS_ENABLE_HASH_ABSTRACTION +#if XMSS_ENABLE_SHA256 +/** @brief Context when we calculate SHA256 hashes with streaming data. */ +typedef struct XmssHMsgSha256Ctx { + /** @brief Intermediate hash value. */ + XmssNativeValue256 intermediate_hash; + /** @brief Partial block, for when the length of the input is not a multiple of the block size, stored in big-endian. */ + uint8_t partial_block[SHA256_BLOCK_SIZE]; + /** @brief Bytes in the partial block. */ + union { + size_t value; + uint64_t alignment; + } bytes_in_partial_block; + /** @brief Bytes that have been hashed, not counting bytes stored in partial_block. */ + uint64_t bytes_hashed; + /** @brief padding to match the size of XmssHMsgShake256_256Ctx . */ + uint8_t _padding[200 + 8 - 32 - 64 - 8 - 8]; +} XmssHMsgSha256Ctx; +#endif -/** - * @brief - * Generate a digest of a message. - * - * @param[out] digest The output digest. - * @param[in] message Input message; may be NULL if and only if message_length is 0. - * @param[in] message_length Input message length in bytes. - */ -typedef void (*prototype_digest)(XmssValue256 *restrict digest, const uint8_t *restrict message, size_t message_length); +#if XMSS_ENABLE_SHAKE256_256 +/** @brief Context when we calculate SHAKE256_256 hashes with streaming data. */ +typedef struct XmssHMsgShake256_256Ctx { + /** @brief Keccak state array for calculating the SHAKE256_256 hash. */ + uint64_t shake256_256_state_array[KECCAK_STATE_ARRAY_SIZE / sizeof(uint64_t)]; + /** @brief Offset for the next absorb; always < 136, the block size of SHAKE256_256. */ + union { + uint_fast8_t value; + uint64_t alignment; + } offset; +} XmssHMsgShake256_256Ctx; +#endif -/** - * @brief - * Generate a digest of an array of 32-bit native words. - * - * @param[out] native_digest The output digest. - * @param[in] words Input data; may be NULL if and only if data_count is 0. - * @param[in] word_count The number of data words. - */ -typedef void (*prototype_native_digest)(XmssNativeValue256 *restrict native_digest, const uint32_t *restrict words, - size_t word_count); +#if XMSS_ENABLE_SHA256 && XMSS_ENABLE_SHAKE256_256 +/** @private */ +XMSS_STATIC_ASSERT(sizeof(XmssHMsgSha256Ctx) == sizeof(XmssHMsgShake256_256Ctx), + "XmssHMsgSha256Ctx and XmssHMsgShake256_256Ctx have different sizes."); +#endif + +/** @brief Context for calculating H_msg hashes from streaming data. */ +typedef union XmssHMsgCtx { +#if XMSS_ENABLE_SHA256 + /** @brief Context when we use SHA256. */ + XmssHMsgSha256Ctx sha256_ctx; +#endif +#if XMSS_ENABLE_SHAKE256_256 + /** @brief Context when we use SHAKE256_256. */ + XmssHMsgShake256_256Ctx shake256_256_ctx; +#endif + /** @brief Context pointer for hash implementations with the generic interface. */ + void *generic_ctx; +} XmssHMsgCtx; + +#if XMSS_ENABLE_HASH_ABSTRACTION /** * @brief @@ -586,7 +621,7 @@ typedef void (*prototype_native_digest)(XmssNativeValue256 *restrict native_dige * @param[out] native_digest The output of the XMSS F(KEY,M) hash function in native form. * @param[in] input The input parameters to the XMSS F(KEY,M) hash function as a single, optimized structure. */ -typedef void (*prototype_F)(XmssNativeValue256 *restrict native_digest, const Input_F *restrict input); +typedef void (*prototype_F)(XmssNativeValue256 *native_digest, const Input_F *input); /** * @brief @@ -598,24 +633,47 @@ typedef void (*prototype_F)(XmssNativeValue256 *restrict native_digest, const In * @param[out] native_digest The output of the XMSS H(KEY,M) hash function in native form. * @param[in] input The input parameters to the XMSS H(KEY,M) hash function as a single, optimized structure. */ -typedef void (*prototype_H)(XmssNativeValue256 *restrict native_digest, const Input_H *restrict input); +typedef void (*prototype_H)(XmssNativeValue256 *native_digest, const Input_H *input); /** * @brief - * Execute the XMSS H_msg(KEY,M) hash function. + * Initialize the context for calculating the XMSS H_msg(KEY,M) hash function. * * @details * KEY = r || Root || toByte(idx_sig, 32). * * The KEY parameters are provided in a single structure for performance reasons. * - * @param[out] native_digest The output of the XMSS H_msg(KEY,M) hash function in native form. + * @param[out] ctx Context holding the intermediate state of the hash calculation. * @param[in] input Input to the XMSS H_msg() hash function, excluding the message. - * @param[in] message Input message; may be NULL if and only if message_length is 0. - * @param[in] message_length Length of message in bytes. */ -typedef void (*prototype_H_msg)(XmssNativeValue256 *restrict native_digest, const Input_H_msg *restrict input, - const uint8_t *restrict message, size_t message_length); +typedef void (*prototype_H_msg_init)(XmssHMsgCtx *ctx, const Input_H_msg *input); + +/** + * @brief + * Update the calculation of the XMSS H_msg(KEY,M) hash function with the next part of M. + * + * @details + * When fault injection tolerance is required, provide a non-NULL `part_verify` parameter. After this function + * completes successfully, compare the value returned in `*part_verify` with the original message `part` pointer. + * + * @param[in,out] ctx Context holding the intermediate state of the hash calculation. + * @param[in] part Next part of the message; may be NULL if and only if part_length is 0. + * @param[in] part_length Length of part in bytes. + * @param[out] part_verify (optional, may be NULL) Outputs a copy of `part` to verify the correct message was + * processed. This can be used to mitigate fault injections. + */ +typedef void (*prototype_H_msg_update)(XmssHMsgCtx *ctx, const uint8_t *part, size_t part_length, + const uint8_t *volatile *part_verify); + +/** + * @brief + * Finalize the calculation of the XMSS H_msg(KEY,M) hash function and output the digest. + * + * @param[out] native_digest The output of the XMSS H_msg(KEY,M) hash function in native form. + * @param[in] ctx Context holding the intermediate state of the hash calculation. + */ +typedef void (*prototype_H_msg_finalize)(XmssNativeValue256 *native_digest, XmssHMsgCtx *ctx); /** * @brief @@ -629,7 +687,9 @@ typedef void (*prototype_H_msg)(XmssNativeValue256 *restrict native_digest, cons * @param[out] native_digest The output of the XMSS PRF(KEY,M) hash function in native form. * @param[in] input The input parameters to the XMSS PRF(KEY,M) hash function as a single, optimized structure. */ -typedef void (*prototype_PRF)(XmssNativeValue256 *restrict native_digest, const Input_PRF *restrict input); +typedef void (*prototype_PRF)(XmssNativeValue256 *native_digest, const Input_PRF *input); + +#if XMSS_ENABLE_SIGNING /** * @brief @@ -643,7 +703,7 @@ typedef void (*prototype_PRF)(XmssNativeValue256 *restrict native_digest, const * @param[out] native_digest The output of the XMSS PRFkeygen(KEY,M) hash function in native form. * @param[in] input The input parameters to the XMSS PRFkeygen(KEY,M) hash function as a single, optimized structure. */ -typedef void (*prototype_PRFkeygen)(XmssNativeValue256 *restrict native_digest, const Input_PRFkeygen *restrict input); +typedef void (*prototype_PRFkeygen)(XmssNativeValue256 *native_digest, const Input_PRFkeygen *input); /** * @brief @@ -663,7 +723,29 @@ typedef void (*prototype_PRFkeygen)(XmssNativeValue256 *restrict native_digest, * @param[in] input The input parameters to the index obfuscation PRFindex(KEY,M) hash function as a single, * optimized structure. */ -typedef void (*prototype_PRFindex)(XmssNativeValue256 *restrict native_digest, const Input_PRFindex *restrict input); +typedef void (*prototype_PRFindex)(XmssNativeValue256 *native_digest, const Input_PRFindex *input); + +/** + * @brief + * Generate a digest of a message. + * + * @param[out] digest The output digest. + * @param[in] message Input message; may be NULL if and only if message_length is 0. + * @param[in] message_length Input message length in bytes. + */ +typedef void (*prototype_digest)(XmssValue256 *digest, const uint8_t *message, size_t message_length); + +/** + * @brief + * Generate a digest of an array of 32-bit native words. + * + * @param[out] native_digest The output digest. + * @param[in] words Input data; may be NULL if and only if data_count is 0. + * @param[in] word_count The number of data words. + */ +typedef void (*prototype_native_digest)(XmssNativeValue256 *native_digest, const uint32_t *words, size_t word_count); + +#endif /* XMSS_ENABLE_SIGNING */ /** * @brief @@ -684,30 +766,40 @@ typedef void (*prototype_PRFindex)(XmssNativeValue256 *restrict native_digest, c * } key_context; * ``` */ -typedef struct { - /** @brief The standardized, generic, byte-oriented digest function. */ - prototype_digest digest; - - /** @brief The internal native 32-bit word-oriented digest function. */ - prototype_native_digest native_digest; - +typedef struct xmss_hashes { /** @brief The XMSS F() hash function. */ prototype_F F; /** @brief The XMSS H() hash function. */ prototype_H H; - /** @brief The XMSS H_msg() hash function. */ - prototype_H_msg H_msg; + /** @brief The XMSS H_msg_init() function. */ + prototype_H_msg_init H_msg_init; + + /** @brief The XMSS H_msg_update() function. */ + prototype_H_msg_update H_msg_update; + + /** @brief The XMSS H_msg_finalize() function. */ + prototype_H_msg_finalize H_msg_finalize; /** @brief The XMSS PRF() hash function. */ prototype_PRF PRF; +#if XMSS_ENABLE_SIGNING + /** @brief The XMSS PRFkeygen() hash function. */ prototype_PRFkeygen PRFkeygen; /** @brief The index obfuscation PRFindex() hash function. */ prototype_PRFindex PRFindex; + + /** @brief The standardized, generic, byte-oriented digest function. */ + prototype_digest digest; + + /** @brief The internal native 32-bit word-oriented digest function. */ + prototype_native_digest native_digest; + +#endif /* XMSS_ENABLE_SIGNING */ } xmss_hashes; #endif /* XMSS_ENABLE_HASH_ABSTRACTION */ diff --git a/src/xmss_ltree.c b/src/xmss_ltree.c new file mode 100644 index 0000000..257e950 --- /dev/null +++ b/src/xmss_ltree.c @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Pepijn Westen + */ + +/** + * @file + * @brief + * XMSS L-Tree hashing. + */ + +#include +#include +#include + +#include "xmss_ltree.h" + +#include "rand_hash.h" +#include "utils.h" + + +void xmss_ltree(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *const output, WotspPublicKey *const pk, ADRS *const adrs, + const XmssNativeValue256 *const seed) +{ + Input_PRF ltree_state = INIT_INPUT_PRF; + size_t len = XMSS_WOTSP_LEN; + + assert(output != NULL); + assert(pk != NULL); + assert(adrs != NULL); + assert(seed != NULL); + ASSERT_HASH_FUNCTIONS(); + + ltree_state.M.ADRS = *adrs; + ltree_state.KEY = *seed; + + /* Reset the tree height. */ + ltree_state.M.ADRS.typed.L_tree_Address.tree_height = 0; + + while (len > 1) { + for (uint32_t i = 0; i < len / 2; i++) { + ltree_state.M.ADRS.typed.L_tree_Address.tree_index = i; + rand_hash(HASH_FUNCTIONS &pk->hashes[i], <ree_state, &pk->hashes[2 * i], &pk->hashes[2 * i + 1]); + } + if (len % 2 == 1) { + /* Move the leftover node to the first position after the end of the first half of the array. */ + pk->hashes[len / 2] = pk->hashes[len - 1]; + len = len / 2 + 1; + } else { + len = len / 2; + } + ltree_state.M.ADRS.typed.L_tree_Address.tree_height += 1; + } + *output = pk->hashes[0]; +} diff --git a/src/xmss_ltree.h b/src/xmss_ltree.h new file mode 100644 index 0000000..870084f --- /dev/null +++ b/src/xmss_ltree.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Pepijn Westen + */ + +/** + * @file + * @brief + * XMSS L-Tree hashing. + */ + +#pragma once + +#ifndef XMSS_XMSS_LTREE_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_XMSS_LTREE_H_INCLUDED + +#include "libxmss.h" +#include "types.h" +#include "wotsp.h" +#include "xmss_hashes.h" + + +/** + * @brief + * Compresses WOTS+ public key. Implementation of L-Tree function from RFC 8391 section 4.1.5. + * + * @warning The caller is responsible for providing valid pointers. For performance reasons these will not be checked. + * @warning the compression is done in-place to conserve memory, so the public key is mangled by this function. + * + * @param[in] hash_functions The hash functions to use. + * @param[out] output The result of the L-tree computation. + * @param[in,out] pk The public key, which will be overwritten during compression. + * @param[in,out] adrs The adrs structure for this ltree operation. Address type must be set by caller. + * @param[in] seed The public seed in native-endian form. + */ +LIBXMSS_STATIC +void xmss_ltree(HASH_FUNCTIONS_PARAMETER XmssNativeValue256 *output, WotspPublicKey *pk, ADRS *adrs, + const XmssNativeValue256 *seed); + +#endif /* !XMSS_XMSS_LTREE_H_INCLUDED */ diff --git a/src/xmss_tree.c b/src/xmss_tree.c index be31d0a..851ebdc 100644 --- a/src/xmss_tree.c +++ b/src/xmss_tree.c @@ -17,11 +17,13 @@ #include "xmss_tree.h" #include "endianness.h" +#include "rand_hash.h" +#include "signing_private.h" #include "types.h" -#include "private.h" #include "utils.h" -#include "wotsp.h" +#include "wotsp_signing.h" #include "xmss_hashes.h" +#include "xmss_ltree.h" /** * @brief @@ -33,7 +35,7 @@ * @brief * A Stack structure for use in the TreeHash algorithm. */ -typedef struct { +typedef struct XmssTreeHashStack { /** * @brief * The number of items on the stack. @@ -64,83 +66,7 @@ typedef struct { .count = 0, \ } -void rand_hash(HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - XmssNativeValue256 *digest_out, Input_PRF *rand_hash_state, const XmssNativeValue256 *left, - const XmssNativeValue256 *right) -{ - Input_H input_h = INIT_INPUT_H; - - assert(digest_out != NULL); - assert(rand_hash_state != NULL); - assert(left != NULL); - assert(right != NULL); -#if XMSS_ENABLE_HASH_ABSTRACTION - assert(hash_functions != NULL); -#endif - - /* Compute KEY for H. */ - rand_hash_state->M.ADRS.typed.L_tree_Address.keyAndMask = 0; - xmss_PRF(HASH_ABSTRACTION(hash_functions) &input_h.KEY, rand_hash_state); - - /* Compute and apply BM_0 to LEFT input node. */ - rand_hash_state->M.ADRS.typed.L_tree_Address.keyAndMask = 1; - xmss_PRF(HASH_ABSTRACTION(hash_functions) &input_h.M[0], rand_hash_state); - for (size_t i = 0; i < XMSS_VALUE_256_WORDS; i++) { - input_h.M[0].data[i] ^= left->data[i]; - } - - /* Compute and apply BM_1 to RIGHT input node. */ - rand_hash_state->M.ADRS.typed.L_tree_Address.keyAndMask = 2; - xmss_PRF(HASH_ABSTRACTION(hash_functions) &input_h.M[1], rand_hash_state); - for (size_t i = 0; i < XMSS_VALUE_256_WORDS; i++) { - input_h.M[1].data[i] ^= right->data[i]; - } - - /* Compute output */ - xmss_H(HASH_ABSTRACTION(hash_functions) digest_out, &input_h); -} - -void -xmss_ltree(HASH_ABSTRACTION(const xmss_hashes *const restrict hash_functions) - XmssNativeValue256 *restrict output, WotspPublicKey *restrict pk, ADRS *restrict adrs, - const XmssNativeValue256 *restrict seed) -{ - Input_PRF ltree_state = INIT_INPUT_PRF; - size_t len = XMSS_WOTSP_LEN; - - assert(output != NULL); - assert(pk != NULL); - assert(adrs != NULL); - assert(seed != NULL); -#if XMSS_ENABLE_HASH_ABSTRACTION - assert(hash_functions != NULL); -#endif - - memcpy(<ree_state.M.ADRS, adrs, sizeof(ADRS)); - native_256_copy(<ree_state.KEY, seed); - - /* Reset the tree height. */ - ltree_state.M.ADRS.typed.L_tree_Address.tree_height = 0; - - while (len > 1) { - for (uint32_t i = 0; i < len / 2; i++) { - ltree_state.M.ADRS.typed.L_tree_Address.tree_index = i; - rand_hash(HASH_ABSTRACTION(hash_functions) &pk->hashes[i], <ree_state, &pk->hashes[2 * i], - &pk->hashes[2 * i + 1]); - } - if (len % 2 == 1) { - /* Move the leftover node to the first position after the end of the first half of the array. */ - native_256_copy(&(pk->hashes[len / 2]), &(pk->hashes[len - 1])); - len = len / 2 + 1; - } else { - len = len / 2; - } - ltree_state.M.ADRS.typed.L_tree_Address.tree_height += 1; - } - native_256_copy(output, &pk->hashes[0]); -} - -XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyContext *key_context, +XmssError xmss_tree_hash(XmssNativeValue256 *const output, const XmssKeyContext *const key_context, const XmssInternalCache *const cache_to_use, const uint32_t start_index, const uint32_t target_node_height) { XmssTreeHashStack stack = INIT_XMSS_TREE_HASH_STACK; @@ -169,7 +95,7 @@ XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyConte } /* Set the seed in the tree_hash_state. */ - native_256_copy(&tree_hash_state.KEY, &key_context->private_stateless.seed); + tree_hash_state.KEY = key_context->private_stateless.seed; for (uint32_t i = 0 ; i < (1u << target_node_height) ; i++) { /* The current node. This is the data storage for the leaf node at OTS address start_index + i. */ @@ -193,7 +119,7 @@ XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyConte XMSS_CACHE_ENTRY_OFFSET(cache_to_use->cache_type, cache_to_use->cache_level, key_context->context.parameter_set, adrs->typed.Hash_Tree_Address.tree_height, adrs->typed.Hash_Tree_Address.tree_index)]; - native_256_copy(&node, cached_digest); + node = *cached_digest; /* Increase i with the sub-tree width of the cached node, accounting for the end-of-loop increment of 1. */ i += (1u << adrs->typed.Hash_Tree_Address.tree_height) - 1u; } else { @@ -210,7 +136,7 @@ XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyConte /* Compress the public key using the ltree algorithm. */ adrs->type = ADRS_type_L_tree_Address; adrs->typed.L_tree_Address.L_tree_address = start_index + i; - xmss_ltree(HASH_ABSTRACTION(&key_context->context.hash_functions) &node, &pk, adrs, + xmss_ltree(HASH_FUNCTIONS_FROM(key_context->context) &node, &pk, adrs, &key_context->private_stateless.seed); /* Prepare the state struct for rand_hash computation. */ @@ -229,14 +155,14 @@ XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyConte */ adrs->typed.Hash_Tree_Address.tree_index = (adrs->typed.Hash_Tree_Address.tree_index - 1) / 2; stack.count -= 1; - rand_hash(HASH_ABSTRACTION(&key_context->context.hash_functions) &node, &tree_hash_state, - &stack.items[stack.count].digest, &node); + rand_hash(HASH_FUNCTIONS_FROM(key_context->context) &node, &tree_hash_state, + &stack.items[stack.count].digest, &node); adrs->typed.Hash_Tree_Address.tree_height += 1; } /* Push the node to the stack. */ assert(stack.count < XMSS_MAX_TREE_DEPTH - 1); - native_256_copy(&stack.items[stack.count].digest, &node); + stack.items[stack.count].digest = node; stack.items[stack.count].tree_height = adrs->typed.Hash_Tree_Address.tree_height; stack.count += 1; } @@ -245,12 +171,12 @@ XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyConte assert(stack.items[0].tree_height == target_node_height); /* Return the node on the stack. */ - native_256_copy(output, &stack.items[0].digest); + *output = stack.items[0].digest; return XMSS_OKAY; } -XmssError xmss_fill_top_cache(XmssInternalCache *cache, const XmssKeyContext *const restrict key_context, - uint32_t subtree_root_height, uint32_t subtree_root_index, uint32_t pre_cached_height) +XmssError xmss_fill_top_cache(XmssInternalCache *const cache, const XmssKeyContext *const key_context, + const uint32_t subtree_root_height, const uint32_t subtree_root_index, const uint32_t pre_cached_height) { /* The tree_hash_state input can largely be reused between the different tree_hash operations. * This structure contains both the SEED and the ADRS. @@ -272,7 +198,7 @@ XmssError xmss_fill_top_cache(XmssInternalCache *cache, const XmssKeyContext *co } /* Set the seed in the tree_hash_state. */ - native_256_copy(&tree_hash_state.KEY, &key_context->private_stateless.seed); + tree_hash_state.KEY = key_context->private_stateless.seed; /* Fill the sub-tree. */ for (uint32_t height = pre_cached_height; height < subtree_root_height; height++) { /* Tree height setting for RAND_HASH is tree height of the input nodes. */ @@ -286,8 +212,7 @@ XmssError xmss_fill_top_cache(XmssInternalCache *cache, const XmssKeyContext *co XmssNativeValue256 *out = &cache->cache[XMSS_CACHE_ENTRY_OFFSET(cache->cache_type, cache->cache_level, key_context->context.parameter_set, height + 1, index >> 1)]; adrs->typed.Hash_Tree_Address.tree_index = index >> 1; - rand_hash(HASH_ABSTRACTION(&key_context->context.hash_functions) - out, &tree_hash_state, left, right); + rand_hash(HASH_FUNCTIONS_FROM(key_context->context) out, &tree_hash_state, left, right); } } return XMSS_OKAY; diff --git a/src/xmss_tree.h b/src/xmss_tree.h index f9c1900..b8335f6 100644 --- a/src/xmss_tree.h +++ b/src/xmss_tree.h @@ -21,7 +21,8 @@ #include "config.h" -#include "private.h" +#include "libxmss.h" +#include "signing_private.h" #include "wotsp.h" /** @@ -47,8 +48,9 @@ * XMSS_TREE_DEPTH(param_set) represents the height of the root node of the tree. * @returns The root node of the tree. */ -XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyContext *key_context, - const XmssInternalCache *const cache_to_use, const uint32_t start_index, const uint32_t target_node_height); +LIBXMSS_STATIC +XmssError xmss_tree_hash(XmssNativeValue256 *output, const XmssKeyContext *key_context, + const XmssInternalCache *cache_to_use, uint32_t start_index, uint32_t target_node_height); /** * @brief @@ -68,39 +70,8 @@ XmssError xmss_tree_hash(XmssNativeValue256 *restrict output, const XmssKeyConte * @retval XMSS_ERR_INVALID_ARGUMENT subtree_root_height, subtree_root_index or pre_cached_height are at odds * with each other or with the cache metadata. */ -XmssError xmss_fill_top_cache(XmssInternalCache *cache, const XmssKeyContext *const restrict keygen_context, +LIBXMSS_STATIC +XmssError xmss_fill_top_cache(XmssInternalCache *cache, const XmssKeyContext *keygen_context, uint32_t subtree_root_height, uint32_t subtree_root_index, uint32_t pre_cached_height); -/** - * @brief - * Compresses WOTS+ public key. Implementation of L-Tree function from RFC 8391 section 4.1.5. - * - * @warning The caller is responsible for providing valid pointers. For performance reasons these will not be checked. - * @warning the compression is done in-place to conserve memory, so the public key is mangled by this function. - * - * @param[in] hash_functions The hash functions to use. - * @param[out] output The result of the L-tree computation. - * @param[in,out] pk The public key, which will be overwritten during compression. - * @param[in,out] adrs The adrs structure for this ltree operation. Address type must be set by caller. - * @param[in] seed The public seed in native-endian form. - */ -void xmss_ltree(HASH_ABSTRACTION(const xmss_hashes *restrict hash_functions) XmssNativeValue256 *restrict output, - WotspPublicKey *restrict pk, ADRS *restrict adrs, const XmssNativeValue256 *restrict seed); - -/** - * @brief - * Implementation of Randomized Tree Hashing (RFC 8391 section 4.1.4). - * - * @warning The caller is responsible for providing valid pointers. For performance reasons these will not be checked. - * - * @param[in] hash_functions The hash functions to use. - * @param[out] digest_out The randomized hash digest. - * @param[in,out] rand_hash_state Structure containing the SEED and the ADRS for this operation. - * The keyAndMask of the address is written by this function. - * @param [in] left The left input node. May alias with digest_out but not with right. - * @param [in] right The right input node. May alias with digest_out but not with left. - */ -void rand_hash(HASH_ABSTRACTION(const xmss_hashes *restrict hash_functions) XmssNativeValue256 *digest_out, - Input_PRF *rand_hash_state, const XmssNativeValue256 *left, const XmssNativeValue256 *right); - #endif /* !XMSS_XMSS_TREE_H_INCLUDED */ diff --git a/src/zeroize.c b/src/zeroize.c new file mode 100644 index 0000000..36ea48f --- /dev/null +++ b/src/zeroize.c @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * Securely purge memory. + */ + +#include +#include + +#include "config.h" + +#include "zeroize.h" + +/** + * @brief + * Volatile pointer to the standard C library memset() function. + */ +static void * (* volatile const memset_p)(void *, int, size_t) = memset; + +#if XMSS_CAN_USE_PRAGMA_OPTIMIZE +#pragma optimize("", off) +#endif + +#if XMSS_CAN_USE_PRAGMA_GCC_OPTIMIZE +#pragma GCC push_options +#pragma GCC optimize("O0") +#endif + +#if XMSS_CAN_USE_PRAGMA_CLANG_OPTIMIZE +#pragma clang optimize off +#endif +void xmss_zeroize(void * const ptr, const size_t size) +{ + /* + * We need to ensure that the memset call in this function cannot be optimized away. For GCC and Clang, we use + * pragmas to disable optimization for this function, because these compilers are our primary target and this + * approach can not be defeated by future improvements to their optimization techniques. + * + * To have a solution that works on almost all compilers, we access memset through a volatile pointer memset_p. + * Because it is volatile, the compiler is not allowed to assume that it still points to memset. + * + * It is still technically allowed to optimize the function call below to + * + * if (memset_p != memset) { + * memset_p(ptr, 0, size); + * } + * + * so the standard does not guarantee that this works. + * + * See also: + * https://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html + * https://www.daemonology.net/blog/2014-09-05-erratum.html + * https://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html + */ + memset_p(ptr, 0, size); +} + +#if XMSS_CAN_USE_PRAGMA_OPTIMIZE +#pragma optimize("", on) +#endif + +#if XMSS_CAN_USE_PRAGMA_GCC_OPTIMIZE +#pragma GCC pop_options +#endif + +#if XMSS_CAN_USE_PRAGMA_CLANG_OPTIMIZE +#pragma clang optimize off +#endif diff --git a/src/zeroize.h b/src/zeroize.h new file mode 100644 index 0000000..dc56b8a --- /dev/null +++ b/src/zeroize.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Max Fillinger + */ + +/** + * @file + * @brief + * Securely purge memory. + */ + +#pragma once + +#ifndef XMSS_ZEROIZE_H_INCLUDED +/** @private @brief Include guard. */ +#define XMSS_ZEROIZE_H_INCLUDED + +#include + +#include "libxmss.h" + +/** + * @copydoc XmssZeroizeFunction + * @see XmssZeroizeFunction + * + * @details + * In pure C99, there is no way to implement a zeroize function that cannot be optimized away. This implementation is a + * best-effort solution that is known to work on almost all compilers. + */ +LIBXMSS_STATIC +void xmss_zeroize(void * const ptr, const size_t size); + +#endif /* !XMSS_ZEROIZE_H_INCLUDED */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5406dae..a952954 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,10 +1,20 @@ # SPDX-FileCopyrightText: 2023 Fox Crypto B.V. # SPDX-License-Identifier: MIT +set(CMAKE_FOLDER tests) + # All our try_compile() checks use the exact same compile options as the library itself. get_directory_property(ACCUMULATED_COMPILE_OPTIONS COMPILE_OPTIONS) get_directory_property(ACCUMULATED_LINK_OPTIONS LINK_OPTIONS) +include_directories( + ../include + ../src + ${CMAKE_CURRENT_BINARY_DIR}/../include + ${CMAKE_CURRENT_BINARY_DIR}/../src +) +link_libraries(${XMSS_HASH_OVERRIDE_LINK_LIBRARIES}) + # Make the build type show up in the test report. add_library(test-build_type STATIC test-build_type.c @@ -18,20 +28,14 @@ get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG if(${GENERATOR_IS_MULTI_CONFIG}) # Multi-configuration build system (e.g., Microsoft Visual Studio). # Use a generator expression (does not work for NAME). - add_test(NAME "Build type" - COMMAND $,test-debug,test-release> - ) + add_test(NAME "Build type" COMMAND $,test-debug,test-release>) elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") # Definitely a debug build; test-debug must succeed (and test-release would fail). - add_test(NAME "Debug build" - COMMAND test-debug - ) + add_test(NAME "Debug build" COMMAND test-debug) else() # Any other build type: Release, MinSizeRel, etc. # In any case: some release build; test-release must succeed (and test-debug would fail). - add_test(NAME "Release build" - COMMAND test-release - ) + add_test(NAME "Release build" COMMAND test-release) endif() @@ -70,133 +74,111 @@ if(XMSS_CAN_USE_STATIC_ASSERT OR XMSS_CAN_USE_EXTENSION_STATIC_ASSERT) PRIVATE ${PROJECT_SOURCE_DIR}/include PRIVATE ${PROJECT_BINARY_DIR}/include ) - add_test(NAME "Static asserts enabled" - COMMAND test-static_assert - ) + add_test(NAME "Static asserts enabled" COMMAND test-static_assert) endif() -add_executable(test-structures test-structures.c) -target_include_directories(test-structures - PRIVATE ${PROJECT_BINARY_DIR}/include - PRIVATE ${PROJECT_SOURCE_DIR}/include - PRIVATE ${PROJECT_BINARY_DIR}/src - PRIVATE ${PROJECT_SOURCE_DIR}/src -) -add_test(NAME "Structure layout" - COMMAND test-structures -) - -add_executable(test-xmss_zeroize test-xmss_zeroize.c) -target_link_libraries(test-xmss_zeroize PRIVATE utils) -add_test(NAME "Secure zeroize" - COMMAND test-xmss_zeroize -) - add_executable(test-endianness test-endianness.c) -target_link_libraries(test-endianness PRIVATE xmss_hashes) -add_test(NAME "Endianness conversions" - COMMAND test-endianness -) +add_test(NAME "Endianness conversions" COMMAND test-endianness) -add_library(nist-test-vectors STATIC +add_library(nist-test-vectors OBJECT nist-test-vectors.c nist-test-vectors.h ) -target_link_libraries(nist-test-vectors PUBLIC xmss_headers) - -add_library(test-xmss_hashes STATIC - test-xmss_hashes.c - test-xmss_hashes.h -) -target_link_libraries(test-xmss_hashes PUBLIC xmss_hashes) - -add_executable(test-xmss_context_initialize test-xmss_context_initialize.c) -target_link_libraries(test-xmss_context_initialize xmss_signing xmss_hashes) -add_test(NAME "xmss_context_initialize" - COMMAND test-xmss_context_initialize -) -add_executable(test-chain test-chain.c) -target_link_libraries(test-chain PRIVATE xmss_hashes) -add_test(NAME "Chain function" - COMMAND test-chain -) - -add_executable(test-rand_hash test-rand_hash.c) -target_link_libraries(test-rand_hash PRIVATE xmss_hashes wotsp) -add_test(NAME "rand_hash function" - COMMAND test-rand_hash -) - -add_executable(test-ltree test-ltree.c) -target_link_libraries(test-ltree PRIVATE xmss_hashes wotsp) -add_test(NAME "ltree function" - COMMAND test-ltree -) - - -add_executable(test-xmss_tree_hash test-xmss_tree_hash.c) -target_link_libraries(test-xmss_tree_hash xmss_signing xmss_hashes) -add_test(NAME "xmss_tree_hash" - COMMAND test-xmss_tree_hash -) - -add_executable(test-xmss_generate_public_key test-xmss_generate_public_key.c) -target_link_libraries(test-xmss_generate_public_key xmss_signing xmss_hashes) -add_test(NAME "xmss_generate_public_key" - COMMAND test-xmss_generate_public_key -) - -add_executable(test-xmss_generate_private_key test-xmss_generate_private_key.c) -target_link_libraries(test-xmss_generate_private_key xmss_signing xmss_hashes) -add_test(NAME "xmss_generate_private_key" - COMMAND test-xmss_generate_private_key -) - -add_executable(test-wotsp test-wotsp.c) -target_link_libraries(test-wotsp wotsp xmss_hashes xmss_signing) -add_test(NAME "Test WOTS+" COMMAND test-wotsp) - -add_executable(test-blob-confusion test-blob-confusion.c) -target_include_directories(test-blob-confusion - PRIVATE ${PROJECT_BINARY_DIR}/src -) -target_link_libraries(test-blob-confusion xmss_signing) -add_test(NAME "Test key blob sizes" COMMAND test-blob-confusion) if(XMSS_ENABLE_SHA256) # Test the generic sha256_digest() hash function. - add_executable(sha256-kat sha256-kat.c) - target_link_libraries(sha256-kat nist-test-vectors xmss_hashes) + add_executable(sha256-kat sha256-kat.c ${XMSS_HASH_OVERRIDE_SOURCES}) + target_link_libraries(sha256-kat nist-test-vectors) add_test(NAME "SHA-256 Known Answer Tests (KATs)" COMMAND sha256-kat WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external-test-vectors/shabytetestvectors ) # Test the XMSS-specialized (F, H, Hmsg, PRF, and PRFkeygen) SHA-256 hash functions. - add_executable(test-sha256_xmss_hashes test-sha256_xmss_hashes.c) - target_link_libraries(test-sha256_xmss_hashes test-xmss_hashes) - add_test(NAME "SHA-256 XMSS Hash Functions" - COMMAND test-sha256_xmss_hashes - ) + add_executable(test-sha256_xmss_hashes test-sha256_xmss_hashes.c ${XMSS_HASH_OVERRIDE_SOURCES}) + add_test(NAME "SHA-256 XMSS Hash Functions" COMMAND test-sha256_xmss_hashes) endif() if(XMSS_ENABLE_SHAKE256_256) # Test the generic shake256_256_digest() hash function. - add_executable(shake256_256-kat shake256_256-kat.c) - target_link_libraries(shake256_256-kat nist-test-vectors xmss_hashes) + add_executable(shake256_256-kat shake256_256-kat.c ${XMSS_HASH_OVERRIDE_SOURCES}) + target_link_libraries(shake256_256-kat nist-test-vectors) add_test(NAME "SHAKE256/256 Known Answer Tests (KATs)" COMMAND shake256_256-kat WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/external-test-vectors/shakebytetestvectors ) # Test the XMSS-specialized (F, H, Hmsg, PRF, and PRFkeygen) SHAKE256/256 hash functions. - add_executable(test-shake256_256_xmss_hashes test-shake256_256_xmss_hashes.c) - target_link_libraries(test-shake256_256_xmss_hashes test-xmss_hashes) - add_test(NAME "SHAKE256/256 XMSS Hash Functions" - COMMAND test-shake256_256_xmss_hashes + add_executable(test-shake256_256_xmss_hashes test-shake256_256_xmss_hashes.c ${XMSS_HASH_OVERRIDE_SOURCES}) + add_test(NAME "SHAKE256/256 XMSS Hash Functions" COMMAND test-shake256_256_xmss_hashes) + +endif() + +add_executable(test-chain test-chain.c ${XMSS_HASH_OVERRIDE_SOURCES}) +add_test(NAME "Chain function" COMMAND test-chain) + +add_executable(test-rand_hash test-rand_hash.c ${XMSS_HASH_OVERRIDE_SOURCES}) +add_test(NAME "rand_hash function" COMMAND test-rand_hash) + +add_executable(test-ltree test-ltree.c ${XMSS_HASH_OVERRIDE_SOURCES}) +add_test(NAME "ltree function" COMMAND test-ltree) + +if(XMSS_ENABLE_SIGNING) + + add_executable(test-structures test-structures.c) + add_test(NAME "Structure layout" COMMAND test-structures) + + add_executable(test-blob-confusion test-blob-confusion.c) + add_test(NAME "Test key blob sizes" COMMAND test-blob-confusion) + + add_executable(test-xmss_zeroize test-xmss_zeroize.c) + add_test(NAME "Secure zeroize" COMMAND test-xmss_zeroize) + + add_executable(test-wotsp test-wotsp.c ${XMSS_HASH_OVERRIDE_SOURCES}) + add_test(NAME "Test WOTS+" COMMAND test-wotsp) + + add_executable(test-xmss_tree_hash test-xmss_tree_hash.c ${XMSS_HASH_OVERRIDE_SOURCES}) + add_test(NAME "xmss_tree_hash" COMMAND test-xmss_tree_hash) + + add_executable(test-xmss_context_initialize test-xmss_context_initialize.c ${XMSS_HASH_OVERRIDE_SOURCES}) + add_test(NAME "xmss_context_initialize" COMMAND test-xmss_context_initialize) + + add_executable(test-xmss_generate_private_key test-xmss_generate_private_key.c ${XMSS_HASH_OVERRIDE_SOURCES}) + target_link_libraries(test-xmss_generate_private_key xmss) + add_test(NAME "xmss_generate_private_key" + COMMAND test-xmss_generate_private_key + WORKING_DIRECTORY $ + ) + + add_executable(test-xmss_sign_verify test-xmss_sign_verify.c ${XMSS_HASH_OVERRIDE_SOURCES}) + target_link_libraries(test-xmss_sign_verify xmss) + add_test(NAME "xmss_sign_verify" + COMMAND test-xmss_sign_verify + WORKING_DIRECTORY $ + ) + + add_executable(test-xmss_generate_public_key test-xmss_generate_public_key.c ${XMSS_HASH_OVERRIDE_SOURCES}) + target_link_libraries(test-xmss_generate_public_key xmss) + add_test(NAME "xmss_generate_public_key" + COMMAND test-xmss_generate_public_key + WORKING_DIRECTORY $ ) endif() + +add_executable(test-version test-version.c) +target_link_libraries(test-version xmss) +add_test(NAME "Library version" + COMMAND test-version + WORKING_DIRECTORY $ +) + +add_executable(test-completeness test-completeness.c) +target_link_libraries(test-completeness xmss) +add_test(NAME "Library completeness" + COMMAND test-completeness + WORKING_DIRECTORY $ +) diff --git a/tests/cmake/test_static_assert.c b/tests/cmake/test_static_assert.c index 8f2ff8c..28dfcbc 100644 --- a/tests/cmake/test_static_assert.c +++ b/tests/cmake/test_static_assert.c @@ -18,4 +18,4 @@ int main(void) * Compiling this with ASSERT_VALUE=1 must always succeed, even when static asserts are not available. * Compiling this with ASSERT_VALUE=0 must fail if and only if CMake detected that static asserts are available. */ -STATIC_ASSERT(ASSERT_VALUE, "This assertion is part of a test. This is not a bug."); +XMSS_STATIC_ASSERT(ASSERT_VALUE, "This assertion is part of a test. This is not a bug."); diff --git a/tests/nist-test-vectors.c b/tests/nist-test-vectors.c index dde364a..f2e2fb1 100644 --- a/tests/nist-test-vectors.c +++ b/tests/nist-test-vectors.c @@ -54,7 +54,7 @@ bool decode_hex(uint8_t *dst, size_t dst_size, const char *src) src += 3; char buffer[3] = { 0 }; for (; dst_size > 0; src += 2, ++dst, --dst_size) { - if (!isxdigit(src[0]) || !isxdigit(src[1])) { + if (!isxdigit((int)src[0]) || !isxdigit((int)src[1])) { return false; } buffer[0] = src[0]; diff --git a/tests/reference-digest.inc b/tests/reference-digest.inc new file mode 100644 index 0000000..c53a53b --- /dev/null +++ b/tests/reference-digest.inc @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/* + +We need to test various xmss hash functions (F, H, H_msg, etc.), for which we need a reference digest function. +We also need to test the underlying primitives (e.g., xmss_sha256_process_block) against the NIST KATs, + for which we (again) need a reference digest function. +Here we construct such a reference digest function from the underlying primitives, such that it can be used +for both. Note that this is very similar (if not identical) to the generic XMSS digest function of the library, +except that the latter is not defined when compiling with XMSS_ENABLE_SIGNING=OFF. The version here is always +available. + +*/ + +#include "types.h" + +#if defined(REFERENCE_DIGEST_SHA256) && !defined(REFERENCE_DIGEST_SHAKE256_256) +# if XMSS_ENABLE_SHA256_GENERIC +# include "override_sha256_generic.h" +# else +# include "sha256_internal_xmss_hashes.h" +# endif +#elif defined(REFERENCE_DIGEST_SHAKE256_256) && !defined(REFERENCE_DIGEST_SHA256) +# if XMSS_ENABLE_SHAKE256_256_GENERIC +# include "override_shake256_256_generic.h" +# else +# include "override_shake256_256_internal.h" +# endif +#else +# error Either REFERENCE_DIGEST_SHA256 or REFERENCE_DIGEST_SHAKE256_256 must be defined. +#endif + + +static inline void reference_digest(XmssValue256 *const digest, const uint8_t *const message, + const size_t message_length) +{ +#if defined(REFERENCE_DIGEST_SHA256) +# if XMSS_ENABLE_SHA256_GENERIC + + // Produce a generic digest from the generic primitives. + void *context = xmss_sha256_init(); + xmss_sha256_update(context, message, message_length); + xmss_sha256_finalize(context, digest); + +# else + + // Produce a generic digest from the internal primitives. + XmssNativeValue256 native_digest; + native_digest = sha256_H0; + sha256_process_message_final(&native_digest, message, message_length, 0); + native_to_big_endian_256(digest, &native_digest); + +# endif +#elif defined(REFERENCE_DIGEST_SHAKE256_256) +# if XMSS_ENABLE_SHAKE256_256_GENERIC + + // Produce a generic digest from the generic primitives. + void *context = xmss_shake256_256_init(); + xmss_shake256_256_update(context, message, message_length); + xmss_shake256_256_finalize(context, digest); + +# else + + // Produce a generic digest from the internal primitives. + uint64_t A[200] = { 0 }; + shake256_256_process_message_final(A, 0, message, message_length); + XmssNativeValue256 native_digest; + // Note that xmss_sponge_squeeze() also is unavailable for XMSS_ENABLE_SIGNING=OFF + xmss_sponge_squeeze_native(&native_digest, A); + native_to_big_endian_256(digest, &native_digest); + +# endif +#else +# error Either REFERENCE_DIGEST_SHA256 or REFERENCE_DIGEST_SHAKE256_256 must be defined. +#endif +} diff --git a/tests/sha256-kat.c b/tests/sha256-kat.c index 71d29bc..cf93cba 100644 --- a/tests/sha256-kat.c +++ b/tests/sha256-kat.c @@ -5,6 +5,15 @@ * SPDX-FileContributor: Frans van Dorsselaer */ +#include "libxmss.c" + +#if XMSS_ENABLE_SIGNING +# define reference_digest sha256_digest +#else +# define REFERENCE_DIGEST_SHA256 +# include "reference-digest.inc" +#endif + #include #include #include @@ -12,11 +21,10 @@ #include #include "nist-test-vectors.h" -#include "sha256_xmss_hashes.h" void on_test_vector(XmssValue256 *digest, const uint8_t *message, size_t message_length) { - sha256_digest(digest, message, message_length); + reference_digest(digest, message, message_length); } static bool monte_carlo_test(const char *const filename) @@ -67,7 +75,7 @@ static bool monte_carlo_test(const char *const filename) for (int j = 3; j <= 1002; ++j) { /* Calculate MD[j]. */ - sha256_digest(&digest, message, 3 * sizeof(XmssValue256)); + reference_digest(&digest, message, 3 * sizeof(XmssValue256)); /* Rotate message: MD[j-3] || MD[j-2] || MD[j-1] -> MD[j-2] || MD[j-1] || MD[j]. */ memmove(message, message + sizeof(XmssValue256), 2 * sizeof(XmssValue256)); memcpy(message + 2 * sizeof(XmssValue256), &digest, sizeof(XmssValue256)); diff --git a/tests/shake256_256-kat.c b/tests/shake256_256-kat.c index 1080c33..70a1818 100644 --- a/tests/shake256_256-kat.c +++ b/tests/shake256_256-kat.c @@ -5,17 +5,26 @@ * SPDX-FileContributor: Frans van Dorsselaer */ +#include "libxmss.c" + +#if XMSS_ENABLE_SIGNING +# define reference_digest shake256_256_digest +#else +# define REFERENCE_DIGEST_SHAKE256_256 +# include "reference-digest.inc" +#endif + #include #include #include #include #include "nist-test-vectors.h" -#include "shake256_256_xmss_hashes.h" + void on_test_vector(XmssValue256 *digest, const uint8_t *message, size_t message_length) { - shake256_256_digest(digest, message, message_length); + reference_digest(digest, message, message_length); } int main(void) diff --git a/tests/test-aarch64-override_sha256_internal.c b/tests/test-aarch64-override_sha256_internal.c new file mode 100644 index 0000000..d96da7e --- /dev/null +++ b/tests/test-aarch64-override_sha256_internal.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +/** + * @file + * @brief + * ARMv8 Cryptography Extensions implementation of SHA-256. + */ + +#include + +#include "override_sha256_internal.h" + + +/** + * @brief + * The constant values K[t] to be used for the iteration t of the hash computation. + * + * @details + * See NIST FIPS 180-4, Section 4.2.2. + * + * These values represent the first thirty-two bits of the fractional parts of the cube roots of the first + * sixty-four prime numbers. + */ +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + + +void xmss_sha256_process_block(XmssNativeValue256 *const Hi, const uint32_t *const Mi) +{ + /* + * See: NIST FIPS 180-4, Section 6.2.2. + * + * This is the part inside the outer loop. + */ + + /* For performance reasons, some local variables are defined uninitialized and are not explicitly set to 0. */ + + /* The ARM intrinsics operate on SIMD vectors (uint32x4_t) instead of plain uint32_t, so we need to load/store. */ + uint32x4_t Hi_SIMD[2] = { vld1q_u32(Hi->data), vld1q_u32(Hi->data + 4) }; + + /* The ARM intrinsics handle the message 4 words at a time, so t = [0..15] instead of [0..63]. */ + + /* 1. Prepare the message schedule, {Wt} */ + uint32x4_t W[16]; + for (uint_fast8_t t = 0; t <= 3; ++t) { + W[t] = vld1q_u32(Mi + t * 4); + } + for (uint_fast8_t t = 4; t <= 15; ++t) { + W[t] = vsha256su1q_u32(vsha256su0q_u32(W[t - 4], W[t - 3]), W[t - 2], W[t - 1]); + } + + /* 2. Initialize the working variables */ + uint32x4_t abcd = Hi_SIMD[0]; + uint32x4_t efgh = Hi_SIMD[1]; + + /* 3. */ + for (uint_fast8_t t = 0; t <= 15; ++t) { + uint32x4_t round_input = vaddq_u32(W[t], vld1q_u32(K + t * 4)); + uint32x4_t tmp_abcd = vsha256hq_u32(abcd, efgh, round_input); + efgh = vsha256h2q_u32(efgh, abcd, round_input); + abcd = tmp_abcd; + } + + /* 4. Compute the next iteration of the intermediate hash value */ + vst1q_u32(Hi->data, vaddq_u32(Hi_SIMD[0], abcd)); + vst1q_u32(Hi->data + 4, vaddq_u32(Hi_SIMD[1], efgh)); +} diff --git a/tests/test-blob-confusion.c b/tests/test-blob-confusion.c index 328c8d1..c822542 100644 --- a/tests/test-blob-confusion.c +++ b/tests/test-blob-confusion.c @@ -24,7 +24,7 @@ #include #include -#include "../src/private.h" +#include "signing_private.h" int main(void) { /* Prevent compilers from complaining about comparing constants. */ diff --git a/tests/test-chain.c b/tests/test-chain.c index 5f816a3..f06d187 100644 --- a/tests/test-chain.c +++ b/tests/test-chain.c @@ -5,31 +5,23 @@ * SPDX-FileContributor: Max Fillinger */ +#include "libxmss.c" + +#include #include #include #include #include -#include "config.h" -#include "wotsp.c" - -#include "xmss_hashes.h" -#if XMSS_ENABLE_SHA256 -#include "sha256_xmss_hashes.h" -#endif -#if XMSS_ENABLE_SHAKE256_256 -#include "shake256_256_xmss_hashes.h" -#endif - static void print_output(const XmssNativeValue256 *output, const XmssNativeValue256 *expected_output) { printf("Output: "); for (int i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("0x%08x ", output->data[i]); + printf("0x%08"PRIx32" ", output->data[i]); } printf("\nExpected: "); for (int i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("0x%08x ", expected_output->data[i]); + printf("0x%08"PRIx32" ", expected_output->data[i]); } printf("\n"); } @@ -43,7 +35,9 @@ static bool test_chain_with_sha256(void) Input_PRF input_prf = INIT_INPUT_PRF; prepare_input_prf_for_chain(&input_prf, &prf_seed, 0); - chain(HASH_ABSTRACTION(&sha256_xmss_hashes) &output, &input_prf, &input, 2, 3); + DEFINE_HASH_FUNCTIONS; + INITIALIZE_HASH_FUNCTIONS(XMSS_PARAM_SHA2_10_256); + chain(HASH_FUNCTIONS &output, &input_prf, &input, 2, 3); /* Expected output calculated with Frans' C# implementation: github.com/dorssel/dotnet-xmss. */ const XmssNativeValue256 expected_output = { { @@ -69,7 +63,9 @@ static bool test_chain_with_shake256_256(void) Input_PRF input_prf = INIT_INPUT_PRF; prepare_input_prf_for_chain(&input_prf, &prf_seed, 0); - chain(HASH_ABSTRACTION(&shake256_256_xmss_hashes) &output, &input_prf, &input, 2, 3); + DEFINE_HASH_FUNCTIONS; + INITIALIZE_HASH_FUNCTIONS(XMSS_PARAM_SHAKE256_10_256); + chain(HASH_FUNCTIONS &output, &input_prf, &input, 2, 3); /* Expected output calculated with Frans' C# implementation: github.com/dorssel/dotnet-xmss. */ const XmssNativeValue256 expected_output = { { @@ -86,7 +82,7 @@ static bool test_chain_with_shake256_256(void) } #endif /* XMSS_ENABLE_SHAKE256_256 */ -int main() +int main(void) { bool success = true; #if XMSS_ENABLE_SHA256 diff --git a/tests/test-completeness.c b/tests/test-completeness.c new file mode 100644 index 0000000..9e5edbb --- /dev/null +++ b/tests/test-completeness.c @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +#include + +#include "errors.h" +#include "verification.h" +#include "version.h" +#if XMSS_ENABLE_SIGNING +# include "signing.h" +#endif + + +int main(void) +{ + (void)xmss_error_to_description(XMSS_OKAY); + (void)xmss_error_to_name(XMSS_OKAY); + (void)xmss_library_get_version(); + (void)xmss_verification_check(NULL, NULL); + (void)xmss_verification_init(NULL, NULL, NULL, 0); + (void)xmss_verification_update(NULL, NULL, 0, NULL); + +#if XMSS_ENABLE_SIGNING + (void)xmss_calculate_public_key_part(NULL, 0); + (void)xmss_context_initialize(NULL, XMSS_PARAM_SHA2_10_256, NULL, NULL, NULL); + (void)xmss_export_public_key(NULL, NULL); + (void)xmss_finish_calculate_public_key(NULL, NULL, NULL); + (void)xmss_free_key_context(NULL); + (void)xmss_free_key_generation_context(NULL); + (void)xmss_free_signing_context(NULL); + (void)xmss_generate_private_key(NULL, NULL, NULL, NULL, XMSS_INDEX_OBFUSCATION_OFF, NULL, NULL); + (void)xmss_generate_public_key(NULL, NULL, NULL, NULL,XMSS_CACHE_NONE, 0, 0); + (void)xmss_get_caching_in_public_key(NULL, NULL, NULL); + (void)xmss_get_signature_count(NULL, NULL, NULL); + (void)xmss_load_private_key(NULL, NULL, NULL, NULL); + (void)xmss_load_public_key(NULL, NULL, NULL); + (void)xmss_merge_signature_space(NULL, NULL, NULL); + (void)xmss_partition_signature_space(NULL, NULL, NULL, 0); + (void)xmss_request_future_signatures(NULL, NULL, 0); + (void)xmss_sign_message(NULL, NULL, NULL); + (void)xmss_verify_exported_public_key(NULL, NULL); + (void)xmss_verify_public_key(NULL, NULL, NULL); + (void)xmss_verify_private_key_stateless(NULL, NULL); + (void)xmss_verify_private_key_stateful(NULL, NULL, NULL, NULL); +#endif + + return EXIT_SUCCESS; +} diff --git a/tests/test-endianness.c b/tests/test-endianness.c index d91d90a..cc348de 100644 --- a/tests/test-endianness.c +++ b/tests/test-endianness.c @@ -48,7 +48,7 @@ static bool test_big_endian_to_native_count(unsigned int count) return true; } -static bool test_big_endian_to_native() +static bool test_big_endian_to_native(void) { bool success = true; @@ -66,7 +66,7 @@ static bool test_inplace_big_endian_to_native_count(uint_fast16_t count) { /* 1 more than the test data to check for overruns */ uint32_t buf[TEST_COUNT] = { 0 }; - memcpy(buf, test_big_endian_data, count * 4); + memcpy(buf, test_big_endian_data, (size_t)count * 4); inplace_big_endian_to_native(buf, count); /* verify that the conversion is correct */ @@ -78,7 +78,7 @@ static bool test_inplace_big_endian_to_native_count(uint_fast16_t count) return true; } -static bool test_inplace_big_endian_to_native() +static bool test_inplace_big_endian_to_native(void) { bool success = true; @@ -113,7 +113,7 @@ static bool test_native_to_big_endian_count(unsigned int count) return true; } -static bool test_native_to_big_endian() +static bool test_native_to_big_endian(void) { bool success = true; diff --git a/tests/test-ltree.c b/tests/test-ltree.c index 8d9edf8..b2de86a 100644 --- a/tests/test-ltree.c +++ b/tests/test-ltree.c @@ -6,31 +6,24 @@ * SPDX-FileContributor: Pepijn Westen */ +#include "libxmss.c" + +#include #include #include #include #include -#include "config.h" -#include "xmss_tree.c" - -#include "xmss_hashes.h" -#if XMSS_ENABLE_SHA256 -#include "sha256_xmss_hashes.h" -#endif -#if XMSS_ENABLE_SHAKE256_256 -#include "shake256_256_xmss_hashes.h" -#endif static void print_output(const XmssNativeValue256 *output, const XmssNativeValue256 *expected_output) { printf("Output: "); for (int i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("0x%08x ", output->data[i]); + printf("0x%08"PRIx32" ", output->data[i]); } printf("\nExpected: "); for (int i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("0x%08x ", expected_output->data[i]); + printf("0x%08"PRIx32" ", expected_output->data[i]); } printf("\n"); } @@ -120,7 +113,9 @@ static bool test_rand_hash_with_sha256(void) XmssNativeValue256 output = {0}; - xmss_ltree(HASH_ABSTRACTION(&sha256_xmss_hashes) &output, &public_key, (ADRS*)adrs, &seed); + DEFINE_HASH_FUNCTIONS; + INITIALIZE_HASH_FUNCTIONS(XMSS_PARAM_SHA2_10_256); + xmss_ltree(HASH_FUNCTIONS &output, &public_key, (ADRS*)adrs, &seed); const bool success = memcmp(&output, &expected_output, sizeof(XmssNativeValue256)) == 0; printf("rand hash function test %s for SHA256!\n", success ? "succeeded" : "failed"); @@ -217,7 +212,9 @@ static bool test_rand_hash_with_shake256_256(void) XmssNativeValue256 output = {0}; - xmss_ltree(HASH_ABSTRACTION(&shake256_256_xmss_hashes) &output, &public_key, (ADRS*)adrs, &seed); + DEFINE_HASH_FUNCTIONS; + INITIALIZE_HASH_FUNCTIONS(XMSS_PARAM_SHAKE256_10_256); + xmss_ltree(HASH_FUNCTIONS &output, &public_key, (ADRS*)adrs, &seed); const bool success = memcmp(&output, &expected_output, sizeof(XmssNativeValue256)) == 0; printf("rand hash function test %s for SHAKE256_256!\n", success ? "succeeded" : "failed"); @@ -230,7 +227,7 @@ static bool test_rand_hash_with_shake256_256(void) #endif /* XMSS_ENABLE_SHAKE256_256 */ -int main() +int main(void) { bool success = true; #if XMSS_ENABLE_SHA256 diff --git a/tests/test-override_sha256_generic.c b/tests/test-override_sha256_generic.c index b94db5f..e9ae9cf 100644 --- a/tests/test-override_sha256_generic.c +++ b/tests/test-override_sha256_generic.c @@ -11,22 +11,22 @@ #include "override_sha256_generic.h" -void *sha256_init(void) +void *xmss_sha256_init(void) { - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + EVP_MD_CTX *const ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); return ctx; } -void sha256_update(void *context, const uint8_t *restrict data, size_t data_length) +void xmss_sha256_update(void *const context, const uint8_t *const data, const size_t data_length) { - EVP_MD_CTX *restrict ctx = (EVP_MD_CTX *)context; + EVP_MD_CTX *const ctx = (EVP_MD_CTX *)context; EVP_DigestUpdate(ctx, data, data_length); } -void sha256_finalize(void *context, XmssValue256 *restrict digest) +void xmss_sha256_finalize(void *const context, XmssValue256 *const digest) { - EVP_MD_CTX *restrict ctx = (EVP_MD_CTX *restrict)context; + EVP_MD_CTX *const ctx = (EVP_MD_CTX *)context; EVP_DigestFinal(ctx, digest->data, NULL); EVP_MD_CTX_free(ctx); } diff --git a/tests/test-override_sha256_internal.c b/tests/test-override_sha256_internal.c new file mode 100644 index 0000000..26dd535 --- /dev/null +++ b/tests/test-override_sha256_internal.c @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +#include +#include +#include + +#define OPENSSL_API_COMPAT 10101 +#include + +#include "override_sha256_internal.h" + + +void xmss_sha256_process_block(XmssNativeValue256 *const Hi, const uint32_t *const Mi) +{ + uint32_t block[16]; + for (int i = 0; i < 16; ++i) { + block[i] = htonl(Mi[i]); + } + + SHA256_CTX sha256; + SHA256_Init(&sha256); + memcpy(sha256.h, Hi, 32); + SHA256_Update(&sha256, block, sizeof(block)); + memcpy(Hi, sha256.h, 32); +} diff --git a/tests/test-override_shake256_256_generic.c b/tests/test-override_shake256_256_generic.c index ccbd326..d38f012 100644 --- a/tests/test-override_shake256_256_generic.c +++ b/tests/test-override_shake256_256_generic.c @@ -13,7 +13,7 @@ #include "override_shake256_256_generic.h" -void *shake256_256_init(void) +void *xmss_shake256_256_init(void) { static unsigned int xoflen = 32; static const OSSL_PARAM params[2] = { @@ -25,21 +25,21 @@ void *shake256_256_init(void) }, { 0 } }; - EVP_MD_CTX *restrict ctx = EVP_MD_CTX_new(); + EVP_MD_CTX *const ctx = EVP_MD_CTX_new(); EVP_DigestInit_ex(ctx, EVP_shake256(), NULL); EVP_MD_CTX_set_params(ctx, params); return ctx; } -void shake256_256_update(void *context, const uint8_t *restrict data, size_t data_length) +void xmss_shake256_256_update(void *context, const uint8_t *const data, const size_t data_length) { - EVP_MD_CTX *restrict ctx = (EVP_MD_CTX *restrict)context; + EVP_MD_CTX *const ctx = (EVP_MD_CTX *)context; EVP_DigestUpdate(ctx, data, data_length); } -void shake256_256_finalize(void *context, XmssValue256 *restrict digest) +void xmss_shake256_256_finalize(void *const context, XmssValue256 *const digest) { - EVP_MD_CTX *restrict ctx = (EVP_MD_CTX *restrict)context; + EVP_MD_CTX *const ctx = (EVP_MD_CTX *)context; EVP_DigestFinal(ctx, digest->data, NULL); EVP_MD_CTX_free(ctx); } diff --git a/tests/test-rand_hash.c b/tests/test-rand_hash.c index 035de32..9fe8046 100644 --- a/tests/test-rand_hash.c +++ b/tests/test-rand_hash.c @@ -6,32 +6,24 @@ * SPDX-FileContributor: Pepijn Westen */ +#include "libxmss.c" + +#include #include #include #include #include -#include "config.h" -#include "xmss_tree.c" - -#include "utils.c" -#include "xmss_hashes.h" -#if XMSS_ENABLE_SHA256 -#include "sha256_xmss_hashes.h" -#endif -#if XMSS_ENABLE_SHAKE256_256 -#include "shake256_256_xmss_hashes.h" -#endif static void print_output(const XmssNativeValue256 *output, const XmssNativeValue256 *expected_output) { printf("Output: "); for (int i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("0x%08x ", output->data[i]); + printf("0x%08"PRIx32" ", output->data[i]); } printf("\nExpected: "); for (int i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("0x%08x ", expected_output->data[i]); + printf("0x%08"PRIx32" ", expected_output->data[i]); } printf("\n"); } @@ -61,9 +53,11 @@ static bool test_rand_hash_with_sha256(void) XmssNativeValue256 output; Input_PRF rand_hash_state = INIT_INPUT_PRF; memcpy(&rand_hash_state.M.ADRS, adrs, sizeof(ADRS)); - native_256_copy(&rand_hash_state.KEY, &seed); + rand_hash_state.KEY = seed; - rand_hash(HASH_ABSTRACTION(&sha256_xmss_hashes) &output, &rand_hash_state, &left, &right); + DEFINE_HASH_FUNCTIONS; + INITIALIZE_HASH_FUNCTIONS(XMSS_PARAM_SHA2_10_256); + rand_hash(HASH_FUNCTIONS &output, &rand_hash_state, &left, &right); const bool success = memcmp(&output, &expected_output, sizeof(XmssNativeValue256)) == 0; printf("rand hash function test %s for SHA256!\n", success ? "succeeded" : "failed"); @@ -98,9 +92,11 @@ static bool test_rand_hash_with_shake256_256(void) XmssNativeValue256 output; Input_PRF rand_hash_state = INIT_INPUT_PRF; memcpy(&rand_hash_state.M.ADRS, adrs, sizeof(ADRS)); - native_256_copy(&rand_hash_state.KEY, &seed); + rand_hash_state.KEY = seed; - rand_hash(HASH_ABSTRACTION(&shake256_256_xmss_hashes) &output, &rand_hash_state, &left, &right); + DEFINE_HASH_FUNCTIONS; + INITIALIZE_HASH_FUNCTIONS(XMSS_PARAM_SHAKE256_10_256); + rand_hash(HASH_FUNCTIONS &output, &rand_hash_state, &left, &right); const bool success = memcmp(&output, &expected_output, sizeof(XmssNativeValue256)) == 0; printf("rand hash function test %s for SHAKE256_256!\n", success ? "succeeded" : "failed"); @@ -112,7 +108,7 @@ static bool test_rand_hash_with_shake256_256(void) } #endif /* XMSS_ENABLE_SHAKE256_256 */ -int main() +int main(void) { bool success = true; #if XMSS_ENABLE_SHA256 diff --git a/tests/test-sha256_xmss_hashes.c b/tests/test-sha256_xmss_hashes.c index a112d63..d2976bf 100644 --- a/tests/test-sha256_xmss_hashes.c +++ b/tests/test-sha256_xmss_hashes.c @@ -5,13 +5,21 @@ * SPDX-FileContributor: Frans van Dorsselaer */ -#include "config.h" +#include "libxmss.c" + +#if XMSS_ENABLE_SIGNING +# define reference_digest sha256_digest +#else +# define REFERENCE_DIGEST_SHA256 +# include "reference-digest.inc" +#endif + +#include "test-xmss_hashes.inc" #if XMSS_ENABLE_HASH_ABSTRACTION # include "sha256_xmss_hashes.h" -# include "test-xmss_hashes.h" -const xmss_hashes *const test_xmss_hashes = &sha256_xmss_hashes; +const xmss_hashes *const hash_functions = &sha256_xmss_hashes; #endif diff --git a/tests/test-shake256_256_xmss_hashes.c b/tests/test-shake256_256_xmss_hashes.c index 7c6d38d..9c12449 100644 --- a/tests/test-shake256_256_xmss_hashes.c +++ b/tests/test-shake256_256_xmss_hashes.c @@ -5,13 +5,21 @@ * SPDX-FileContributor: Frans van Dorsselaer */ -#include "config.h" +#include "libxmss.c" + +#if XMSS_ENABLE_SIGNING +# define reference_digest shake256_256_digest +#else +# define REFERENCE_DIGEST_SHAKE256_256 +# include "reference-digest.inc" +#endif + +#include "test-xmss_hashes.inc" #if XMSS_ENABLE_HASH_ABSTRACTION # include "shake256_256_xmss_hashes.h" -# include "test-xmss_hashes.h" -const xmss_hashes *const test_xmss_hashes = &shake256_256_xmss_hashes; +const xmss_hashes *const hash_functions = &shake256_256_xmss_hashes; #endif diff --git a/tests/test-static_assert.c b/tests/test-static_assert.c index dca2381..94d8a9a 100644 --- a/tests/test-static_assert.c +++ b/tests/test-static_assert.c @@ -44,7 +44,7 @@ int main(void) #endif /* This should always compile (this is what compat.h is for). */ - STATIC_ASSERT(1, ""); + XMSS_STATIC_ASSERT(1, ""); /* was deliberately not included, so EXIT_XXX cannot be used. */ return have_static_assert ? 0 : 1; diff --git a/tests/test-structures.c b/tests/test-structures.c index 2a64ec3..43be4f8 100644 --- a/tests/test-structures.c +++ b/tests/test-structures.c @@ -7,7 +7,7 @@ #include #include -#include "private.h" +#include "signing_private.h" int main(void) { uint8_t buf[XMSS_KEY_GENERATION_CONTEXT_SIZE(16)]; diff --git a/tests/test-version.c b/tests/test-version.c new file mode 100644 index 0000000..16413a1 --- /dev/null +++ b/tests/test-version.c @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2024 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Frans van Dorsselaer + */ + +#include "version.h" + +#include +#include +#include +#include + +int main(void) +{ + bool success = true; + + // Test the availability of all the macro's (compile-time tests). + (void)XMSS_LIBRARY_VERSION; + (void)XMSS_LIBRARY_VERSION_MAJOR; + (void)XMSS_LIBRARY_VERSION_MINOR; + (void)XMSS_LIBRARY_VERSION_PATCH; + (void)XMSS_LIBRARY_VERSION_CONSTRUCT(1,2,3); + + // Test the runtime. + uint32_t runtime_version = xmss_library_get_version(); + success = success && (runtime_version == XMSS_LIBRARY_VERSION); + + puts(success ? "PASS" : "FAIL"); + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/test-wotsp.c b/tests/test-wotsp.c index 9093b9b..4dbe71a 100644 --- a/tests/test-wotsp.c +++ b/tests/test-wotsp.c @@ -5,17 +5,14 @@ * SPDX-FileContributor: Max Fillinger */ +#include "libxmss.c" + #include #include #include #include #include -#include "config.h" -#include "wotsp.h" - -#include "private.h" -#include "signing.h" #if XMSS_ENABLE_SHA256 static bool test_pubkey_gen_sha256(void) @@ -438,8 +435,8 @@ static bool test_verify(void) wotsp_sign(context, &signature, &message_digest, &secret_seed, &public_seed, 0); /* Check that we calculate the right public key for the signed message. */ - wotsp_calculate_expected_public_key(HASH_ABSTRACTION(&context->hash_functions) &expected_public_key, - &message_digest, &signature, &public_seed, 0); + wotsp_calculate_expected_public_key(HASH_FUNCTIONS_FROM(*context) &expected_public_key, &message_digest, + &signature, &public_seed, 0); bool success = memcmp(&public_key, &expected_public_key, sizeof(public_key)) == 0; if (!success) { printf("Failed to verify correct signature for %s.\n", hash_names[i]); @@ -447,8 +444,8 @@ static bool test_verify(void) overall_success = overall_success && success; /* Check that we calculate a different public key for an incorrect message. */ - wotsp_calculate_expected_public_key(HASH_ABSTRACTION(&context->hash_functions) &expected_public_key, - &bad_digest, &signature, &public_seed, 0); + wotsp_calculate_expected_public_key(HASH_FUNCTIONS_FROM(*context) &expected_public_key, &bad_digest, + &signature, &public_seed, 0); success = memcmp(&public_key, &expected_public_key, sizeof(public_key)) != 0; if (!success) { printf("Incorrectly verified a wrong signature for %s.\n", hash_names[i]); diff --git a/tests/test-xmss_context_initialize.c b/tests/test-xmss_context_initialize.c index 4861b9c..dda894b 100644 --- a/tests/test-xmss_context_initialize.c +++ b/tests/test-xmss_context_initialize.c @@ -5,22 +5,11 @@ * SPDX-FileContributor: Pepijn Westen */ +#include "libxmss.c" + #include #include -#include "signing.h" - -#include "types.h" -#include "structures.h" - -#include "utils.h" -// for some white-box tests -#include "private.h" -#if XMSS_ENABLE_HASH_ABSTRACTION -# include "sha256_xmss_hashes.h" -# include "shake256_256_xmss_hashes.h" -#endif - static bool free_called = false; static bool realloc_called = false; @@ -56,10 +45,6 @@ struct argument_convenience_struct { XmssZeroizeFunction zeroize; }; -// this is horrible, but it's a throw away test. -#define peek_opaque(name, instance_pointer, member) \ - ((struct name*)instance_pointer)->member - int main(void) { bool success = true; @@ -103,8 +88,8 @@ int main(void) // test success with stdlib realloc test(args, XMSS_OKAY); success = success && (context_ptr_dynamic != NULL); - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, initialized) == XMSS_INITIALIZATION_INITIALIZED; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, parameter_set) == (uint32_t)args.parameter_set; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->initialized == XMSS_INITIALIZATION_INITIALIZED; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->parameter_set == (uint32_t)args.parameter_set; if (success) free(context_ptr_dynamic); context_ptr_dynamic = NULL; @@ -112,15 +97,10 @@ int main(void) #if XMSS_ENABLE_SHAKE256_256 test(args, XMSS_OKAY); success = success && (context_ptr_dynamic != NULL); - success = success && ((struct XmssSigningContext*) context_ptr_dynamic)->initialized == XMSS_INITIALIZATION_INITIALIZED; - success = success && ((struct XmssSigningContext*) context_ptr_dynamic)->parameter_set == (uint32_t)args.parameter_set; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->initialized == XMSS_INITIALIZATION_INITIALIZED; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->parameter_set == (uint32_t)args.parameter_set; #if XMSS_ENABLE_HASH_ABSTRACTION - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).digest == shake256_256_xmss_hashes.digest; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).F == shake256_256_xmss_hashes.F; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).H == shake256_256_xmss_hashes.H; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).H_msg == shake256_256_xmss_hashes.H_msg; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).PRF == shake256_256_xmss_hashes.PRF; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).PRFkeygen == shake256_256_xmss_hashes.PRFkeygen; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->hash_functions == &shake256_256_xmss_hashes; #endif if (success) free(context_ptr_dynamic); context_ptr_dynamic = NULL; @@ -130,15 +110,10 @@ int main(void) args.parameter_set = XMSS_PARAM_SHA2_16_256; test(args, XMSS_OKAY); success = success && (context_ptr_dynamic != NULL); - success = success && ((struct XmssSigningContext*) context_ptr_dynamic)->initialized == XMSS_INITIALIZATION_INITIALIZED; - success = success && ((struct XmssSigningContext*) context_ptr_dynamic)->parameter_set == (uint32_t)args.parameter_set; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->initialized == XMSS_INITIALIZATION_INITIALIZED; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->parameter_set == (uint32_t)args.parameter_set; #if XMSS_ENABLE_HASH_ABSTRACTION - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).digest == sha256_xmss_hashes.digest; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).F == sha256_xmss_hashes.F; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).H == sha256_xmss_hashes.H; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).H_msg == sha256_xmss_hashes.H_msg; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).PRF == sha256_xmss_hashes.PRF; - success = success && peek_opaque(XmssSigningContext, context_ptr_dynamic, hash_functions).PRFkeygen == sha256_xmss_hashes.PRFkeygen; + success = success && ((XmssSigningContext*) context_ptr_dynamic)->hash_functions == &sha256_xmss_hashes; #endif if (success) free(context_ptr_dynamic); context_ptr_dynamic = NULL; @@ -156,10 +131,10 @@ int main(void) // test realloc succeeds args.context = &context_ptr; test(args, XMSS_OKAY); - success = success && ((struct XmssSigningContext*) context_ptr)->parameter_set == (uint32_t)args.parameter_set; + success = success && ((XmssSigningContext*) context_ptr)->parameter_set == (uint32_t)args.parameter_set; success = success && realloc_called; success = success && !free_called; - success = success && peek_opaque(XmssSigningContext, context_ptr, zeroize) == xmss_zeroize; + success = success && ((XmssSigningContext*) context_ptr)->zeroize == xmss_zeroize; realloc_called = false; return success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/test-xmss_generate_private_key.c b/tests/test-xmss_generate_private_key.c index c6e6ca5..3e6b4c0 100644 --- a/tests/test-xmss_generate_private_key.c +++ b/tests/test-xmss_generate_private_key.c @@ -8,21 +8,14 @@ #include #include +#include "config.h" #include "signing.h" -#include "types.h" -#include "structures.h" -#include -#include - -#include "utils.h" - - int main(void) { bool success = true; - static XmssSigningContext *context_ptr_dynamic = NULL; + XmssSigningContext *context_ptr_dynamic = NULL; #if XMSS_ENABLE_SHA256 const uint32_t any_supported_param_set = XMSS_PARAM_SHA2_10_256; diff --git a/tests/test-xmss_generate_public_key.c b/tests/test-xmss_generate_public_key.c index 138a40d..621d878 100644 --- a/tests/test-xmss_generate_public_key.c +++ b/tests/test-xmss_generate_public_key.c @@ -10,23 +10,16 @@ #include #include -#include "config.h" - -#include "signing.h" -#include "xmss_tree.h" - #include "endianness.h" -#include "structures.h" -#include "types.h" -#include "xmss_hashes.h" -#include "utils.h" +#include "signing.h" +#include "signing_private.h" int main(void) { bool success = true; - static XmssSigningContext *context_ptr_dynamic = NULL; + XmssSigningContext *context_ptr_dynamic = NULL; #if !XMSS_ENABLE_SHA256 const uint32_t any_supported_param_set = XMSS_PARAM_SHAKE256_10_256; @@ -98,11 +91,11 @@ int main(void) success = success && keygen_context == NULL; XmssPublicKeyInternal *public_key_internal = (XmssPublicKeyInternal *)public_key_blob->data; if (success) { - if (memcmp(&public_key_internal->public_key, &expected_root_be, sizeof(XmssValue256))) { + if (memcmp(&public_key_internal->root, &expected_root_be, sizeof(XmssValue256))) { success = false; printf("xmss_finish_calculate_public_key returned an unexpected result:\n"); for (size_t i = 0; i < sizeof(XmssValue256); i++) { - printf("%02x", public_key_internal->public_key.data[i]); + printf("%02x", public_key_internal->root.data[i]); } printf("\n"); printf("Expected result:\n"); @@ -115,13 +108,13 @@ int main(void) printf("One of the public key generation functions failed.\n"); } - XmssPublicKeyBlob *exported_public_key_blob = NULL; - success = success && XMSS_OKAY == xmss_export_public_key(&exported_public_key_blob, key_context); + XmssPublicKey exported_public_key = { 0 }; + success = success && XMSS_OKAY == xmss_export_public_key(&exported_public_key, key_context); /* Check public key against expected public key. */ - success = success && memcmp(expected_pk, (uint8_t *)&exported_public_key_blob->data, sizeof(XmssPublicKey)) == 0; + success = success && memcmp(expected_pk, (uint8_t *)&exported_public_key, sizeof(XmssPublicKey)) == 0; /* Check public key against signing key_context. */ - success = success && XMSS_OKAY == xmss_verify_exported_public_key(exported_public_key_blob, key_context); + success = success && XMSS_OKAY == xmss_verify_exported_public_key(&exported_public_key, key_context); xmss_free_key_context(key_context); free(stateless_blob); diff --git a/tests/test-xmss_hashes.h b/tests/test-xmss_hashes.h deleted file mode 100644 index 2f03399..0000000 --- a/tests/test-xmss_hashes.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. - * SPDX-License-Identifier: MIT - * - * SPDX-FileContributor: Frans van Dorsselaer - */ - -#pragma once - -#ifndef XMSS_TEST_XMSS_HASHES_H_INCLUDED -/** @private @brief Include guard. */ -#define XMSS_TEST_XMSS_HASHES_H_INCLUDED - -#include "config.h" - -#if XMSS_ENABLE_HASH_ABSTRACTION - -# include "xmss_hashes.h" - -extern const xmss_hashes *const test_xmss_hashes; - -#endif - -#endif /* !XMSS_TEST_XMSS_HASHES_H_INCLUDED */ diff --git a/tests/test-xmss_hashes.c b/tests/test-xmss_hashes.inc similarity index 85% rename from tests/test-xmss_hashes.c rename to tests/test-xmss_hashes.inc index 6024524..1b5c754 100644 --- a/tests/test-xmss_hashes.c +++ b/tests/test-xmss_hashes.inc @@ -11,57 +11,12 @@ #include #include -#include "test-xmss_hashes.h" -#include "endianness.h" -#include "xmss_hashes.h" +#if XMSS_ENABLE_HASH_ABSTRACTION +extern const xmss_hashes *const hash_functions; -/* Test all counts from 0 up to and including 3 full blocks (we use the bigger block size of SHAKE256). */ -#define MAX_WORD_COUNT (3 * 136 / sizeof(uint32_t)) - -static bool test_native_digest_count(size_t count) -{ - /* Setup */ - uint32_t words[MAX_WORD_COUNT]; - - for (uint32_t i = 0; i < count; ++i) { - /* some non-trivial data */ - words[i] = (uint32_t)(count * 0x01234567 + i * 0xfedcba98); - } - - /* Test */ - - XmssNativeValue256 native_digest; - xmss_native_digest(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, words, count); - XmssValue256 digest; - native_to_big_endian_256(&digest, &native_digest); - - /* Verification */ - - uint8_t verification_message[MAX_WORD_COUNT * sizeof(uint32_t)]; - native_to_big_endian(verification_message, words, (uint_fast16_t)count); - XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, - count * sizeof(uint32_t)); - - return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; -} - - -static bool test_native_digest() -{ - bool success = true; - - for (size_t word_count = 33; word_count <= MAX_WORD_COUNT; ++word_count) { - if (!test_native_digest_count(word_count)) { - fprintf(stderr, "Failed test_native_digest for word count %u\n", (unsigned int)word_count); - success = false; - } - } - - return success; -} +#endif /* Some 32 unique bytes of test data */ @@ -121,7 +76,7 @@ static void toByte(uint8_t *dst, uint64_t x) } -static bool test_F() +static bool test_F(void) { /* Setup */ @@ -135,7 +90,7 @@ static bool test_F() /* Test */ XmssNativeValue256 native_digest; - xmss_F(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, &input); + xmss_F(HASH_FUNCTIONS &native_digest, &input); XmssValue256 digest; native_to_big_endian_256(&digest, &native_digest); @@ -146,8 +101,7 @@ static bool test_F() memcpy(verification_message + 32, &test_KEY, 32); memcpy(verification_message + 64, &test_M, 32); XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, - sizeof(verification_message)); + reference_digest(&verification_digest, verification_message, sizeof(verification_message)); return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; @@ -156,7 +110,7 @@ static bool test_F() } -static bool test_H() +static bool test_H(void) { /* Setup */ @@ -170,7 +124,7 @@ static bool test_H() /* Test */ XmssNativeValue256 native_digest; - xmss_H(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, &input); + xmss_H(HASH_FUNCTIONS &native_digest, &input); XmssValue256 digest; native_to_big_endian_256(&digest, &native_digest); @@ -181,8 +135,7 @@ static bool test_H() memcpy(verification_message + 32, &test_KEY, 32); memcpy(verification_message + 64, test_M, 64); XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, - sizeof(verification_message)); + reference_digest(&verification_digest, verification_message, sizeof(verification_message)); return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; @@ -215,8 +168,11 @@ static bool test_H_msg_length(size_t message_length) /* Test */ + XmssHMsgCtx ctx; XmssNativeValue256 native_digest; - xmss_H_msg(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, &input, test_message, message_length); + xmss_H_msg_init(HASH_FUNCTIONS &ctx, &input); + xmss_H_msg_update(HASH_FUNCTIONS &ctx, test_message, message_length, NULL); + xmss_H_msg_finalize(HASH_FUNCTIONS &native_digest, &ctx); XmssValue256 digest; native_to_big_endian_256(&digest, &native_digest); @@ -236,7 +192,7 @@ static bool test_H_msg_length(size_t message_length) memcpy(verification_message + 128, test_message, message_length); XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, 128 + message_length); + reference_digest(&verification_digest, verification_message, 128 + message_length); free(test_message); free(verification_message); @@ -247,7 +203,7 @@ static bool test_H_msg_length(size_t message_length) # undef test_Root } -static bool test_H_msg() +static bool test_H_msg(void) { bool success = true; @@ -262,8 +218,7 @@ static bool test_H_msg() return success; } - -static bool test_PRF() +static bool test_PRF(void) { /* Setup */ @@ -277,7 +232,7 @@ static bool test_PRF() /* Test */ XmssNativeValue256 native_digest; - xmss_PRF(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, &input); + xmss_PRF(HASH_FUNCTIONS &native_digest, &input); XmssValue256 digest; native_to_big_endian_256(&digest, &native_digest); @@ -292,8 +247,7 @@ static bool test_PRF() memcpy(verification_message + 32, &test_KEY, 32); memcpy(verification_message + 64, &test_ADRS, 32); XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, - sizeof(verification_message)); + reference_digest(&verification_digest, verification_message, sizeof(verification_message)); return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; @@ -302,7 +256,10 @@ static bool test_PRF() } -static bool test_PRFkeygen() +#if XMSS_ENABLE_SIGNING + + +static bool test_PRFkeygen(void) { /* Setup */ @@ -319,7 +276,7 @@ static bool test_PRFkeygen() XmssNativeValue256 native_digest; XmssValue256 digest; - xmss_PRFkeygen(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, &input); + xmss_PRFkeygen(HASH_FUNCTIONS &native_digest, &input); native_to_big_endian_256(&digest, &native_digest); /* @@ -334,8 +291,7 @@ static bool test_PRFkeygen() memcpy(verification_message + 64, &test_SEED, 32); memcpy(verification_message + 96, &test_ADRS, 32); XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, - sizeof(verification_message)); + reference_digest(&verification_digest, verification_message, sizeof(verification_message)); return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; @@ -345,7 +301,7 @@ static bool test_PRFkeygen() } -static bool test_PRFindex() +static bool test_PRFindex(void) { /* Setup */ @@ -361,7 +317,7 @@ static bool test_PRFindex() XmssNativeValue256 native_digest; XmssValue256 digest; - xmss_PRFindex(HASH_ABSTRACTION(test_xmss_hashes) &native_digest, &input); + xmss_PRFindex(HASH_FUNCTIONS &native_digest, &input); native_to_big_endian_256(&digest, &native_digest); /* @@ -376,8 +332,7 @@ static bool test_PRFindex() memcpy(verification_message + 64, &test_SEED, 32); toByte(verification_message + 96, 42); XmssValue256 verification_digest; - xmss_digest(HASH_ABSTRACTION(test_xmss_hashes) &verification_digest, verification_message, - sizeof(verification_message)); + reference_digest(&verification_digest, verification_message, sizeof(verification_message)); return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; @@ -386,17 +341,68 @@ static bool test_PRFindex() } +/* Test all counts from 0 up to and including 3 full blocks (we use the bigger block size of SHAKE256). */ +#define MAX_WORD_COUNT (3 * 136 / sizeof(uint32_t)) + +static bool test_native_digest_count(size_t count) +{ + /* Setup */ + uint32_t words[MAX_WORD_COUNT]; + + for (uint32_t i = 0; i < count; ++i) { + /* some non-trivial data */ + words[i] = (uint32_t)(count * 0x01234567 + i * 0xfedcba98); + } + + /* Test */ + + XmssNativeValue256 native_digest; + xmss_native_digest(HASH_FUNCTIONS &native_digest, words, count); + XmssValue256 digest; + native_to_big_endian_256(&digest, &native_digest); + + /* Verification */ + + uint8_t verification_message[MAX_WORD_COUNT * sizeof(uint32_t)]; + native_to_big_endian(verification_message, words, (uint_fast16_t)count); + XmssValue256 verification_digest; + reference_digest(&verification_digest, verification_message, count * sizeof(uint32_t)); + + return memcmp(&digest, &verification_digest, sizeof(XmssValue256)) == 0; +} + + +static bool test_native_digest(void) +{ + bool success = true; + + for (size_t word_count = 33; word_count <= MAX_WORD_COUNT; ++word_count) { + if (!test_native_digest_count(word_count)) { + fprintf(stderr, "Failed test_native_digest for word count %u\n", (unsigned int)word_count); + success = false; + } + } + + return success; +} + + +#endif /* XMSS_ENABLE_SIGNING */ + + int main(void) { bool success = true; - success = success && test_native_digest(); success = success && test_F(); success = success && test_H(); success = success && test_H_msg(); success = success && test_PRF(); +#if XMSS_ENABLE_SIGNING success = success && test_PRFkeygen(); success = success && test_PRFindex(); + success = success && test_native_digest(); +#endif return success ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/test-xmss_sign_verify.c b/tests/test-xmss_sign_verify.c new file mode 100644 index 0000000..5effe01 --- /dev/null +++ b/tests/test-xmss_sign_verify.c @@ -0,0 +1,177 @@ +/* + * SPDX-FileCopyrightText: 2023 Fox Crypto B.V. + * SPDX-License-Identifier: MIT + * + * SPDX-FileContributor: Pepijn Westen + */ + +#include +#include +#include +#include + +#include "endianness.h" +#include "signing.h" +#include "signing_private.h" +#include "verification.h" + +/* + * Run sign and verify test, in which a single signature is generated and verified as a sanity check. + * Generates a key for the provided parameter set, places a signature and verifies that the signature verifies. +*/ +static bool sign_verify_test(XmssParameterSetOID test_parameter_set) +{ + bool success = true; + + XmssSigningContext *context_ptr_dynamic = NULL; + XmssKeyContext *key_context = NULL; + XmssPrivateKeyStatelessBlob *stateless_blob = NULL; + XmssPrivateKeyStatefulBlob *stateful_blob = NULL; + XmssKeyGenerationContext *keygen_context = NULL; + XmssPublicKeyInternalBlob *public_key_blob = NULL; + XmssInternalCache *internal_cache = NULL; + XmssInternalCache *generation_cache = NULL; + XmssPublicKey exported_public_key = { 0 }; + XmssSignatureBlob *signature_blob = NULL; + + success = success && xmss_context_initialize(&context_ptr_dynamic, test_parameter_set, realloc, free, NULL); + + uint8_t random[32] = {0}; + uint8_t secure_random[96] = {0}; + + for (size_t i = 0; i < sizeof(secure_random); i++) { + secure_random[i] = (uint8_t)i; + } + + XmssBuffer random_buffer = {sizeof(random), random}; + XmssBuffer secure_random_buffer = {sizeof(secure_random), secure_random}; + + XmssIndexObfuscationSetting index_obfuscation_setting = XMSS_INDEX_OBFUSCATION_OFF; + + /* Call xmss_generate_private_key with some reasonable params. */ + success = success && xmss_generate_private_key(&key_context, &stateless_blob, &stateful_blob, + &secure_random_buffer, index_obfuscation_setting, &random_buffer, context_ptr_dynamic) == XMSS_OKAY; + + success = success && XMSS_OKAY == xmss_generate_public_key(&keygen_context, &internal_cache, &generation_cache, + key_context, XMSS_CACHE_TOP, 0, 1); + success = success && XMSS_OKAY == xmss_calculate_public_key_part(keygen_context, 0); + success = success && XMSS_OKAY == xmss_finish_calculate_public_key(&public_key_blob, &keygen_context, + key_context); + success = success && (keygen_context == NULL); + + success = success && XMSS_OKAY == xmss_export_public_key(&exported_public_key, key_context); + success = success && XMSS_OKAY == xmss_request_future_signatures(&stateful_blob, key_context, 1); + + // The message, 10 * the maximum block size (136 for SHAKE) + uint8_t message_buffer[10 * 136]; + for (size_t i = 0; i < sizeof(message_buffer); ++i) { + // some "random" stuff, doesn't really matter + message_buffer[i] = (uint8_t)((13 + 31 * i) & 0xff); + } + XmssBuffer message = {sizeof(message_buffer), message_buffer}; + + // Sign the message + success = success && XMSS_OKAY == xmss_sign_message(&signature_blob, key_context, &message); + + // Verify that the signature verifies + XmssVerificationContext verification_ctx = {0}; + XmssSignature *signature = xmss_get_signature_struct(signature_blob); + const uint8_t *volatile part_verify = NULL; + success = success && XMSS_OKAY == xmss_verification_init(&verification_ctx, &exported_public_key, signature, + signature_blob->data_size); + success = success && XMSS_OKAY == xmss_verification_update(&verification_ctx, message.data, message.data_size, + &part_verify); + success = success && part_verify == message.data; + success = success && XMSS_OKAY == xmss_verification_check(&verification_ctx, &exported_public_key); + // redundant, for fault tolerance + success = success && XMSS_OKAY == xmss_verification_check(&verification_ctx, &exported_public_key); + + // Verify message with different "corner case" part sizes (block size SHA256: 64, SHAKE256/256: 136) + // We'll test every combination of first part size + part size for the remainder (i.e., this is 400+ test cases). + size_t part_sizes[] = { + 0, + 1, + 2, + 64 - 2, + 64 - 1, + 64, + 64 + 1, + 64 + 2, + 136 - 2, + 136 - 1, + 136, + 136 + 1, + 136 + 2, + 2 * 64 - 2, + 2 * 64 - 1, + 2 * 64, + 2 * 64 + 1, + 2 * 64 + 2, + 2 * 136 - 2, + 2 * 136 - 1, + 2 * 136, + 2 * 136 + 1, + 2 * 136 + 2, + sizeof(message_buffer) - 2, + sizeof(message_buffer) - 1, + }; + for (size_t first_index = 0; first_index < sizeof(part_sizes) / sizeof(size_t); ++first_index) { + // We need to skip size 0 for the remaining parts + for (size_t other_index = 1; other_index < sizeof(part_sizes) / sizeof(size_t); ++other_index) { + const uint8_t *part = message.data; + size_t remaining = message.data_size; + // size of the first part + size_t part_size = part_sizes[first_index]; + + success = success && XMSS_OKAY == xmss_verification_init(&verification_ctx, &exported_public_key, signature, + signature_blob->data_size); + while (remaining > 0) { + if (part_size > remaining) { + // we've reached the end, this is the final part + part_size = remaining; + } + + success = success && XMSS_OKAY == xmss_verification_update(&verification_ctx, part, part_size, + &part_verify); + success = success && part_verify == part; + + part += part_size; + remaining -= part_size; + // size of the remaining parts, if any + part_size = part_sizes[other_index]; + } + success = success && XMSS_OKAY == xmss_verification_check(&verification_ctx, &exported_public_key); + // redundant, for fault tolerance + success = success && XMSS_OKAY == xmss_verification_check(&verification_ctx, &exported_public_key); + } + } + + free(signature_blob); + free(keygen_context); + xmss_free_key_context(key_context); + free(public_key_blob); + free(stateless_blob); + free(stateful_blob); + free(context_ptr_dynamic); + free(internal_cache); + free(generation_cache); + + return success; +} + +int main(void) +{ + bool success = true; + +#if XMSS_ENABLE_SHA256 + success = success && sign_verify_test(XMSS_PARAM_SHA2_10_256); +#endif +#if XMSS_ENABLE_SHAKE256_256 + success = success && sign_verify_test(XMSS_PARAM_SHAKE256_10_256); +#endif +#if (!XMSS_ENABLE_SHA256) && (!XMSS_ENABLE_SHAKE256_256) +# error "Invalid configuration" +#endif + + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/tests/test-xmss_tree_hash.c b/tests/test-xmss_tree_hash.c index f2d8b3c..ff92639 100644 --- a/tests/test-xmss_tree_hash.c +++ b/tests/test-xmss_tree_hash.c @@ -5,27 +5,20 @@ * SPDX-FileContributor: Pepijn Westen */ +#include "libxmss.c" + +#include #include #include #include #include -#include "config.h" - -#include "signing.h" -#include "xmss_tree.h" - -#include "structures.h" -#include "types.h" -#include "xmss_hashes.h" -#include "utils.h" - int main(void) { bool success = true; - static XmssSigningContext *context_ptr_dynamic = NULL; + XmssSigningContext *context_ptr_dynamic = NULL; #if !XMSS_ENABLE_SHA256 const uint32_t any_supported_param_set = XMSS_PARAM_SHAKE256_10_256; @@ -67,12 +60,12 @@ int main(void) success = false; printf("xmss_tree_hash returned an unexpected result:\n"); for (size_t i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("%08x", root.data[i]); + printf("%08"PRIx32" ", root.data[i]); } printf("\n"); printf("expected result:\n"); for (size_t i = 0; i < XMSS_VALUE_256_WORDS; i++) { - printf("%08x", expected_root.data[i]); + printf("%08"PRIx32" ", expected_root.data[i]); } } diff --git a/tests/test-xmss_zeroize.c b/tests/test-xmss_zeroize.c index 25e6d80..f3ffcd2 100644 --- a/tests/test-xmss_zeroize.c +++ b/tests/test-xmss_zeroize.c @@ -5,14 +5,14 @@ * SPDX-FileContributor: Frans van Dorsselaer */ +#include "zeroize.c" + #include #include #include #include #include -#include "utils.h" - /** * @brief * Volatile pointer to some volatile character object.