From 3aeeb420e075fcfb16d4f5c6cf725c7544ff56ca Mon Sep 17 00:00:00 2001 From: Andrea C Date: Wed, 28 Jan 2026 17:09:58 +0000 Subject: [PATCH 1/3] Change csprng API --- rs/consensus/src/consensus/batch_delivery.rs | 7 +-- rs/consensus/utils/src/lib.rs | 12 +--- rs/consensus/utils/src/membership.rs | 3 +- rs/crypto/prng/src/lib.rs | 37 ++++------- rs/crypto/prng/src/tests.rs | 60 ++++++++++++------ rs/crypto/prng/tests/tests.rs | 63 ++++++++++++------- rs/execution_environment/src/scheduler.rs | 3 +- .../src/scheduler/test_utilities.rs | 3 +- .../src/scheduler/threshold_signatures.rs | 2 +- rs/replay/src/player.rs | 8 +-- rs/types/types/src/crypto.rs | 1 + rs/types/types/src/crypto/hash.rs | 12 ++++ 12 files changed, 121 insertions(+), 90 deletions(-) diff --git a/rs/consensus/src/consensus/batch_delivery.rs b/rs/consensus/src/consensus/batch_delivery.rs index dd1ab0e79268..3d9cee92ce04 100644 --- a/rs/consensus/src/consensus/batch_delivery.rs +++ b/rs/consensus/src/consensus/batch_delivery.rs @@ -11,9 +11,7 @@ use ic_consensus_idkg::utils::{ generate_responses_to_signature_request_contexts, get_idkg_subnet_public_keys_and_pre_signatures, }; -use ic_consensus_utils::{ - crypto_hashable_to_seed, membership::Membership, pool_reader::PoolReader, -}; +use ic_consensus_utils::{membership::Membership, pool_reader::PoolReader}; use ic_consensus_vetkd::VetKdPayloadBuilderImpl; use ic_error_types::RejectCode; use ic_https_outcalls_consensus::payload_builder::CanisterHttpPayloadBuilderImpl; @@ -28,6 +26,7 @@ use ic_protobuf::{ log::consensus_log_entry::v1::ConsensusLogEntry, registry::{crypto::v1::PublicKey as PublicKeyProto, subnet::v1::InitialNiDkgTranscriptRecord}, }; +use ic_types::crypto::crypto_hashable_to_randomness; use ic_types::{ Height, PrincipalId, Randomness, SubnetId, batch::{ @@ -167,7 +166,7 @@ pub(crate) fn deliver_batches_with_result_processor( } } - let randomness = Randomness::from(crypto_hashable_to_seed(&tape)); + let randomness = crypto_hashable_to_randomness(&tape); // Retrieve the dkg summary block let Some(summary_block) = pool.dkg_summary_block_for_finalized_height(height) else { diff --git a/rs/consensus/utils/src/lib.rs b/rs/consensus/utils/src/lib.rs index f2a53912dbd8..15bea05d21bb 100644 --- a/rs/consensus/utils/src/lib.rs +++ b/rs/consensus/utils/src/lib.rs @@ -14,7 +14,7 @@ use ic_types::{ Height, NodeId, RegistryVersion, ReplicaVersion, SubnetId, consensus::{Block, BlockProposal, HasCommittee, HasHeight, HasRank, Threshold}, crypto::{ - CryptoHash, CryptoHashable, Signed, + Signed, threshold_sig::ni_dkg::{NiDkgId, NiDkgReceivers, NiDkgTag, NiDkgTranscript}, }, }; @@ -56,16 +56,6 @@ impl RoundRobin { } } -/// Convert a CryptoHashable into a 32 bytes which can be used to seed a RNG -pub fn crypto_hashable_to_seed(hashable: &T) -> [u8; 32] { - let hash = ic_types::crypto::crypto_hash(hashable); - let CryptoHash(hash_bytes) = hash.get(); - let mut seed = [0; 32]; // zero padded if digest is less than 32 bytes - let n = hash_bytes.len().min(32); - seed[0..n].copy_from_slice(&hash_bytes[0..n]); - seed -} - /// Return the validated block proposals with the lowest rank at height `h` that /// have not been disqualified, if there are any. Else, return an empty Vec. pub fn find_lowest_ranked_non_disqualified_proposals( diff --git a/rs/consensus/utils/src/membership.rs b/rs/consensus/utils/src/membership.rs index bc4afb327362..38a768f39b9e 100644 --- a/rs/consensus/utils/src/membership.rs +++ b/rs/consensus/utils/src/membership.rs @@ -80,7 +80,8 @@ impl Membership { // `sort_unstable` is effectively the same as `sort` but slightly more // efficient. node_ids.sort_unstable(); - let mut rng = Csprng::from_random_beacon_and_purpose(previous_beacon, purpose); + let seed = Csprng::seed_from_random_beacon(previous_beacon); + let mut rng = Csprng::from_seed_and_purpose(seed, purpose); node_ids.shuffle(&mut rng); Ok(node_ids) } diff --git a/rs/crypto/prng/src/lib.rs b/rs/crypto/prng/src/lib.rs index b10027cf9c85..c59405c1a91b 100644 --- a/rs/crypto/prng/src/lib.rs +++ b/rs/crypto/prng/src/lib.rs @@ -1,9 +1,9 @@ //! Offers cryptographically secure pseudorandom number generation (CSPRNG). use RandomnessPurpose::*; use ic_crypto_internal_seed::Seed; +use ic_types::Randomness; use ic_types::consensus::RandomBeacon; -use ic_types::crypto::CryptoHashable; -use ic_types::{Randomness, crypto::crypto_hash}; +use ic_types::crypto::crypto_hashable_to_randomness; use rand::{CryptoRng, Error, RngCore}; use rand_chacha::ChaCha20Rng; use std::fmt; @@ -30,34 +30,23 @@ pub struct Csprng { } impl Csprng { - /// Creates a CSPRNG from the given random beacon for the given purpose. - pub fn from_random_beacon_and_purpose( - random_beacon: &RandomBeacon, - purpose: &RandomnessPurpose, - ) -> Self { - let seed = Self::seed_from_crypto_hashable(random_beacon); - let seed_for_purpose = seed.derive(&purpose.domain_separator()); - Csprng::from_seed(seed_for_purpose) - } - /// Creates a CSPRNG from the given seed for the given purpose. - pub fn from_seed_and_purpose(seed: &Randomness, purpose: &RandomnessPurpose) -> Self { - let seed = Seed::from_bytes(&seed.get()); + pub fn from_seed_and_purpose(seed: Seed, purpose: &RandomnessPurpose) -> Self { let seed_for_purpose = seed.derive(&purpose.domain_separator()); - Csprng::from_seed(seed_for_purpose) - } - - /// Creates a CSPRNG from the given seed. - fn from_seed(seed: ic_crypto_internal_seed::Seed) -> Self { Csprng { - rng: seed.into_rng(), + rng: seed_for_purpose.into_rng(), } } - /// Creates a CSPRNG seed from the given crypto hashable. - fn seed_from_crypto_hashable(crypto_hashable: &T) -> Seed { - let hash = crypto_hash(crypto_hashable); - Seed::from_bytes(&hash.get().0) + /// Creates a seed from the given randomness. + pub fn seed_from_randomness(randomness: &Randomness) -> Seed { + Seed::from_bytes(&randomness.get()) + } + + /// Creates a seed from the given random beacon. + pub fn seed_from_random_beacon(random_beacon: &RandomBeacon) -> Seed { + let randomness = crypto_hashable_to_randomness(random_beacon); + Csprng::seed_from_randomness(&randomness) } } diff --git a/rs/crypto/prng/src/tests.rs b/rs/crypto/prng/src/tests.rs index c6c7ad08962d..72d0f0205f44 100644 --- a/rs/crypto/prng/src/tests.rs +++ b/rs/crypto/prng/src/tests.rs @@ -30,23 +30,21 @@ fn should_use_unique_domain_separator_per_randomness_purpose() { #[test] fn should_incorporate_crypto_hash_domain_when_generating_randomness_for_random_beacon() { - // Because the crypto hash domain of random beacons is hardcoded and cannot be - // controlled from within a test, the only way to ensure that the crypto - // hash domain of the random beacon is incorporated when generating the - // randomness is to test the actual expected implementation. + // Verify that the seed derivation from a random beacon incorporates the crypto + // hash domain by checking that different random beacons produce different seeds. + + let rb1 = fake_random_beacon(1); + let rb2 = fake_random_beacon(2); - let rb = fake_random_beacon(1); for purpose in RandomnessPurpose::iter() { - // Replicate the implementation: hash the random beacon, create a seed, derive for purpose - let hash = ic_types::crypto::crypto_hash(&rb); - let seed = ic_crypto_internal_seed::Seed::from_bytes(&hash.get().0); - let seed_for_purpose = seed.derive(&purpose.domain_separator()); - let mut csprng = Csprng::from_seed(seed_for_purpose); + let seed1 = Csprng::seed_from_random_beacon(&rb1); + let seed2 = Csprng::seed_from_random_beacon(&rb2); - assert_eq!( - Csprng::from_random_beacon_and_purpose(&rb, &purpose).next_u32(), - csprng.next_u32() - ) + let mut csprng1 = Csprng::from_seed_and_purpose(seed1, &purpose); + let mut csprng2 = Csprng::from_seed_and_purpose(seed2, &purpose); + + // Different random beacons should produce different randomness + assert_ne!(csprng1.next_u32(), csprng2.next_u32()); } } @@ -61,11 +59,12 @@ fn fake_dkg_id(h: u64) -> NiDkgId { #[test] fn should_produce_different_randomness_for_execution_thread_edge_cases() { - let seed = ic_types::Randomness::new([99; 32]); + let randomness = ic_types::Randomness::new([99; 32]); + let seed = Csprng::seed_from_randomness(&randomness); - let mut rng_0 = Csprng::from_seed_and_purpose(&seed, &ExecutionThread(0)); - let mut rng_max = Csprng::from_seed_and_purpose(&seed, &ExecutionThread(u32::MAX)); - let mut rng_1 = Csprng::from_seed_and_purpose(&seed, &ExecutionThread(1)); + let mut rng_0 = Csprng::from_seed_and_purpose(seed.clone(), &ExecutionThread(0)); + let mut rng_max = Csprng::from_seed_and_purpose(seed.clone(), &ExecutionThread(u32::MAX)); + let mut rng_1 = Csprng::from_seed_and_purpose(seed, &ExecutionThread(1)); let val_0 = rng_0.next_u32(); let val_max = rng_max.next_u32(); @@ -77,6 +76,31 @@ fn should_produce_different_randomness_for_execution_thread_edge_cases() { assert_ne!(val_max, val_1); } +#[test] +fn seed_from_random_beacon_should_match_manual_extraction_via_crypto_hashable_to_randomness() { + let rb = fake_random_beacon(42); + + // Direct path: seed_from_random_beacon + let seed_direct = Csprng::seed_from_random_beacon(&rb); + + // Manual path: crypto_hashable_to_randomness -> seed_from_randomness + let randomness = crypto_hashable_to_randomness(&rb); + let seed_manual = Csprng::seed_from_randomness(&randomness); + + // Both seeds should produce the same CSPRNG output for all purposes + for purpose in RandomnessPurpose::iter() { + let mut rng_direct = Csprng::from_seed_and_purpose(seed_direct.clone(), &purpose); + let mut rng_manual = Csprng::from_seed_and_purpose(seed_manual.clone(), &purpose); + + assert_eq!( + rng_direct.next_u64(), + rng_manual.next_u64(), + "Mismatch for purpose {:?}", + purpose + ); + } +} + fn fake_random_beacon(height: u64) -> RandomBeacon { Signed { content: RandomBeaconContent::new( diff --git a/rs/crypto/prng/tests/tests.rs b/rs/crypto/prng/tests/tests.rs index 948b9a8e5af2..52dfa8776fc1 100644 --- a/rs/crypto/prng/tests/tests.rs +++ b/rs/crypto/prng/tests/tests.rs @@ -43,19 +43,21 @@ fn should_produce_deterministic_randomness_from_random_beacon_and_purpose() { fix_replica_version(); let random_beacon = fake_random_beacon(1); + let seed = Csprng::seed_from_random_beacon(&random_beacon); - let mut rng = Csprng::from_random_beacon_and_purpose(&random_beacon, &BlockmakerRanking); + let mut rng = Csprng::from_seed_and_purpose(seed, &BlockmakerRanking); assert_eq!(rng.next_u32(), 1_242_121_839); } #[test] -fn should_produce_deterministic_randomness_from_seed_and_purpose() { +fn should_produce_deterministic_randomness_from_randomness_and_purpose() { fix_replica_version(); - let seed = seed(); + let randomness = seed(); + let crypto_seed = Csprng::seed_from_randomness(&randomness); - let mut rng = Csprng::from_seed_and_purpose(&seed, &CommitteeSampling); + let mut rng = Csprng::from_seed_and_purpose(crypto_seed, &CommitteeSampling); assert_eq!(rng.next_u32(), 2_206_231_697); } @@ -63,9 +65,10 @@ fn should_produce_deterministic_randomness_from_seed_and_purpose() { #[test] fn should_offer_methods_of_rng_trait() { use rand::Rng; - let seed = seed(); + let randomness = seed(); + let crypto_seed = Csprng::seed_from_randomness(&randomness); - let mut rng = Csprng::from_seed_and_purpose(&seed, &CommitteeSampling); + let mut rng = Csprng::from_seed_and_purpose(crypto_seed, &CommitteeSampling); assert_eq!(rng.r#gen::(), 2_206_231_697); } @@ -75,10 +78,11 @@ fn should_generate_purpose_specific_randomness_for_random_beacon() { fix_replica_version(); let rb = random_beacon(); + let crypto_seed = Csprng::seed_from_random_beacon(&rb); - let mut rng_cs = Csprng::from_random_beacon_and_purpose(&rb, &CommitteeSampling); - let mut rng_br = Csprng::from_random_beacon_and_purpose(&rb, &BlockmakerRanking); - let mut rng_et = Csprng::from_random_beacon_and_purpose(&rb, &ExecutionThread(0)); + let mut rng_cs = Csprng::from_seed_and_purpose(crypto_seed.clone(), &CommitteeSampling); + let mut rng_br = Csprng::from_seed_and_purpose(crypto_seed.clone(), &BlockmakerRanking); + let mut rng_et = Csprng::from_seed_and_purpose(crypto_seed, &ExecutionThread(0)); let mut set = BTreeSet::new(); assert!(set.insert(rng_cs.next_u32())); @@ -91,11 +95,12 @@ fn should_generate_purpose_specific_randomness_for_random_beacon() { #[test] fn should_generate_purpose_specific_randomness_for_randomness_seed() { - let seed = seed(); + let randomness = seed(); + let crypto_seed = Csprng::seed_from_randomness(&randomness); - let mut rng_cs = Csprng::from_seed_and_purpose(&seed, &CommitteeSampling); - let mut rng_br = Csprng::from_seed_and_purpose(&seed, &BlockmakerRanking); - let mut rng_et = Csprng::from_seed_and_purpose(&seed, &ExecutionThread(0)); + let mut rng_cs = Csprng::from_seed_and_purpose(crypto_seed.clone(), &CommitteeSampling); + let mut rng_br = Csprng::from_seed_and_purpose(crypto_seed.clone(), &BlockmakerRanking); + let mut rng_et = Csprng::from_seed_and_purpose(crypto_seed, &ExecutionThread(0)); let mut set = BTreeSet::new(); assert!(set.insert(rng_cs.next_u32())); @@ -114,20 +119,26 @@ fn should_produce_different_randomness_for_same_purpose_for_different_random_bea assert_ne!(rb1, rb2); let purpose = CommitteeSampling; - let mut csprng1 = Csprng::from_random_beacon_and_purpose(&rb1, &purpose); - let mut csprng2 = Csprng::from_random_beacon_and_purpose(&rb2, &purpose); + let seed1 = Csprng::seed_from_random_beacon(&rb1); + let seed2 = Csprng::seed_from_random_beacon(&rb2); + + let mut csprng1 = Csprng::from_seed_and_purpose(seed1, &purpose); + let mut csprng2 = Csprng::from_seed_and_purpose(seed2, &purpose); assert_ne!(csprng1.next_u32(), csprng2.next_u32()); } #[test] fn should_produce_different_randomness_for_same_purpose_for_different_randomness_seeds() { - let (s1, s2) = (seed(), seed_2()); - assert_ne!(s1, s2); + let (r1, r2) = (seed(), seed_2()); + assert_ne!(r1, r2); let purpose = CommitteeSampling; - let mut csprng1 = Csprng::from_seed_and_purpose(&s1, &purpose); - let mut csprng2 = Csprng::from_seed_and_purpose(&s2, &purpose); + let seed1 = Csprng::seed_from_randomness(&r1); + let seed2 = Csprng::seed_from_randomness(&r2); + + let mut csprng1 = Csprng::from_seed_and_purpose(seed1, &purpose); + let mut csprng2 = Csprng::from_seed_and_purpose(seed2, &purpose); assert_ne!(csprng1.next_u32(), csprng2.next_u32()); } @@ -137,23 +148,27 @@ fn should_produce_different_randomness_for_different_execution_threads_for_rando fix_replica_version(); let rb = random_beacon(); + let crypto_seed = Csprng::seed_from_random_beacon(&rb); let (thread_1, thread_2) = (1, 2); assert_ne!(thread_1, thread_2); - let mut csprng1 = Csprng::from_random_beacon_and_purpose(&rb, &ExecutionThread(thread_1)); - let mut csprng2 = Csprng::from_random_beacon_and_purpose(&rb, &ExecutionThread(thread_2)); + let mut csprng1 = + Csprng::from_seed_and_purpose(crypto_seed.clone(), &ExecutionThread(thread_1)); + let mut csprng2 = Csprng::from_seed_and_purpose(crypto_seed, &ExecutionThread(thread_2)); assert_ne!(csprng1.next_u32(), csprng2.next_u32()); } #[test] fn should_produce_different_randomness_for_different_execution_threads_for_randomness_seed() { - let seed = seed(); + let randomness = seed(); + let crypto_seed = Csprng::seed_from_randomness(&randomness); let (thread_1, thread_2) = (1, 2); assert_ne!(thread_1, thread_2); - let mut csprng1 = Csprng::from_seed_and_purpose(&seed, &ExecutionThread(thread_1)); - let mut csprng2 = Csprng::from_seed_and_purpose(&seed, &ExecutionThread(thread_2)); + let mut csprng1 = + Csprng::from_seed_and_purpose(crypto_seed.clone(), &ExecutionThread(thread_1)); + let mut csprng2 = Csprng::from_seed_and_purpose(crypto_seed, &ExecutionThread(thread_2)); assert_ne!(csprng1.next_u32(), csprng2.next_u32()); } diff --git a/rs/execution_environment/src/scheduler.rs b/rs/execution_environment/src/scheduler.rs index 4cc6722b8d0f..bf4106332ea4 100644 --- a/rs/execution_environment/src/scheduler.rs +++ b/rs/execution_environment/src/scheduler.rs @@ -1300,8 +1300,9 @@ impl Scheduler for SchedulerImpl { // passing the number of scheduler cores is ok. It would need to be // updated in case the execution of subnet messages is running across // many threads to ensure a unique execution thread id. + let seed = Csprng::seed_from_randomness(&randomness); csprng = Csprng::from_seed_and_purpose( - &randomness, + seed, &ExecutionThread(self.config.scheduler_cores as u32), ); diff --git a/rs/execution_environment/src/scheduler/test_utilities.rs b/rs/execution_environment/src/scheduler/test_utilities.rs index 41369a42d82e..e46a8e432ddb 100644 --- a/rs/execution_environment/src/scheduler/test_utilities.rs +++ b/rs/execution_environment/src/scheduler/test_utilities.rs @@ -559,8 +559,9 @@ impl SchedulerTest { pub fn drain_subnet_messages(&mut self) -> ReplicatedState { let state = self.state.take().unwrap(); let compute_allocation_used = state.total_compute_allocation(); + let seed = Csprng::seed_from_randomness(&Randomness::from([0; 32])); let mut csprng = Csprng::from_seed_and_purpose( - &Randomness::from([0; 32]), + seed, &ExecutionThread(self.scheduler.config.scheduler_cores as u32), ); let mut round_limits = RoundLimits { diff --git a/rs/execution_environment/src/scheduler/threshold_signatures.rs b/rs/execution_environment/src/scheduler/threshold_signatures.rs index 40bc787db093..960d0699501e 100644 --- a/rs/execution_environment/src/scheduler/threshold_signatures.rs +++ b/rs/execution_environment/src/scheduler/threshold_signatures.rs @@ -656,7 +656,7 @@ mod tests { vec![], pre_signature_stashes, &mut Csprng::from_seed_and_purpose( - &Randomness::new([1; 32]), + Csprng::seed_from_randomness(&Randomness::new([1; 32])), &ic_crypto_prng::RandomnessPurpose::ExecutionThread(1), ), &RegistryExecutionSettings { diff --git a/rs/replay/src/player.rs b/rs/replay/src/player.rs index f1c98ef062fa..727fbdac7632 100644 --- a/rs/replay/src/player.rs +++ b/rs/replay/src/player.rs @@ -13,10 +13,7 @@ use ic_artifact_pool::{ use ic_config::{Config, artifact_pool::ArtifactPoolConfig, subnet_config::SubnetConfig}; use ic_consensus::consensus::batch_delivery::deliver_batches; use ic_consensus_certification::VerifierImpl; -use ic_consensus_utils::{ - crypto_hashable_to_seed, lookup_replica_version, membership::Membership, - pool_reader::PoolReader, -}; +use ic_consensus_utils::{lookup_replica_version, membership::Membership, pool_reader::PoolReader}; use ic_crypto_for_verification_only::CryptoComponentForVerificationOnly; use ic_error_types::UserError; use ic_execution_environment::ExecutionServices; @@ -56,6 +53,7 @@ use ic_registry_transport::{ serialize_get_value_request, }; use ic_state_manager::StateManagerImpl; +use ic_types::crypto::crypto_hashable_to_randomness; use ic_types::{ CryptoHashOfPartialState, CryptoHashOfState, Height, NodeId, PrincipalId, Randomness, RegistryVersion, ReplicaVersion, SubnetId, Time, UserId, @@ -759,7 +757,7 @@ impl Player { ( last_block.context.registry_version, last_block.context.time + Duration::from_nanos(1), - Randomness::from(crypto_hashable_to_seed(&last_block)), + crypto_hashable_to_randomness(&last_block), last_block.version.clone(), ) } diff --git a/rs/types/types/src/crypto.rs b/rs/types/types/src/crypto.rs index d052a746663b..9e5621214dbb 100644 --- a/rs/types/types/src/crypto.rs +++ b/rs/types/types/src/crypto.rs @@ -9,6 +9,7 @@ pub use hash::CryptoHashDomain; pub use hash::CryptoHashable; pub use hash::CryptoHashableTestDummy; pub use hash::crypto_hash; +pub use hash::crypto_hashable_to_randomness; mod sign; diff --git a/rs/types/types/src/crypto/hash.rs b/rs/types/types/src/crypto/hash.rs index 5ea26a0955b4..84c8ddb55b9f 100644 --- a/rs/types/types/src/crypto/hash.rs +++ b/rs/types/types/src/crypto/hash.rs @@ -1,5 +1,6 @@ //! Defines hash types. +use crate::Randomness; use crate::canister_http::{ CanisterHttpResponse, CanisterHttpResponseMetadata, CanisterHttpResponseShare, }; @@ -449,3 +450,14 @@ pub fn crypto_hash(data: &T) -> CryptoHashOf { data.hash(&mut hash); CryptoHashOf::new(CryptoHash(hash.finish().to_vec())) } + +/// Derives `Randomness` from a `CryptoHashable` value using a domain-separated hash. +/// +/// This function computes a cryptographic hash of the input data using the same +/// domain separation as `crypto_hash`, then converts the resulting hash bytes +/// into `Randomness`. +pub fn crypto_hashable_to_randomness(data: &T) -> Randomness { + let mut hash = Sha256::new_with_context(&DomainSeparationContext::new(data.domain())); + data.hash(&mut hash); + Randomness::from(hash.finish()) +} From 30caaff2daaa66d54b2be49e0e8373220508fef2 Mon Sep 17 00:00:00 2001 From: Andrea C Date: Thu, 29 Jan 2026 09:24:00 +0000 Subject: [PATCH 2/3] Change definition of crypto hashable to randomness --- rs/types/types/src/crypto/hash.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rs/types/types/src/crypto/hash.rs b/rs/types/types/src/crypto/hash.rs index 84c8ddb55b9f..925338b05c1c 100644 --- a/rs/types/types/src/crypto/hash.rs +++ b/rs/types/types/src/crypto/hash.rs @@ -455,9 +455,15 @@ pub fn crypto_hash(data: &T) -> CryptoHashOf { /// /// This function computes a cryptographic hash of the input data using the same /// domain separation as `crypto_hash`, then converts the resulting hash bytes -/// into `Randomness`. +/// into `Randomness`. If the hash is less than 32 bytes, the remaining bytes +/// are padded with zeros. If the hash is greater than 32 bytes, the remaining +/// bytes are truncated. pub fn crypto_hashable_to_randomness(data: &T) -> Randomness { - let mut hash = Sha256::new_with_context(&DomainSeparationContext::new(data.domain())); - data.hash(&mut hash); - Randomness::from(hash.finish()) + let hash = crypto_hash(data); + let CryptoHash(hash_bytes) = hash.get(); + let mut randomness = [0; 32]; // zero padded if digest is less than 32 bytes + let n = hash_bytes.len().min(32); + randomness[0..n].copy_from_slice(&hash_bytes[0..n]); + + Randomness::from(randomness) } From 609aa9b15ed14abb7ff0e971ad323a7fb238d288 Mon Sep 17 00:00:00 2001 From: IDX GitHub Automation Date: Thu, 29 Jan 2026 09:27:00 +0000 Subject: [PATCH 3/3] Automatically fixing code for linting and formatting issues --- rs/types/types/src/crypto/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rs/types/types/src/crypto/hash.rs b/rs/types/types/src/crypto/hash.rs index 925338b05c1c..4d5e5c46c47e 100644 --- a/rs/types/types/src/crypto/hash.rs +++ b/rs/types/types/src/crypto/hash.rs @@ -455,7 +455,7 @@ pub fn crypto_hash(data: &T) -> CryptoHashOf { /// /// This function computes a cryptographic hash of the input data using the same /// domain separation as `crypto_hash`, then converts the resulting hash bytes -/// into `Randomness`. If the hash is less than 32 bytes, the remaining bytes +/// into `Randomness`. If the hash is less than 32 bytes, the remaining bytes /// are padded with zeros. If the hash is greater than 32 bytes, the remaining /// bytes are truncated. pub fn crypto_hashable_to_randomness(data: &T) -> Randomness {