Respond to 1.1 A1

This commit is contained in:
Luke Parker
2025-07-23 08:42:04 -04:00
parent 21ce50ecf7
commit b426bfcfe8
3 changed files with 23 additions and 19 deletions

View File

@@ -100,7 +100,7 @@ fn core(
ring: &[[EdwardsPoint; 2]], ring: &[[EdwardsPoint; 2]],
I: &EdwardsPoint, I: &EdwardsPoint,
pseudo_out: &EdwardsPoint, pseudo_out: &EdwardsPoint,
msg: &[u8; 32], msg_hash: &[u8; 32],
D: &EdwardsPoint, D: &EdwardsPoint,
s: &[Scalar], s: &[Scalar],
A_c1: &Mode, 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 // Unfortunately, it's I D pseudo_out instead of pseudo_out I D, meaning this needs to be
// truncated just to add it back // truncated just to add it back
to_hash.extend(pseudo_out.compress().to_bytes()); 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 // Configure the loop based on if we're signing or verifying
let start; let start;
@@ -245,7 +245,7 @@ impl Clsag {
I: &EdwardsPoint, I: &EdwardsPoint,
input: &ClsagContext, input: &ClsagContext,
mask: Scalar, mask: Scalar,
msg: &[u8; 32], msg_hash: &[u8; 32],
A: EdwardsPoint, A: EdwardsPoint,
AH: EdwardsPoint, AH: EdwardsPoint,
) -> ClsagSignCore { ) -> ClsagSignCore {
@@ -261,7 +261,7 @@ impl Clsag {
s.push(Scalar::random(rng)); s.push(Scalar::random(rng));
} }
let ((D, c_p, c_c), c1) = 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 { ClsagSignCore {
incomplete_clsag: Clsag { D, s, c1 }, incomplete_clsag: Clsag { D, s, c1 },
@@ -288,11 +288,15 @@ impl Clsag {
/// `inputs` is of the form (discrete logarithm of the key, context). /// `inputs` is of the form (discrete logarithm of the key, context).
/// ///
/// `sum_outputs` is for the sum of the output commitments' masks. /// `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<R: RngCore + CryptoRng>( pub fn sign<R: RngCore + CryptoRng>(
rng: &mut R, rng: &mut R,
mut inputs: Vec<(Zeroizing<Scalar>, ClsagContext)>, mut inputs: Vec<(Zeroizing<Scalar>, ClsagContext)>,
sum_outputs: Scalar, sum_outputs: Scalar,
msg: [u8; 32], msg_hash: [u8; 32],
) -> Result<Vec<(Clsag, EdwardsPoint)>, ClsagError> { ) -> Result<Vec<(Clsag, EdwardsPoint)>, ClsagError> {
// Create the key images // Create the key images
let mut key_image_generators = vec![]; let mut key_image_generators = vec![];
@@ -329,7 +333,7 @@ impl Clsag {
&key_images[i], &key_images[i],
&inputs[i].1, &inputs[i].1,
mask, mask,
&msg, &msg_hash,
nonce.deref() * ED25519_BASEPOINT_TABLE, nonce.deref() * ED25519_BASEPOINT_TABLE,
nonce.deref() * key_image_generators[i], nonce.deref() * key_image_generators[i],
); );
@@ -345,7 +349,7 @@ impl Clsag {
nonce.zeroize(); nonce.zeroize();
debug_assert!(clsag 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()); .is_ok());
res.push((clsag, pseudo_out)); res.push((clsag, pseudo_out));
@@ -360,7 +364,7 @@ impl Clsag {
ring: &[[EdwardsPoint; 2]], ring: &[[EdwardsPoint; 2]],
I: &EdwardsPoint, I: &EdwardsPoint,
pseudo_out: &EdwardsPoint, pseudo_out: &EdwardsPoint,
msg: &[u8; 32], msg_hash: &[u8; 32],
) -> Result<(), ClsagError> { ) -> Result<(), ClsagError> {
// Preliminary checks // Preliminary checks
// s, c1, and points must also be encoded canonically, which is checked at time of decode // 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)?; 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 { if c1 != self.c1 {
Err(ClsagError::InvalidC1)?; Err(ClsagError::InvalidC1)?;
} }

View File

@@ -132,7 +132,7 @@ pub struct ClsagMultisig {
mask_recv: Option<ClsagMultisigMaskReceiver>, mask_recv: Option<ClsagMultisigMaskReceiver>,
mask: Option<Scalar>, mask: Option<Scalar>,
msg: Option<[u8; 32]>, msg_hash: Option<[u8; 32]>,
interim: Option<Interim>, interim: Option<Interim>,
} }
@@ -156,7 +156,7 @@ impl ClsagMultisig {
mask_recv: Some(mask_recv), mask_recv: Some(mask_recv),
mask: None, mask: None,
msg: None, msg_hash: None,
interim: None, interim: None,
}, },
mask_send, mask_send,
@@ -253,7 +253,7 @@ impl Algorithm<Ed25519> for ClsagMultisig {
view: &ThresholdView<Ed25519>, view: &ThresholdView<Ed25519>,
nonce_sums: &[Vec<dfg::EdwardsPoint>], nonce_sums: &[Vec<dfg::EdwardsPoint>],
nonces: Vec<Zeroizing<dfg::Scalar>>, nonces: Vec<Zeroizing<dfg::Scalar>>,
msg: &[u8], msg_hash: &[u8],
) -> dfg::Scalar { ) -> dfg::Scalar {
// Use the transcript to get a seeded random number generator // Use the transcript to get a seeded random number generator
// //
@@ -264,14 +264,14 @@ impl Algorithm<Ed25519> for ClsagMultisig {
// opening of the commitment being re-randomized (and what it's re-randomized to) // 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")); 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( let sign_core = Clsag::sign_core(
&mut rng, &mut rng,
&self.image.expect("verifying a share despite never processing any addendums").0, &self.image.expect("verifying a share despite never processing any addendums").0,
&self.context, &self.context,
self.mask.expect("mask wasn't set"), 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][0].0,
nonce_sums[0][1].0, nonce_sums[0][1].0,
); );
@@ -303,7 +303,7 @@ impl Algorithm<Ed25519> for ClsagMultisig {
self.context.decoys.ring(), self.context.decoys.ring(),
&self.image.expect("verifying a signature despite never processing any addendums").0, &self.image.expect("verifying a signature despite never processing any addendums").0,
&interim.pseudo_out, &interim.pseudo_out,
self.msg.as_ref().unwrap(), self.msg_hash.as_ref().unwrap(),
) )
.is_ok() .is_ok()
{ {

View File

@@ -31,7 +31,7 @@ const RING_INDEX: u8 = 3;
#[test] #[test]
fn clsag() { fn clsag() {
for real in 0 .. RING_LEN { 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 secrets = (Zeroizing::new(Scalar::ZERO), Scalar::ZERO);
let mut ring = vec![]; let mut ring = vec![];
@@ -61,18 +61,18 @@ fn clsag() {
.unwrap(), .unwrap(),
)], )],
Scalar::random(&mut OsRng), Scalar::random(&mut OsRng),
msg, msg_hash,
) )
.unwrap() .unwrap()
.swap_remove(0); .swap_remove(0);
let image = let image =
hash_to_point((ED25519_BASEPOINT_TABLE * secrets.0.deref()).compress().0) * secrets.0.deref(); 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. // make sure verification fails if we throw a random `c1` at it.
clsag.c1 = Scalar::random(&mut OsRng); 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());
} }
} }