This commit is contained in:
Luke Parker
2025-08-08 23:57:38 -04:00
parent 387615705c
commit bb726b58af

View File

@@ -89,8 +89,8 @@ impl ClsagContext {
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum Mode { enum Mode {
Sign(usize, EdwardsPoint, EdwardsPoint), Sign { signer_index: u8, A: EdwardsPoint, AH: EdwardsPoint },
Verify(Scalar), Verify { c1: Scalar, D_serialized: EdwardsPoint },
} }
// Core of the CLSAG algorithm, applicable to both sign and verify with minimal differences // Core of the CLSAG algorithm, applicable to both sign and verify with minimal differences
@@ -101,17 +101,17 @@ fn core(
I: &EdwardsPoint, I: &EdwardsPoint,
pseudo_out: &EdwardsPoint, pseudo_out: &EdwardsPoint,
msg_hash: &[u8; 32], msg_hash: &[u8; 32],
D: &EdwardsPoint, D_torsion_free: &EdwardsPoint,
s: &[Scalar], s: &[Scalar],
A_c1: &Mode, A_c1: &Mode,
) -> ((EdwardsPoint, Scalar, Scalar), Scalar) { ) -> ((EdwardsPoint, Scalar, Scalar), Scalar) {
let n = ring.len(); let n = ring.len();
let images_precomp = match A_c1 { let images_precomp = match A_c1 {
Mode::Sign(..) => None, Mode::Sign { .. } => None,
Mode::Verify(..) => Some(VartimeEdwardsPrecomputation::new([I, D])), Mode::Verify { .. } => Some(VartimeEdwardsPrecomputation::new([I, D_torsion_free])),
}; };
let D_INV_EIGHT = D * INV_EIGHT(); let D_inv_eight = D_torsion_free * INV_EIGHT();
// Generate the transcript // Generate the transcript
// Instead of generating multiple, a single transcript is created and then edited as needed // Instead of generating multiple, a single transcript is created and then edited as needed
@@ -140,7 +140,14 @@ fn core(
} }
to_hash.extend(I.compress().to_bytes()); to_hash.extend(I.compress().to_bytes());
to_hash.extend(D_INV_EIGHT.compress().to_bytes()); match A_c1 {
Mode::Sign { .. } => {
to_hash.extend(D_inv_eight.compress().to_bytes());
}
Mode::Verify { D_serialized, .. } => {
to_hash.extend(D_serialized.compress().to_bytes());
}
}
to_hash.extend(pseudo_out.compress().to_bytes()); to_hash.extend(pseudo_out.compress().to_bytes());
// mu_P with agg_0 // mu_P with agg_0
let mu_P = keccak256_to_scalar(&to_hash); let mu_P = keccak256_to_scalar(&to_hash);
@@ -163,15 +170,16 @@ fn core(
let end; let end;
let mut c; let mut c;
match A_c1 { match A_c1 {
Mode::Sign(r, A, AH) => { Mode::Sign { signer_index, A, AH } => {
start = r + 1; let signer_index = usize::from(*signer_index);
end = r + n; start = signer_index + 1;
end = signer_index + n;
to_hash.extend(A.compress().to_bytes()); to_hash.extend(A.compress().to_bytes());
to_hash.extend(AH.compress().to_bytes()); to_hash.extend(AH.compress().to_bytes());
c = keccak256_to_scalar(&to_hash); c = keccak256_to_scalar(&to_hash);
} }
Mode::Verify(c1) => { Mode::Verify { c1, .. } => {
start = 0; start = 0;
end = n; end = n;
c = *c1; c = *c1;
@@ -186,10 +194,10 @@ fn core(
// (s_i * G) + (c_p * P_i) + (c_c * C_i) // (s_i * G) + (c_p * P_i) + (c_c * C_i)
let L = match A_c1 { let L = match A_c1 {
Mode::Sign(..) => { Mode::Sign { .. } => {
EdwardsPoint::multiscalar_mul([s[i], c_p, c_c], [ED25519_BASEPOINT_POINT, P[i], C[i]]) EdwardsPoint::multiscalar_mul([s[i], c_p, c_c], [ED25519_BASEPOINT_POINT, P[i], C[i]])
} }
Mode::Verify(..) => { Mode::Verify { .. } => {
G_PRECOMP().vartime_mixed_multiscalar_mul([s[i]], [c_p, c_c], [P[i], C[i]]) G_PRECOMP().vartime_mixed_multiscalar_mul([s[i]], [c_p, c_c], [P[i], C[i]])
} }
}; };
@@ -198,8 +206,10 @@ fn core(
// (c_p * I) + (c_c * D) + (s_i * PH) // (c_p * I) + (c_c * D) + (s_i * PH)
let R = match A_c1 { let R = match A_c1 {
Mode::Sign(..) => EdwardsPoint::multiscalar_mul([c_p, c_c, s[i]], [I, D, &PH]), Mode::Sign { .. } => {
Mode::Verify(..) => images_precomp EdwardsPoint::multiscalar_mul([c_p, c_c, s[i]], [I, D_torsion_free, &PH])
}
Mode::Verify { .. } => images_precomp
.as_ref() .as_ref()
.expect("value populated when verifying wasn't populated") .expect("value populated when verifying wasn't populated")
.vartime_mixed_multiscalar_mul([c_p, c_c], [s[i]], [PH]), .vartime_mixed_multiscalar_mul([c_p, c_c], [s[i]], [PH]),
@@ -217,7 +227,7 @@ fn core(
} }
// This first tuple is needed to continue signing, the latter is the c to be tested/worked with // This first tuple is needed to continue signing, the latter is the c to be tested/worked with
((D_INV_EIGHT, c * mu_P, c * mu_C), c1) ((D_inv_eight, c * mu_P, c * mu_C), c1)
} }
/// The CLSAG signature, as used in Monero. /// The CLSAG signature, as used in Monero.
@@ -250,19 +260,26 @@ impl Clsag {
A: EdwardsPoint, A: EdwardsPoint,
AH: EdwardsPoint, AH: EdwardsPoint,
) -> ClsagSignCore { ) -> ClsagSignCore {
let r: usize = input.decoys.signer_index().into(); let signer_index = input.decoys.signer_index();
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate(); let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
let mask_delta = input.commitment.mask - mask; let mask_delta = input.commitment.mask - mask;
let H = hash_to_point(input.decoys.ring()[r][0].compress().0); let H = hash_to_point(input.decoys.ring()[usize::from(signer_index)][0].compress().0);
let D = H * mask_delta; let D = H * mask_delta;
let mut s = Vec::with_capacity(input.decoys.ring().len()); let mut s = Vec::with_capacity(input.decoys.ring().len());
for _ in 0 .. input.decoys.ring().len() { for _ in 0 .. input.decoys.ring().len() {
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(
core(input.decoys.ring(), I, &pseudo_out, msg_hash, &D, &s, &Mode::Sign(r, A, AH)); input.decoys.ring(),
I,
&pseudo_out,
msg_hash,
&D,
&s,
&Mode::Sign { signer_index, A, AH },
);
ClsagSignCore { ClsagSignCore {
incomplete_clsag: Clsag { D, s, c1 }, incomplete_clsag: Clsag { D, s, c1 },
@@ -379,12 +396,20 @@ impl Clsag {
Err(ClsagError::InvalidImage)?; Err(ClsagError::InvalidImage)?;
} }
let D = self.D.mul_by_cofactor(); let D_torsion_free = self.D.mul_by_cofactor();
if D.is_identity() { if D_torsion_free.is_identity() {
Err(ClsagError::InvalidD)?; Err(ClsagError::InvalidD)?;
} }
let (_, c1) = core(ring, I, pseudo_out, msg_hash, &D, &self.s, &Mode::Verify(self.c1)); let (_, c1) = core(
ring,
I,
pseudo_out,
msg_hash,
&D_torsion_free,
&self.s,
&Mode::Verify { c1: self.c1, D_serialized: self.D },
);
if c1 != self.c1 { if c1 != self.c1 {
Err(ClsagError::InvalidC1)?; Err(ClsagError::InvalidC1)?;
} }