From b426bfcfe89c09436ff3a1dad1eee1bb3746c30d Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Wed, 23 Jul 2025 08:42:04 -0400 Subject: [PATCH] Respond to 1.1 A1 --- networks/monero/ringct/clsag/src/lib.rs | 22 ++++++++++++-------- networks/monero/ringct/clsag/src/multisig.rs | 12 +++++------ networks/monero/ringct/clsag/src/tests.rs | 8 +++---- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/networks/monero/ringct/clsag/src/lib.rs b/networks/monero/ringct/clsag/src/lib.rs index 0aab537b..454c34fd 100644 --- a/networks/monero/ringct/clsag/src/lib.rs +++ b/networks/monero/ringct/clsag/src/lib.rs @@ -100,7 +100,7 @@ fn core( ring: &[[EdwardsPoint; 2]], I: &EdwardsPoint, pseudo_out: &EdwardsPoint, - msg: &[u8; 32], + msg_hash: &[u8; 32], D: &EdwardsPoint, s: &[Scalar], A_c1: &Mode, @@ -156,7 +156,7 @@ fn core( // Unfortunately, it's I D pseudo_out instead of pseudo_out I D, meaning this needs to be // truncated just to add it back to_hash.extend(pseudo_out.compress().to_bytes()); - to_hash.extend(msg); + to_hash.extend(msg_hash); // Configure the loop based on if we're signing or verifying let start; @@ -245,7 +245,7 @@ impl Clsag { I: &EdwardsPoint, input: &ClsagContext, mask: Scalar, - msg: &[u8; 32], + msg_hash: &[u8; 32], A: EdwardsPoint, AH: EdwardsPoint, ) -> ClsagSignCore { @@ -261,7 +261,7 @@ impl Clsag { s.push(Scalar::random(rng)); } let ((D, c_p, c_c), c1) = - core(input.decoys.ring(), I, &pseudo_out, msg, &D, &s, &Mode::Sign(r, A, AH)); + core(input.decoys.ring(), I, &pseudo_out, msg_hash, &D, &s, &Mode::Sign(r, A, AH)); ClsagSignCore { incomplete_clsag: Clsag { D, s, c1 }, @@ -288,11 +288,15 @@ impl Clsag { /// `inputs` is of the form (discrete logarithm of the key, context). /// /// `sum_outputs` is for the sum of the output commitments' masks. + /// + /// WARNING: This follows the Fiat-Shamir transcript format used by the Monero protocol, which + /// makes assumptions on what has already been transcripted and bound to within `msg_hash`. Do + /// not use this if you don't know what you're doing. pub fn sign( rng: &mut R, mut inputs: Vec<(Zeroizing, ClsagContext)>, sum_outputs: Scalar, - msg: [u8; 32], + msg_hash: [u8; 32], ) -> Result, ClsagError> { // Create the key images let mut key_image_generators = vec![]; @@ -329,7 +333,7 @@ impl Clsag { &key_images[i], &inputs[i].1, mask, - &msg, + &msg_hash, nonce.deref() * ED25519_BASEPOINT_TABLE, nonce.deref() * key_image_generators[i], ); @@ -345,7 +349,7 @@ impl Clsag { nonce.zeroize(); debug_assert!(clsag - .verify(inputs[i].1.decoys.ring(), &key_images[i], &pseudo_out, &msg) + .verify(inputs[i].1.decoys.ring(), &key_images[i], &pseudo_out, &msg_hash) .is_ok()); res.push((clsag, pseudo_out)); @@ -360,7 +364,7 @@ impl Clsag { ring: &[[EdwardsPoint; 2]], I: &EdwardsPoint, pseudo_out: &EdwardsPoint, - msg: &[u8; 32], + msg_hash: &[u8; 32], ) -> Result<(), ClsagError> { // Preliminary checks // s, c1, and points must also be encoded canonically, which is checked at time of decode @@ -379,7 +383,7 @@ impl Clsag { Err(ClsagError::InvalidD)?; } - let (_, c1) = core(ring, I, pseudo_out, msg, &D, &self.s, &Mode::Verify(self.c1)); + let (_, c1) = core(ring, I, pseudo_out, msg_hash, &D, &self.s, &Mode::Verify(self.c1)); if c1 != self.c1 { Err(ClsagError::InvalidC1)?; } diff --git a/networks/monero/ringct/clsag/src/multisig.rs b/networks/monero/ringct/clsag/src/multisig.rs index bfbb8fc5..8fdb73f2 100644 --- a/networks/monero/ringct/clsag/src/multisig.rs +++ b/networks/monero/ringct/clsag/src/multisig.rs @@ -132,7 +132,7 @@ pub struct ClsagMultisig { mask_recv: Option, mask: Option, - msg: Option<[u8; 32]>, + msg_hash: Option<[u8; 32]>, interim: Option, } @@ -156,7 +156,7 @@ impl ClsagMultisig { mask_recv: Some(mask_recv), mask: None, - msg: None, + msg_hash: None, interim: None, }, mask_send, @@ -253,7 +253,7 @@ impl Algorithm for ClsagMultisig { view: &ThresholdView, nonce_sums: &[Vec], nonces: Vec>, - msg: &[u8], + msg_hash: &[u8], ) -> dfg::Scalar { // Use the transcript to get a seeded random number generator // @@ -264,14 +264,14 @@ impl Algorithm for ClsagMultisig { // opening of the commitment being re-randomized (and what it's re-randomized to) let mut rng = ChaCha20Rng::from_seed(self.transcript.rng_seed(b"decoy_responses")); - self.msg = Some(msg.try_into().expect("CLSAG message should be 32-bytes")); + self.msg_hash = Some(msg_hash.try_into().expect("CLSAG message hash should be 32-bytes")); let sign_core = Clsag::sign_core( &mut rng, &self.image.expect("verifying a share despite never processing any addendums").0, &self.context, self.mask.expect("mask wasn't set"), - self.msg.as_ref().unwrap(), + self.msg_hash.as_ref().unwrap(), nonce_sums[0][0].0, nonce_sums[0][1].0, ); @@ -303,7 +303,7 @@ impl Algorithm for ClsagMultisig { self.context.decoys.ring(), &self.image.expect("verifying a signature despite never processing any addendums").0, &interim.pseudo_out, - self.msg.as_ref().unwrap(), + self.msg_hash.as_ref().unwrap(), ) .is_ok() { diff --git a/networks/monero/ringct/clsag/src/tests.rs b/networks/monero/ringct/clsag/src/tests.rs index ba71d69c..d4ae1f41 100644 --- a/networks/monero/ringct/clsag/src/tests.rs +++ b/networks/monero/ringct/clsag/src/tests.rs @@ -31,7 +31,7 @@ const RING_INDEX: u8 = 3; #[test] fn clsag() { for real in 0 .. RING_LEN { - let msg = [1; 32]; + let msg_hash = [1; 32]; let mut secrets = (Zeroizing::new(Scalar::ZERO), Scalar::ZERO); let mut ring = vec![]; @@ -61,18 +61,18 @@ fn clsag() { .unwrap(), )], Scalar::random(&mut OsRng), - msg, + msg_hash, ) .unwrap() .swap_remove(0); let image = hash_to_point((ED25519_BASEPOINT_TABLE * secrets.0.deref()).compress().0) * secrets.0.deref(); - clsag.verify(&ring, &image, &pseudo_out, &msg).unwrap(); + clsag.verify(&ring, &image, &pseudo_out, &msg_hash).unwrap(); // make sure verification fails if we throw a random `c1` at it. clsag.c1 = Scalar::random(&mut OsRng); - assert!(clsag.verify(&ring, &image, &pseudo_out, &msg).is_err()); + assert!(clsag.verify(&ring, &image, &pseudo_out, &msg_hash).is_err()); } }