Skip to content

Commit

Permalink
fix: Make public announcement encryption deterministic
Browse files Browse the repository at this point in the history
In order to reuse proofs of previous runs in tests, we need to
have deterministic public announcements. So I came up with a way to
derive this deterministically and, still, cryptographically
secure.
  • Loading branch information
Sword-Smith committed Oct 11, 2024
1 parent e044769 commit e0f4481
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 15 deletions.
20 changes: 20 additions & 0 deletions src/models/state/wallet/address/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down
12 changes: 3 additions & 9 deletions src/models/state/wallet/address/generation_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -175,15 +174,10 @@ impl GenerationReceivingAddress {
}

pub fn encrypt(&self, utxo: &Utxo, sender_randomness: Digest) -> Vec<BFieldElement> {
// 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();

Expand Down
9 changes: 3 additions & 6 deletions src/models/state/wallet/address/symmetric_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ 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;
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;
Expand Down Expand Up @@ -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<BFieldElement> {
// 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

Expand Down

0 comments on commit e0f4481

Please sign in to comment.