Remove DLEq proofs from CLSAG multisig

1) Removes the key image DLEq on the Monero side of things, as the produced
   signature share serves as a DLEq for it.
2) Removes the nonce DLEqs from modular-frost as they're unnecessary for
   monero-serai. Updates documentation accordingly.

Without the proof the nonces are internally consistent, the produced signatures
from modular-frost can be argued as a batch-verifiable CP93 DLEq (R0, R1, s),
or as a GSP for the CP93 DLEq statement (which naturally produces (R0, R1, s)).

The lack of proving the nonces consistent does make the process weaker, yet
it's also unnecessary for the class of protocols this is intended to service.
To provide DLEqs for the nonces would be to provide PoKs for the nonce
commitments (in the traditional Schnorr case).
This commit is contained in:
Luke Parker
2024-04-21 22:50:07 -04:00
parent 558a2bfa46
commit a25e6330bd
12 changed files with 131 additions and 306 deletions

View File

@@ -12,7 +12,7 @@ use crate::{
/// Tests for the nonce handling code.
pub mod nonces;
use nonces::{test_multi_nonce, test_invalid_commitment, test_invalid_dleq_proof};
use nonces::test_multi_nonce;
/// Vectorized test suite to ensure consistency.
pub mod vectors;
@@ -267,6 +267,4 @@ pub fn test_ciphersuite<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut
test_schnorr_blame::<R, C, H>(rng);
test_multi_nonce::<R, C>(rng);
test_invalid_commitment::<R, C>(rng);
test_invalid_dleq_proof::<R, C>(rng);
}

View File

@@ -9,14 +9,12 @@ use transcript::{Transcript, RecommendedTranscript};
use ciphersuite::group::{ff::Field, Group, GroupEncoding};
use dleq::MultiDLEqProof;
pub use dkg::tests::{key_gen, recover_key};
use crate::{
Curve, Participant, ThresholdView, ThresholdKeys, FrostError,
algorithm::Algorithm,
sign::{Writable, SignMachine},
tests::{algorithm_machines, preprocess, sign},
tests::{algorithm_machines, sign},
};
#[derive(Clone)]
@@ -157,75 +155,3 @@ pub fn test_multi_nonce<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
let machines = algorithm_machines(&mut *rng, &MultiNonce::<C>::new(), &keys);
sign(&mut *rng, &MultiNonce::<C>::new(), keys.clone(), machines, &[]);
}
/// Test malleating a commitment for a nonce across generators causes the preprocess to error.
pub fn test_invalid_commitment<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
let keys = key_gen::<R, C>(&mut *rng);
let machines = algorithm_machines(&mut *rng, &MultiNonce::<C>::new(), &keys);
let (machines, mut preprocesses) = preprocess(&mut *rng, machines, |_, _| {});
// Select a random participant to give an invalid commitment
let participants = preprocesses.keys().collect::<Vec<_>>();
let faulty = *participants
[usize::try_from(rng.next_u64() % u64::try_from(participants.len()).unwrap()).unwrap()];
// Grab their preprocess
let mut preprocess = preprocesses.remove(&faulty).unwrap();
// Mutate one of the commitments
let nonce =
preprocess.commitments.nonces.get_mut(usize::try_from(rng.next_u64()).unwrap() % 2).unwrap();
let generators_len = nonce.generators.len();
nonce.generators[usize::try_from(rng.next_u64()).unwrap() % generators_len].0
[usize::try_from(rng.next_u64()).unwrap() % 2] = C::G::random(&mut *rng);
// The commitments are validated at time of deserialization (read_preprocess)
// Accordingly, serialize it and read it again to make sure that errors
assert!(machines
.iter()
.next()
.unwrap()
.1
.read_preprocess::<&[u8]>(&mut preprocess.serialize().as_ref())
.is_err());
}
/// Test malleating the DLEq proof for a preprocess causes it to error.
pub fn test_invalid_dleq_proof<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
let keys = key_gen::<R, C>(&mut *rng);
let machines = algorithm_machines(&mut *rng, &MultiNonce::<C>::new(), &keys);
let (machines, mut preprocesses) = preprocess(&mut *rng, machines, |_, _| {});
// Select a random participant to give an invalid DLEq proof
let participants = preprocesses.keys().collect::<Vec<_>>();
let faulty = *participants
[usize::try_from(rng.next_u64() % u64::try_from(participants.len()).unwrap()).unwrap()];
// Invalidate it by replacing it with a completely different proof
let dlogs = [Zeroizing::new(C::F::random(&mut *rng)), Zeroizing::new(C::F::random(&mut *rng))];
let mut preprocess = preprocesses.remove(&faulty).unwrap();
preprocess.commitments.dleq = Some(MultiDLEqProof::prove(
&mut *rng,
&mut RecommendedTranscript::new(b"Invalid DLEq Proof"),
&nonces::<C>(),
&dlogs,
));
assert!(machines
.iter()
.next()
.unwrap()
.1
.read_preprocess::<&[u8]>(&mut preprocess.serialize().as_ref())
.is_err());
// Also test None for a proof will cause an error
preprocess.commitments.dleq = None;
assert!(machines
.iter()
.next()
.unwrap()
.1
.read_preprocess::<&[u8]>(&mut preprocess.serialize().as_ref())
.is_err());
}

View File

@@ -14,7 +14,7 @@ use ciphersuite::group::{ff::PrimeField, GroupEncoding};
use crate::{
curve::Curve,
Participant, ThresholdCore, ThresholdKeys,
algorithm::{IetfTranscript, Hram, IetfSchnorr},
algorithm::{Hram, IetfSchnorr},
sign::{
Writable, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Preprocess,
PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine,
@@ -191,7 +191,6 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
nonces: vec![NonceCommitments {
generators: vec![GeneratorCommitments(these_commitments)],
}],
dleq: None,
},
addendum: (),
};
@@ -301,12 +300,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
}
// Also test it at the Commitments level
let (generated_nonces, commitments) = Commitments::<C>::new::<_, IetfTranscript>(
&mut TransparentRng(randomness),
&share,
&[vec![C::generator()]],
&[],
);
let (generated_nonces, commitments) =
Commitments::<C>::new::<_>(&mut TransparentRng(randomness), &share, &[vec![C::generator()]]);
assert_eq!(generated_nonces.len(), 1);
assert_eq!(generated_nonces[0].0, [nonces[0].clone(), nonces[1].clone()]);