use curve25519_dalek::ristretto::CompressedRistretto;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use crate::member::*;
#[derive(Debug)]
pub struct Signature {
pub challenge: Scalar,
pub responses: Vec<Scalar>,
pub key_images: Vec<CompressedRistretto>,
}
pub enum Error {
IncorrectNumOfResponses,
IncorrectNumOfPubKeys,
BadKeyImages,
ChallengeMismatch,
MemberError(String),
}
impl From<crate::member::Error> for Error {
fn from(e: crate::member::Error) -> Error {
let err_string = format!(" underlying member error {:?}", e);
Error::MemberError(err_string)
}
}
impl Signature {
pub fn verify(
&self,
compressed_pub_keys: &mut Vec<Vec<CompressedRistretto>>,
msg: &[u8],
) -> Result<(), Error> {
let num_key_images = self.key_images.len();
let num_responses = self.responses.len();
if num_responses % num_key_images != 0 {
return Err(Error::IncorrectNumOfResponses);
}
if compressed_pub_keys.len() != (num_responses / num_key_images) {
return Err(Error::IncorrectNumOfPubKeys);
}
let mut decompressed_keys: Vec<Vec<RistrettoPoint>> = Vec::new();
for compressed_key_set in compressed_pub_keys {
decompressed_keys.push(self.decompress_keys(&compressed_key_set))
}
let chunked_responses: Vec<_> = self.responses.chunks(num_key_images).collect();
let decomp_key_images = self.decompress_key_images()?;
let mut challenge = self.challenge.clone();
for (pub_keys, responses) in decompressed_keys.iter().zip(chunked_responses.iter()) {
challenge =
compute_challenge_ring(pub_keys, &challenge, &decomp_key_images, responses, msg);
}
if self.challenge != challenge {
return Err(Error::ChallengeMismatch);
}
Ok(())
}
fn decompress_key_images(&self) -> Result<Vec<RistrettoPoint>, Error> {
let mut decompressed_key_images = Vec::with_capacity(self.key_images.len());
for key_image in self.key_images.iter() {
let dec_key_image = key_image.decompress().ok_or(Error::BadKeyImages)?;
decompressed_key_images.push(dec_key_image);
}
Ok(decompressed_key_images)
}
fn decompress_keys(&self, compressed_keys: &Vec<CompressedRistretto>) -> Vec<RistrettoPoint> {
let mut decompressed = Vec::with_capacity(compressed_keys.len());
for key in compressed_keys {
decompressed.push(key.decompress().unwrap());
}
decompressed
}
}
#[cfg(test)]
mod test {
extern crate test;
use crate::tests_helper::*;
use test::Bencher;
use crate::constants;
use rand::seq::SliceRandom;
use rand::thread_rng;
#[test]
fn test_verify_fail_shuffle_keys() {
let num_keys = 2;
let num_decoys = 11;
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();
pub_keys.shuffle(&mut thread_rng());
assert!(sig.verify(&mut pub_keys, msg).is_err());
}
#[test]
fn test_verify_fail_incorrect_num_keys() {
let num_keys = 2;
let num_decoys = 11;
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();
pub_keys.push(vec![constants::BASEPOINT.compress()]);
assert!(sig.verify(&mut pub_keys, msg).is_err());
pub_keys.remove(pub_keys.len() - 1);
assert!(sig.verify(&mut pub_keys, msg).is_ok());
pub_keys.remove(pub_keys.len() - 1);
assert!(sig.verify(&mut pub_keys, msg).is_err());
}
#[bench]
fn bench_verify(b: &mut Bencher) {
let num_keys = 2;
let num_decoys = 11;
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));
}
}