use crate::member::Member;
use crate::signature::Signature;
use curve25519_dalek::ristretto::CompressedRistretto;
use curve25519_dalek::scalar::Scalar;
use crate::transcript::TranscriptProtocol;
use merlin::Transcript;
#[derive(Debug)]
pub enum Error {
NoSigner,
NotEnoughMembers,
NumberOfKeysMismatch,
MoreThanOneSigner,
DuplicateKeysExist,
UnderlyingErr(String),
}
impl From<crate::member::Error> for crate::clsag::Error {
fn from(e: crate::member::Error) -> crate::clsag::Error {
match e {
crate::member::Error::NotASigner => Error::UnderlyingErr(String::from(
"Tried to use a method specific to a signer in the clsag module",
)),
crate::member::Error::NotADecoy => Error::UnderlyingErr(String::from(
"Tried to use a method specific to a decoy in the clsag module",
)),
}
}
}
pub struct Clsag {
members: Vec<Member>,
}
impl Clsag {
pub fn new() -> Self {
Clsag {
members: Vec::new(),
}
}
pub fn add_member(&mut self, member: Member) {
self.members.push(member);
}
pub fn public_keys_bytes(&self) -> Vec<u8> {
self.members
.iter()
.map(|member| member.public_set.to_bytes())
.flatten()
.collect()
}
pub fn public_keys(&self) -> Vec<Vec<CompressedRistretto>> {
self.members
.iter()
.map(|member| member.public_set.to_keys())
.collect()
}
pub fn sign(&self, msg: &[u8]) -> Result<Signature, Error> {
self.check_format()?;
let num_members = self.members.len();
let mut all_challenges: Vec<Scalar> = Vec::with_capacity(num_members);
let signer_index = self.find_signer()?;
let signer = &self.members[signer_index];
let pubkey_matrix = self.public_keys_bytes();
let key_images = signer.compute_key_images()?;
let aggregation_cooeff = calc_aggregation_coefficients(&pubkey_matrix, &key_images, msg);
let mut challenge = signer.compute_challenge_commitment(&pubkey_matrix)?;
all_challenges.push(challenge);
for decoy in self
.members
.iter()
.cycle()
.skip(signer_index + 1)
.take(num_members - 1)
{
challenge = decoy.compute_decoy_challenge(
&challenge,
&key_images,
&aggregation_cooeff,
&pubkey_matrix,
)?;
all_challenges.push(challenge);
}
let signers_response = signer.compute_signer_response(challenge, &aggregation_cooeff)?;
let mut all_responses: Vec<Scalar> = Vec::with_capacity(num_members);
for member in &self.members {
match member.is_signer() {
true => {
all_responses.push(signers_response);
}
false => {
let mem_response =
member
.response
.clone()
.ok_or(Error::UnderlyingErr(String::from(
"member does not have a response value",
)))?;
all_responses.push(mem_response);
}
}
}
all_challenges.reverse();
let first_challenge = all_challenges[signer_index];
Ok(Signature {
challenge: first_challenge,
responses: all_responses,
key_images: key_images,
})
}
pub fn find_signer(&self) -> Result<usize, Error> {
let signer_index = self
.members
.iter()
.position(|member| member.is_signer())
.ok_or(Error::NoSigner)?;
Ok(signer_index)
}
pub fn num_signers(&self) -> usize {
let signers: Vec<&Member> = self
.members
.iter()
.filter(|member| member.is_signer())
.collect();
signers.len()
}
fn check_format(&self) -> Result<(), Error> {
if self.members.len() < 2 {
return Err(Error::NotEnoughMembers);
}
let num_signers = self.num_signers();
match num_signers {
0 => return Err(Error::NoSigner),
1 => (),
_ => return Err(Error::MoreThanOneSigner),
};
let first_member_num_keys = self.members[0].num_keys();
let all_same_num_keys = self
.members
.iter()
.all(|member| member.num_keys() == first_member_num_keys);
if !all_same_num_keys {
return Err(Error::NumberOfKeysMismatch);
}
let no_duplicates_exists = self
.members
.iter()
.all(|member| !member.public_set.duplicates_exist());
if !no_duplicates_exists {
return Err(Error::DuplicateKeysExist);
}
Ok(())
}
}
pub fn calc_aggregation_coefficients(
pubkey_matrix: &[u8],
key_images: &[CompressedRistretto],
message: &[u8],
) -> Vec<Scalar> {
let key_images_bytes: Vec<u8> = key_images
.iter()
.map(|key_image| key_image.to_bytes().to_vec())
.flatten()
.collect();
let num_keys_per_user = key_images.len();
let mut agg_coef = Vec::with_capacity(num_keys_per_user);
for i in 0..num_keys_per_user {
let mut transcript = Transcript::new(b"clsag");
transcript.append_message(b"msg", message);
transcript.append_u64(b"", i as u64);
transcript.append_message(b"", pubkey_matrix);
transcript.append_message(b"", &key_images_bytes);
agg_coef.push(transcript.challenge_scalar(b""));
}
agg_coef
}
#[cfg(test)]
mod test {
extern crate test;
use super::*;
use crate::tests_helper::*;
use test::Bencher;
#[test]
fn test_check_format() {
let num_decoys = 10;
let num_keys = 3;
let mut clsag = generate_clsag_with(num_decoys, num_keys);
let msg = b"hello world";
match clsag.sign(msg) {
Ok(_) => panic!("expected an error as there is no signer in the ring"),
Err(Error::NoSigner) => {}
Err(_) => panic!("got an error, however we expected no signer error"),
}
clsag.add_member(generate_signer(num_keys));
clsag.add_member(generate_signer(num_keys));
match clsag.sign(msg) {
Ok(_) => panic!("expected an error as there are too many signers in the ring"),
Err(Error::MoreThanOneSigner) => {}
Err(_) => panic!("got an error, however we expected a more than one signer error"),
}
clsag = generate_clsag_with(num_decoys, num_keys);
clsag.add_member(generate_decoy(num_keys + 1));
clsag.add_member(generate_signer(num_keys));
match clsag.sign(msg) {
Ok(_) => {
panic!("expected an error as one member has more keys than another in the ring")
}
Err(Error::NumberOfKeysMismatch) => {}
Err(_) => panic!("got an error, however we expected a `number of keys mismatch` error"),
};
clsag = generate_clsag_with(num_decoys, num_keys);
clsag.add_member(generate_signer(num_keys));
let first_member = &mut clsag.members[0];
let first_member_last_element = &mut first_member.public_set.0.last().unwrap();
first_member.public_set.0[0] = first_member_last_element.clone();
match clsag.sign(msg) {
Ok(_) => panic!("expected an error as one member has a duplicate key"),
Err(Error::DuplicateKeysExist) => {}
Err(_) => panic!("got an error, however we expected a `duplicate keys` error"),
};
}
#[test]
fn test_sign_no_error() {
let num_decoys = 10;
let num_keys = 3;
let mut clsag = generate_clsag_with(num_decoys, num_keys);
let msg = b"hello world";
clsag.add_member(generate_signer(num_keys));
let signature = clsag.sign(msg).unwrap();
assert_eq!(num_keys, signature.key_images.len());
let num_members = num_decoys + 1;
assert_eq!(num_members, signature.responses.len());
}
#[bench]
fn bench_sign(b: &mut Bencher) {
let num_keys = 2;
let num_decoys = 11;
let msg = b"hello world";
let mut clsag = generate_clsag_with(num_decoys, num_keys);
clsag.add_member(generate_signer(num_keys));
b.iter(|| clsag.sign(msg));
}
}