use crate::member::Member;
use crate::signature::Signature;
use curve25519_dalek::ristretto::CompressedRistretto;
use curve25519_dalek::scalar::Scalar;
#[derive(Debug)]
pub enum Error {
NoSigner,
NotEnoughMembers,
NumberOfKeysMismatch,
MoreThanOneSigner,
DuplicateKeysExist,
UnderlyingErr(String),
}
impl From<crate::member::Error> for crate::mlsag::Error {
fn from(e: crate::member::Error) -> crate::mlsag::Error {
match e {
crate::member::Error::NotASigner => Error::UnderlyingErr(String::from(
"Tried to use a method specific to a signer in the mlsag module",
)),
crate::member::Error::NotADecoy => Error::UnderlyingErr(String::from(
"Tried to use a method specific to a decoy in the mlsag module",
)),
}
}
}
pub struct Mlsag {
members: Vec<Member>,
}
impl Mlsag {
pub fn new() -> Self {
Mlsag {
members: Vec::new(),
}
}
pub fn add_member(&mut self, member: Member) {
self.members.push(member);
}
pub fn public_keys(&self) -> Vec<Vec<CompressedRistretto>> {
self.members
.iter()
.map(|member| member.public_set.compress())
.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 key_images = signer.compute_key_images()?;
let mut challenge = signer.compute_challenge_commitment(msg)?;
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, msg)?;
all_challenges.push(challenge);
}
let mut signers_responses = signer.compute_signer_responses(challenge)?;
let mut all_responses: Vec<Scalar> = Vec::with_capacity(num_members * signer.num_keys());
for member in &self.members {
match member.is_signer() {
true => {
all_responses.append(&mut signers_responses);
}
false => {
let mut mem_response = member.responses.clone();
all_responses.append(&mut mem_response);
}
}
}
all_challenges.reverse();
let first_challenge = all_challenges[signer_index];
let compressed_key_images = key_images.into_iter().map(|x| x.compress()).collect();
Ok(Signature {
challenge: first_challenge,
responses: all_responses,
key_images: compressed_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(())
}
}
#[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 mlsag = generate_mlsag_with(num_decoys, num_keys);
let msg = b"hello world";
match mlsag.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"),
}
mlsag.add_member(generate_signer(num_keys));
mlsag.add_member(generate_signer(num_keys));
match mlsag.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"),
}
mlsag = generate_mlsag_with(num_decoys, num_keys);
mlsag.add_member(generate_decoy(num_keys + 1));
mlsag.add_member(generate_signer(num_keys));
match mlsag.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"),
};
mlsag = generate_mlsag_with(num_decoys, num_keys);
mlsag.add_member(generate_signer(num_keys));
let first_member = &mut mlsag.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 mlsag.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 mlsag = generate_mlsag_with(num_decoys, num_keys);
let msg = b"hello world";
mlsag.add_member(generate_signer(num_keys));
let signature = mlsag.sign(msg).unwrap();
assert_eq!(num_keys, signature.key_images.len());
let num_members = num_decoys + 1;
assert_eq!(num_members * num_keys, signature.responses.len());
}
macro_rules! param_bench_verify {
($func_name: ident,$num_keys:expr, $num_decoys :expr) => {
#[bench]
fn $func_name(b: &mut Bencher) {
let num_keys = $num_keys;
let num_decoys = $num_decoys;
let msg = b"hello world";
let mut mlsag = generate_mlsag_with(num_decoys, num_keys);
mlsag.add_member(generate_signer(num_keys));
let sig = mlsag.sign(msg).unwrap();
let mut pub_keys = mlsag.public_keys();
b.iter(|| sig.verify(&mut pub_keys, msg));
}
};
}
param_bench_verify!(bench_verify_2, 2, 2);
param_bench_verify!(bench_verify_4, 2, 3);
param_bench_verify!(bench_verify_6, 2, 5);
param_bench_verify!(bench_verify_8, 2, 7);
param_bench_verify!(bench_verify_11, 2, 10);
param_bench_verify!(bench_verify_16, 2, 15);
param_bench_verify!(bench_verify_32, 2, 31);
param_bench_verify!(bench_verify_64, 2, 63);
param_bench_verify!(bench_verify_128, 2, 127);
param_bench_verify!(bench_verify_256, 2, 255);
param_bench_verify!(bench_verify_512, 2, 511);
}