From 6b0033425cf696a41259b881a58ab1b71619f9b8 Mon Sep 17 00:00:00 2001 From: Uncle Stretch Date: Thu, 1 Oct 2026 21:20:17 +0300 Subject: [PATCH] implement more secure entropy hedging for offchain RNG Signed-off-by: Uncle Stretch --- pallets/exodus/Cargo.toml | 2 +- pallets/exodus/src/benchmarking.rs | 3 +- pallets/exodus/src/lib.rs | 60 ++++++++++------------- pallets/exodus/src/types/local_storage.rs | 4 +- 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/pallets/exodus/Cargo.toml b/pallets/exodus/Cargo.toml index f84b526..a6ee67e 100644 --- a/pallets/exodus/Cargo.toml +++ b/pallets/exodus/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ghost-exodus" -version = "0.0.17" +version = "0.0.18" description = "Threshold signature generation with DKG included" license.workspace = true authors.workspace = true diff --git a/pallets/exodus/src/benchmarking.rs b/pallets/exodus/src/benchmarking.rs index 2da947c..fcf8949 100644 --- a/pallets/exodus/src/benchmarking.rs +++ b/pallets/exodus/src/benchmarking.rs @@ -85,8 +85,7 @@ mod benchmarks { dkg_state.insert_index(authority_index); QualificationDkgState::::insert(&network_curve, dkg_state); - let mut rng = Pallet::::get_offchain_rng(authority_index); - + let mut rng = ChaCha20Rng::seed_from_u64(694201337); let (_, public_package) = network_curve.dkg_part1( authority_index, max_signers, diff --git a/pallets/exodus/src/lib.rs b/pallets/exodus/src/lib.rs index 7f43c6b..e879e24 100644 --- a/pallets/exodus/src/lib.rs +++ b/pallets/exodus/src/lib.rs @@ -31,7 +31,7 @@ use sp_io::hashing::blake2_256; use ghost_helpers::{ get_byzantium_quorum_threshold, SubstrateBlake2Hasher, - hash_chain::sequential_hash, + hash_chain::{cumulative_hash, sequential_hash}, networks::{NetworkData, NetworkCurve, NetworkType}, bounded_bitmap::{validate_bitmap_sizes, BoundedBitmap}, }; @@ -3559,12 +3559,10 @@ impl Pallet { let authority_index = exodus_storage.authority_index; let network_curve = exodus_storage.network_curve; - #[cfg(not(any(test, feature = "runtime-benchmarks")))] - let mut rng = Self::get_offchain_rng(); - - #[cfg(any(test, feature = "runtime-benchmarks"))] - let mut rng = Self::get_offchain_rng(authority_index); + let exodus_storage_encoded = exodus_storage.encode(); + let rng_context = [exodus_storage_encoded.as_slice(), secret_share]; + let mut rng = Self::get_offchain_rng(&rng_context); let (nonce, hiding_commitment, binding_commitment) = network_curve .generate_nonce_commitment(secret_share, &mut rng)?; @@ -3776,12 +3774,15 @@ impl Pallet { )); } - #[cfg(not(any(test, feature = "runtime-benchmarks")))] - let mut rng = Self::get_offchain_rng(); + let dkg_storage_encoded = dkg_storage.encode(); + let qualification_state_encoded = qualification_state.encode(); - #[cfg(any(test, feature = "runtime-benchmarks"))] - let mut rng = Self::get_offchain_rng(authority_index); + let rng_context = [ + dkg_storage_encoded.as_slice(), + qualification_state_encoded.as_slice(), + ]; + let mut rng = Self::get_offchain_rng(&rng_context); let (secret_package, public_package) = network_curve .dkg_part1(authority_index, max_signers, min_signers, &mut rng)?; @@ -4736,11 +4737,17 @@ impl Pallet { dkg_index: DkgIndex, network_curve: NetworkCurve, ) { - #[cfg(not(any(test, feature = "runtime-benchmarks")))] - let mut rng = Self::get_offchain_rng(); + let authority_index_bytes = authority_index.to_le_bytes(); + let dkg_index_bytes = dkg_index.to_le_bytes(); + let network_curve_bytes = [network_curve as u8]; - #[cfg(any(test, feature = "runtime-benchmarks"))] - let mut rng = Self::get_offchain_rng(authority_index); + let rng_context = [ + round1_secret_package, + authority_index_bytes.as_slice(), + dkg_index_bytes.as_slice(), + network_curve_bytes.as_slice(), + ]; + let mut rng = Self::get_offchain_rng(&rng_context); for (&receiver_index, round2_package) in round2_packages.iter() { if receiver_index == authority_index { continue; } @@ -4811,26 +4818,13 @@ impl Pallet { } } - #[cfg(any(test, feature = "runtime-benchmarks"))] - fn get_offchain_rng(authority_index: AuthIndex) -> ChaCha20Rng { - let mut seed = [0u8; 32]; - - let unique_id = (authority_index as u64).saturating_add(1); - let index_bytes = unique_id.to_le_bytes(); - - for (i, &byte) in index_bytes.iter().enumerate() { - if i < seed.len() { - seed[i] ^= byte; - } - } - - rand_chacha::ChaCha20Rng::from_seed(seed) - } - - #[cfg(not(any(test, feature = "runtime-benchmarks")))] - fn get_offchain_rng() -> ChaCha20Rng { + fn get_offchain_rng(context: &[&[u8]]) -> ChaCha20Rng { let seed = sp_io::offchain::random_seed(); - ChaCha20Rng::from_seed(seed) + let seeded_context = sp_std::iter::once(&seed[..]) + .chain(context.iter().copied()); + + let hashed_seed = cumulative_hash::(seeded_context); + ChaCha20Rng::from_seed(hashed_seed.to_fixed_bytes()) } fn create_storage_key(first: &[u8], second: &[u8]) -> Vec { diff --git a/pallets/exodus/src/types/local_storage.rs b/pallets/exodus/src/types/local_storage.rs index 8bfce89..81a7c59 100644 --- a/pallets/exodus/src/types/local_storage.rs +++ b/pallets/exodus/src/types/local_storage.rs @@ -75,7 +75,7 @@ impl ExodusStorageWrapper { } } -#[derive(Default)] +#[derive(Default, Encode)] pub struct ExodusStorage where BlockNumber: Default, @@ -365,7 +365,7 @@ where } } -#[derive(Default, Clone, Copy)] +#[derive(Default, Encode, Clone, Copy)] pub struct DkgStorage where BlockNumber: Copy + Clone,