What changed, and why it matters
This commit changes how Monero hashes certain internal data types. Previously, some types were hashed using a simple identity function that directly used raw bytes as the hash value, which can cause many items to land in the same hash-table bucket and potentially leak information through timing or collision patterns. The patch replaces that with SipHash-2-4, a keyed, cryptographically designed hash function, using a secret random key generated at startup. This is a defensive hardening change; the commit message frames it only as a performance/distribution optimization, not as a security fix.
Treat as a defensive hardening commit. Review whether any containers using these hash specializations previously exhibited pathological collision behavior, and verify that the new crypto_siphash_key is generated before any hashed containers are populated. No immediate incident response is indicated by the commit alone.
Security signals we found
Replaces identity/trivial hash with keyed SipHash-2-4
Adds secret random key initialization and cleanup for hash keying
Removes size constraint that previously limited hashing to types at least as large as size_t
Touches hash functions for cryptographic types (rct::key and generic crypto types)
Commit message describes change as optimization, not security fix
Evidence from the diff
The patch modifies CRYPTO_DEFINE_HASH_FUNCTIONS in src/crypto/generic-ops.h and the std::hash
Changed components
src/crypto/generic-ops.hsrc/crypto/random.csrc/crypto/random.hsrc/ringct/rctTypes.hInspect captured patch +32 / −8
diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h
index 71af4b7..236b881 100644
--- a/src/crypto/generic-ops.h
+++ b/src/crypto/generic-ops.h
@@ -32,9 +32,13 @@
#include <cstddef>
#include <cstring>
+#include <cstdint>
#include <functional>
#include <memory>
#include <sodium/crypto_verify_32.h>
+#include <sodium/crypto_shorthash_siphash24.h>
+
+#include "random.h"
#define CRYPTO_MAKE_COMPARABLE(type) \
namespace crypto { \
@@ -57,22 +61,29 @@ 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,
+ "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<unsigned char*>(&h), static_cast<const unsigned char*>(data), length, crypto_siphash_key);
+ return h;
+ }
+}
+
#define CRYPTO_DEFINE_HASH_FUNCTIONS(type) \
namespace crypto { \
- static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \
inline std::size_t hash_value(const type &_v) { \
- std::size_t h; \
- memcpy(&h, std::addressof(_v), sizeof(h)); \
- return h; \
+ return siphash_to_size_t(std::addressof(_v), sizeof(_v)); \
} \
} \
namespace std { \
template<> \
struct hash<crypto::type> { \
std::size_t operator()(const crypto::type &_v) const { \
- std::size_t h; \
- memcpy(&h, std::addressof(_v), sizeof(h)); \
- return h; \
+ return ::crypto::siphash_to_size_t(std::addressof(_v), sizeof(_v)); \
} \
}; \
}
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 643d158..64a066a 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -96,6 +96,7 @@ static void generate_system_random_bytes(size_t n, void *result) {
#endif
static union hash_state state;
+unsigned char crypto_siphash_key[16];
#if !defined(NDEBUG)
static volatile int curstate; /* To catch thread safety problems. */
@@ -107,10 +108,12 @@ FINALIZER(deinit_random) {
curstate = 0;
#endif
memset(&state, 0, sizeof(union hash_state));
+ memset(crypto_siphash_key, 0, sizeof(crypto_siphash_key));
}
INITIALIZER(init_random) {
generate_system_random_bytes(32, &state);
+ generate_system_random_bytes(sizeof(crypto_siphash_key), crypto_siphash_key);
REGISTER_FINALIZER(deinit_random);
#if !defined(NDEBUG)
assert(curstate == 0);
diff --git a/src/crypto/random.h b/src/crypto/random.h
index 41601e4..253b84f 100644
--- a/src/crypto/random.h
+++ b/src/crypto/random.h
@@ -32,5 +32,15 @@
#include <stddef.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
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];
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 608aa5b..2d872cd 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -768,7 +768,7 @@ inline std::ostream &operator <<(std::ostream &o, const rct::key &v) {
namespace std
{
- template<> struct hash<rct::key> { std::size_t operator()(const rct::key &k) const { return reinterpret_cast<const std::size_t&>(k); } };
+ template<> struct hash<rct::key> { std::size_t operator()(const rct::key &k) const { return ::crypto::siphash_to_size_t(&k, sizeof(k)); } };
}
BLOB_SERIALIZER(rct::key);
Why this scored 45/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.