From 92582340a2e625969d9ad5ce07006af71605fa7c Mon Sep 17 00:00:00 2001 From: jeffro256 Date: Tue, 22 Sep 2026 12:05:50 -0500 Subject: [PATCH] crypto: random and siphash static-init-safety Co-authored-by: Thomas Co-authored-by: selsta --- src/crypto/c_threads.h | 14 +++++++++++- src/crypto/generic-ops.h | 5 +++-- src/crypto/random.c | 47 +++++++++++++++++++++++++++++++++++----- src/crypto/random.h | 5 +++-- tests/crypto/random.c | 1 + 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/crypto/c_threads.h b/src/crypto/c_threads.h index 267b91f50..e9d82d1c2 100644 --- a/src/crypto/c_threads.h +++ b/src/crypto/c_threads.h @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2024, The Monero Project +// Copyright (c) 2019-2026, The Monero Project // // All rights reserved. // @@ -48,6 +48,12 @@ #define CTHR_THREAD_JOIN(thr) do { WaitForSingleObject(thr, INFINITE); CloseHandle(thr); } while(0) #define CTHR_THREAD_CLOSE(thr) CloseHandle((HANDLE)thr); +#define CTHR_ONCE_FLAG INIT_ONCE +#define CTHR_ONCE_INIT INIT_ONCE_STATIC_INIT +#define CTHR_ONCE_CALL(flag, f) (0 != InitOnceExecuteOnce(flag, f, NULL, NULL)) +#define CTHR_ONCE_DECLARE_CB(name) BOOL CALLBACK name(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) +#define CTHR_ONCE_CB_SUCCESS true + #else #include @@ -67,4 +73,10 @@ #define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL) #define CTHR_THREAD_CLOSE(thr) pthread_detach(thr) +#define CTHR_ONCE_FLAG pthread_once_t +#define CTHR_ONCE_INIT PTHREAD_ONCE_INIT +#define CTHR_ONCE_CALL(flag, f) (0 == pthread_once(flag, f)) +#define CTHR_ONCE_DECLARE_CB(name) void name(void) +#define CTHR_ONCE_CB_SUCCESS + #endif diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 80da7aaab..2cf98af12 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -73,12 +73,13 @@ namespace crypto { \ namespace crypto { inline std::size_t siphash_to_size_t(const void *data, std::size_t length) { - static_assert(sizeof(crypto_siphash_key) == crypto_shorthash_siphash24_KEYBYTES, + static_assert(16 == crypto_shorthash_siphash24_KEYBYTES, "crypto_siphash_key size must match the SipHash-2-4 key length"); static_assert(sizeof(std::uint64_t) == crypto_shorthash_siphash24_BYTES, "std::uint64_t size must match the SipHash-2-4 digest length"); std::uint64_t h; - crypto_shorthash_siphash24(reinterpret_cast(&h), static_cast(data), length, crypto_siphash_key); + crypto_shorthash_siphash24(reinterpret_cast(&h), static_cast(data), length, + get_static_siphash_key()); return h; } } diff --git a/src/crypto/random.c b/src/crypto/random.c index bc82bdfdd..6cfa47a22 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2024, The Monero Project +// Copyright (c) 2014-2026, The Monero Project // // All rights reserved. // @@ -32,6 +32,7 @@ #include #include +#include "c_threads.h" #include "hash-ops.h" #include "initializer.h" #include "random.h" @@ -97,18 +98,22 @@ static void generate_system_random_bytes(size_t n, void *result) { #endif static union hash_state state; -unsigned char crypto_siphash_key[16]; +static unsigned char crypto_siphash_key[16]; #if !defined(NDEBUG) static volatile int curstate; /* To catch thread safety problems. */ #endif +static CTHR_ONCE_FLAG init_random_once = CTHR_ONCE_INIT; FINALIZER(deinit_random) { #if !defined(NDEBUG) - assert(curstate == 1); + assert(curstate == 1 || curstate == 0); curstate = 0; #endif memset(&state, 0, sizeof(union hash_state)); +} + +FINALIZER(deinit_sip) { memset(crypto_siphash_key, 0, sizeof(crypto_siphash_key)); } @@ -122,19 +127,20 @@ static void lock_random_state(void) { #define HASH_CAPACITY_AREA 64 -INITIALIZER(init_random) { +static CTHR_ONCE_DECLARE_CB(init_random) { static_assert(sizeof(state) == HASH_DATA_AREA + HASH_CAPACITY_AREA, "Keccak state is the wrong size"); lock_random_state(); generate_system_random_bytes(HASH_DATA_AREA, &state); hash_permutation(&state); - generate_system_random_bytes(sizeof(crypto_siphash_key), crypto_siphash_key); REGISTER_FINALIZER(deinit_random); #if !defined(NDEBUG) assert(curstate == 0); curstate = 1; #endif + + return CTHR_ONCE_CB_SUCCESS; } void generate_random_bytes_not_thread_safe(size_t n, void *result) { @@ -144,6 +150,9 @@ void generate_random_bytes_not_thread_safe(size_t n, void *result) { * leaving this function does not leak the previous squeezed values. */ + const int r = CTHR_ONCE_CALL(&init_random_once, init_random); + (void) r; assert(r); + #if !defined(NDEBUG) assert(curstate == 1); curstate = 2; @@ -171,6 +180,14 @@ void generate_random_bytes_not_thread_safe(size_t n, void *result) { void add_extra_entropy_not_thread_safe(const void *ptr, size_t bytes) { + const int r = CTHR_ONCE_CALL(&init_random_once, init_random); + (void) r; assert(r); + +#if !defined(NDEBUG) + assert(curstate == 1); + curstate = 2; +#endif + size_t i; while (bytes > 0) @@ -182,4 +199,24 @@ void add_extra_entropy_not_thread_safe(const void *ptr, size_t bytes) ptr = cpadd(ptr, round_bytes); hash_permutation(&state); } + +#if !defined(NDEBUG) + assert(curstate == 2); + curstate = 1; +#endif +} + +static CTHR_ONCE_DECLARE_CB(init_sip) +{ + generate_system_random_bytes(sizeof(crypto_siphash_key), crypto_siphash_key); + REGISTER_FINALIZER(deinit_sip); + return CTHR_ONCE_CB_SUCCESS; +} + +const unsigned char *get_static_siphash_key(void) +{ + static CTHR_ONCE_FLAG once = CTHR_ONCE_INIT; + const int r = CTHR_ONCE_CALL(&once, init_sip); + (void) r; assert(r); + return crypto_siphash_key; } diff --git a/src/crypto/random.h b/src/crypto/random.h index 253b84fae..bdb1f8f92 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2024, The Monero Project +// Copyright (c) 2014-2026, The Monero Project // // All rights reserved. // @@ -39,7 +39,8 @@ extern "C" { void generate_random_bytes_not_thread_safe(size_t n, void *result); void add_extra_entropy_not_thread_safe(const void *ptr, size_t bytes); -extern unsigned char crypto_siphash_key[16]; +//! @brief Return pointer to random, constant 16-byte key, thread-safe and static-init-safe +const unsigned char *get_static_siphash_key(void); #ifdef __cplusplus } diff --git a/tests/crypto/random.c b/tests/crypto/random.c index dc7310901..64ad0d1c9 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -33,6 +33,7 @@ #include "crypto-tests.h" void setup_random(void) { + generate_random_bytes_not_thread_safe(0, NULL); memset(&state, 42, sizeof(union hash_state)); hash_permutation(&state); }