diff --git a/src/models/state/wallet/address/common.rs b/src/models/state/wallet/address/common.rs index 64bbe612..e0e24b53 100644 --- a/src/models/state/wallet/address/common.rs +++ b/src/models/state/wallet/address/common.rs @@ -12,6 +12,7 @@ use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::lock_script::LockScript; use crate::models::blockchain::transaction::lock_script::LockScriptAndWitness; +use crate::models::blockchain::transaction::utxo::Utxo; use crate::models::blockchain::transaction::PublicAnnouncement; use crate::prelude::twenty_first; @@ -20,6 +21,25 @@ pub fn derive_receiver_id(seed: Digest) -> BFieldElement { Hash::hash_varlen(&[seed.values().to_vec(), vec![BFieldElement::new(2)]].concat()).values()[0] } +/// Derive a seed and a nonce deterministically, in order to produce +/// deterministic public announcements, since these are needed to be able to +/// reuse proofs for tests. These values are used in the encryption +/// step. +pub fn deterministically_derive_seed_and_nonce( + utxo: &Utxo, + sender_randomness: Digest, +) -> ([u8; 32], BFieldElement) { + let combined = Tip5::hash_pair(sender_randomness, utxo.lock_script_hash); + let [e0, e1, e2, e3, e4] = combined.values(); + let e0: [u8; 8] = e0.into(); + let e1: [u8; 8] = e1.into(); + let e2: [u8; 8] = e2.into(); + let e3: [u8; 8] = e3.into(); + let seed: [u8; 32] = [e0, e1, e2, e3].concat().try_into().unwrap(); + + (seed, e4) +} + /// retrieves key-type field from a [PublicAnnouncement] /// /// returns an error if the field is not present diff --git a/src/models/state/wallet/address/generation_address.rs b/src/models/state/wallet/address/generation_address.rs index 56f47948..8aba00ac 100644 --- a/src/models/state/wallet/address/generation_address.rs +++ b/src/models/state/wallet/address/generation_address.rs @@ -21,8 +21,6 @@ use anyhow::Result; use bech32::FromBase32; use bech32::ToBase32; use bech32::Variant; -use rand::thread_rng; -use rand::Rng; use serde_derive::Deserialize; use serde_derive::Serialize; use twenty_first::math::b_field_element::BFieldElement; @@ -32,6 +30,7 @@ use twenty_first::math::tip5::Digest; use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; use super::common; +use super::common::deterministically_derive_seed_and_nonce; use crate::config_models::network::Network; use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::lock_script::LockScript; @@ -175,15 +174,10 @@ impl GenerationReceivingAddress { } pub fn encrypt(&self, utxo: &Utxo, sender_randomness: Digest) -> Vec { - // derive shared key - let mut randomness = [0u8; 32]; - let mut rng = thread_rng(); - rng.fill(&mut randomness); + let (randomness, nonce_bfe) = + deterministically_derive_seed_and_nonce(utxo, sender_randomness); let (shared_key, kem_ctxt) = lattice::kem::enc(self.encryption_key, randomness); - // sample nonce - let nonce_bfe: BFieldElement = rng.gen(); - // convert payload to bytes let plaintext = bincode::serialize(&(utxo, sender_randomness)).unwrap(); diff --git a/src/models/state/wallet/address/symmetric_key.rs b/src/models/state/wallet/address/symmetric_key.rs index c10414ce..c6733e8f 100644 --- a/src/models/state/wallet/address/symmetric_key.rs +++ b/src/models/state/wallet/address/symmetric_key.rs @@ -6,8 +6,6 @@ use aead::KeyInit; use aes_gcm::Aes256Gcm; use aes_gcm::Nonce; use anyhow::Result; -use rand::thread_rng; -use rand::Rng; use serde::Deserialize; use serde::Serialize; use twenty_first::math::b_field_element::BFieldElement; @@ -15,6 +13,7 @@ use twenty_first::math::tip5::Digest; use twenty_first::util_types::algebraic_hasher::AlgebraicHasher; use super::common; +use super::common::deterministically_derive_seed_and_nonce; use crate::models::blockchain::shared::Hash; use crate::models::blockchain::transaction::lock_script::LockScript; use crate::models::blockchain::transaction::lock_script::LockScriptAndWitness; @@ -145,12 +144,10 @@ impl SymmetricKey { /// The output of `encrypt()` should be used as the input to `decrypt()`. pub fn encrypt(&self, utxo: &Utxo, sender_randomness: Digest) -> Vec { // 1. init randomness - let mut randomness = [0u8; 32]; - let mut rng = thread_rng(); - rng.fill(&mut randomness); + let (_randomness, nonce_bfe) = + deterministically_derive_seed_and_nonce(utxo, sender_randomness); // 2. generate random nonce - let nonce_bfe: BFieldElement = rng.gen(); let nonce_as_bytes = [&nonce_bfe.value().to_be_bytes(), [0u8; 4].as_slice()].concat(); let nonce = Nonce::from_slice(&nonce_as_bytes); // almost 64 bits; unique per message