From 5ffd4407b4e914a1cbdb14cf60ecf710275ea5aa Mon Sep 17 00:00:00 2001 From: wowario Date: Tue, 23 Sep 2025 01:10:12 +0300 Subject: [PATCH] build cache with Argon2d --- src/crypto/wbag-hash.c | 61 +++++++++++++---------------- src/crypto/wbag-hash.h | 3 +- src/cryptonote_basic/wbag_cache.cpp | 40 ++----------------- 3 files changed, 32 insertions(+), 72 deletions(-) diff --git a/src/crypto/wbag-hash.c b/src/crypto/wbag-hash.c index 54610aca9..551000bc7 100644 --- a/src/crypto/wbag-hash.c +++ b/src/crypto/wbag-hash.c @@ -1,8 +1,8 @@ // -// Wownero Blake-AES-Groestl ("WBAG") +// Wownero Blake-Argon2d-Groestl ("WBAG") // -// Memory-hard hash function that combines BLAKE256 and Groestl256 using hardware-accelerated AES -// - Cache: parameterized, power-of-two, default 256 MiB of 64-byte entries derived via BLAKE256 +// Memory-hard hash function that combines BLAKE256, Argon2d, and Groestl256 using hardware-accelerated AES +// - Cache: Argon2d-derived buffer of size WBAG_CACHE_BYTES (default 256 MiB) // - Prehash: st = BLAKE256('m' || bd), parsed as four 64-bit little-endian lanes // - Rounds: data-dependent cache index, XOR entry words into lanes, rotate by SplitMix64-derived counts, // then one AES round per 128-bit pair (two total) using w0..w3 as the round keys @@ -43,6 +43,7 @@ #include "wbag-hash.h" #include "blake256.h" +#include "argon2/argon2.h" #include "aesb.h" #include "groestl.h" @@ -69,6 +70,14 @@ int WBAG_get_aes_mode(void) { return g_wbag_aes_mode; } #define WBAG_N_ENTRIES ( (WBAG_CACHE_BYTES / WBAG_ENTRY_BYTES) ) +#ifndef WBAG_ARGON2_P +#define WBAG_ARGON2_P 2u //threads +#endif +#ifndef WBAG_ARGON2_T +#define WBAG_ARGON2_T 1u // passes +#endif +_Static_assert(WBAG_ARGON2_P == 2u, "WBAG requires Argon2 p=2"); + // Upper bound to mitigate large-input in WBAG_hash() allocation #ifndef WBAG_MAX_BD_LEN #define WBAG_MAX_BD_LEN (16u * 1024u * 1024u) // 16 MiB @@ -186,13 +195,12 @@ static inline uint64_t WBAG_splitmix64(uint64_t x) { // 32-bit FNV-1a over the textual description of params uint32_t WBAG_params_signature(void) { - char buf[96]; + char buf[160]; int n = snprintf(buf, sizeof(buf), - "cache=%zu;entry=%zu;entries=%zu;rounds=%u", - (size_t)WBAG_CACHE_BYTES, - (size_t)WBAG_ENTRY_BYTES, - (size_t)WBAG_N_ENTRIES, - (unsigned)WBAG_ROUNDS); + "cache=%zu;entry=%zu;entries=%zu;rounds=%u;cache_prf=argon2d;ver=0x%02x;t=%u;p=%u", + (size_t)WBAG_CACHE_BYTES, (size_t)WBAG_ENTRY_BYTES, (size_t)WBAG_N_ENTRIES, + (unsigned)WBAG_ROUNDS, (unsigned)ARGON2_VERSION_13, + (unsigned)WBAG_ARGON2_T, (unsigned)WBAG_ARGON2_P); if (n < 0) n = 0; if (n >= (int)sizeof(buf)) n = (int)sizeof(buf) - 1; @@ -286,35 +294,20 @@ void WBAG_epoch_seed(uint64_t epoch, const uint8_t anchor_hash[32], uint8_t out3 } // Cache builder -void WBAG_build_cache(const uint8_t seed32[32], uint8_t *out_base, uint64_t start, uint64_t end) +int WBAG_build_cache(const uint8_t seed32[32], uint8_t *out_base) { - if (!seed32 || !out_base) return; - if (start >= end) return; - if (end > (uint64_t)WBAG_N_ENTRIES) return; + if (!seed32 || !out_base) return -1; - enum { IN_LEN = 32 + 4 + 4 }; - uint8_t in[IN_LEN]; - memcpy(in, seed32, 32); + const uint32_t m_cost_kib = (uint32_t)(WBAG_CACHE_BYTES / 1024u); + const uint32_t p = WBAG_ARGON2_P; + const uint32_t t_cost = (uint32_t)WBAG_ARGON2_T; - const size_t eb = (size_t)WBAG_ENTRY_BYTES; - uint8_t *base = out_base + (size_t)start * eb; - - for (uint64_t i = start; i < end; i++) { - uint8_t *entry = base + (size_t)(i - start) * eb; - WBAG_store32_le(&in[32], (uint32_t)i); - - size_t written = 0; - uint32_t blk = 0; - uint8_t out[32]; - while (written < eb) { - WBAG_store32_le(&in[36], blk++); - blake256_hash(out, in, (uint64_t)IN_LEN); - size_t take = eb - written; - if (take > 32) take = 32; - memcpy(entry + written, out, take); - written += take; - } + int rc = argon2_hash(t_cost, m_cost_kib, p, seed32, 32, "WBAG-cache", 10, out_base, (size_t)WBAG_CACHE_BYTES, NULL, 0, Argon2_d, ARGON2_VERSION_13); + if (rc != ARGON2_OK) { + memset(out_base, 0, (size_t)WBAG_CACHE_BYTES); + return rc; } + return 0; } // Hash: prehash, rounds with AES mix, Groestl finalize diff --git a/src/crypto/wbag-hash.h b/src/crypto/wbag-hash.h index 0729daf23..d03ff5b5b 100644 --- a/src/crypto/wbag-hash.h +++ b/src/crypto/wbag-hash.h @@ -12,7 +12,8 @@ void WBAG_set_aes_mode(int mode); int WBAG_get_aes_mode(void); void WBAG_epoch_seed(uint64_t epoch, const uint8_t anchor_hash[32], uint8_t out32[32]); -void WBAG_build_cache(const uint8_t seed[32], uint8_t *out_base, uint64_t start, uint64_t end); + +int WBAG_build_cache(const uint8_t seed32[32], uint8_t *out_base); int WBAG_hash(const void *bd, size_t bd_len, const uint8_t *cache, uint8_t out32[32]); diff --git a/src/cryptonote_basic/wbag_cache.cpp b/src/cryptonote_basic/wbag_cache.cpp index 8c7319c60..94ed52856 100644 --- a/src/cryptonote_basic/wbag_cache.cpp +++ b/src/cryptonote_basic/wbag_cache.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include "misc_log_ex.h" #include "string_tools.h" @@ -47,15 +46,8 @@ inline bool cache_matches(uint64_t e, const crypto::hash &ah) { return p && p->epoch == e && p->anchor_hash == ah; } -// Build threads; 2 threads by default -static std::atomic g_build_threads{2}; - } // namespace -void set_build_threads(unsigned t) noexcept { - g_build_threads.store(t, std::memory_order_relaxed); -} - bool ensure_cache(uint64_t e, const crypto::hash &ah) { if (cache_matches(e, ah)) @@ -109,36 +101,10 @@ bool ensure_cache(uint64_t e, const crypto::hash &ah) alignas(64) uint8_t seed[32]; epoch_seed_from_anchor(e, ah, seed); - unsigned req = g_build_threads.load(std::memory_order_relaxed); - unsigned hw = std::thread::hardware_concurrency(); - if (hw == 0) hw = 1; - unsigned T = req ? req : hw; - if (T < 1) T = 1; - const uint64_t n_entries = n; - if (T > n_entries) T = static_cast(n_entries); - std::atomic failed{false}; - if (T == 1) { - WBAG_build_cache(seed, fresh->data.data(), 0, n_entries); - } else { - std::vector workers; - workers.reserve(T); - for (unsigned i = 0; i < T; ++i) { - const uint64_t start = (static_cast(i) * n_entries) / T; - const uint64_t end = (static_cast(i + 1) * n_entries) / T; - workers.emplace_back([&, start, end]() noexcept { - try { - WBAG_build_cache(seed, fresh->data.data(), start, end); - } catch (...) { - failed.store(true, std::memory_order_relaxed); - } - }); - } - for (auto &t : workers) { try { t.join(); } catch (...) { failed.store(true, std::memory_order_relaxed); } } - if (failed.load(std::memory_order_relaxed)) { - MERROR("WBAG parallel build failed; not publishing" << " (epoch=" << e << ", anchor=" << anchor_hex << ")"); - return false; - } + if (WBAG_build_cache(seed, fresh->data.data()) != 0) { + MERROR("WBAG Argon2d cache build failed" << " (epoch=" << e << ", anchor=" << anchor_hex << ")"); + return false; } // final re-check to avoid duplicate publish