use crate::prelude::*;
use crate::traits::{KeyImageGen, Link, Sign, Verify};
use curve25519_dalek::constants;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::MultiscalarMul;
use digest::generic_array::typenum::U64;
use digest::Digest;
use rand_core::{CryptoRng, RngCore};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone)]
pub struct BLSAG {
pub challenge: Scalar,
pub responses: Vec<Scalar>,
pub ring: Vec<RistrettoPoint>,
pub key_image: RistrettoPoint,
}
impl KeyImageGen<Scalar, RistrettoPoint> for BLSAG {
fn generate_key_image<Hash: Digest<OutputSize = U64> + Clone + Default>(
k: Scalar,
) -> RistrettoPoint {
let k_point: RistrettoPoint = k * constants::RISTRETTO_BASEPOINT_POINT;
let key_image: RistrettoPoint = k * RistrettoPoint::from_hash(
Hash::default().chain_update(k_point.compress().as_bytes()),
);
key_image
}
}
impl Sign<Scalar, Vec<RistrettoPoint>> for BLSAG {
fn sign<
Hash: Digest<OutputSize = U64> + Clone + Default,
CSPRNG: CryptoRng + RngCore + Default,
>(
k: Scalar,
mut ring: Vec<RistrettoPoint>,
secret_index: usize,
message: &[u8],
) -> BLSAG {
let mut csprng = CSPRNG::default();
let k_point: RistrettoPoint = k * constants::RISTRETTO_BASEPOINT_POINT;
let key_image: RistrettoPoint = BLSAG::generate_key_image::<Hash>(k);
let n = ring.len() + 1;
ring.insert(secret_index, k_point);
let a: Scalar = Scalar::random(&mut csprng);
let mut rs: Vec<Scalar> = (0..n).map(|_| Scalar::random(&mut csprng)).collect();
let mut cs: Vec<Scalar> = (0..n).map(|_| Scalar::ZERO).collect();
let mut message_hash = Hash::default();
message_hash.update(message);
let mut hashes: Vec<Hash> = (0..n).map(|_| message_hash.clone()).collect();
hashes[(secret_index + 1) % n].update(
(a * constants::RISTRETTO_BASEPOINT_POINT)
.compress()
.as_bytes(),
);
hashes[(secret_index + 1) % n].update(
(a * RistrettoPoint::from_hash(
Hash::default().chain_update(k_point.compress().as_bytes()),
))
.compress()
.as_bytes(),
);
cs[(secret_index + 1) % n] = Scalar::from_hash(hashes[(secret_index + 1) % n].clone());
let mut i = (secret_index + 1) % n;
loop {
hashes[(i + 1) % n].update(
RistrettoPoint::multiscalar_mul(
&[rs[i % n], cs[i % n]],
&[constants::RISTRETTO_BASEPOINT_POINT, ring[i % n]],
)
.compress()
.as_bytes(),
);
hashes[(i + 1) % n].update(
RistrettoPoint::multiscalar_mul(
&[rs[i % n], cs[i % n]],
&[
RistrettoPoint::from_hash(
Hash::default().chain_update(ring[i % n].compress().as_bytes()),
),
key_image,
],
)
.compress()
.as_bytes(),
);
cs[(i + 1) % n] = Scalar::from_hash(hashes[(i + 1) % n].clone());
if (secret_index >= 1 && i % n == (secret_index - 1) % n)
|| (secret_index == 0 && i % n == n - 1)
{
break;
} else {
i = (i + 1) % n;
}
}
rs[secret_index] = a - (cs[secret_index] * k);
BLSAG {
challenge: cs[0],
responses: rs,
ring,
key_image,
}
}
}
impl Verify for BLSAG {
fn verify<Hash: Digest<OutputSize = U64> + Clone + Default>(
signature: BLSAG,
message: &[u8],
) -> bool {
let mut reconstructed_c: Scalar = signature.challenge;
let n = signature.ring.len();
for j in 0..n {
let mut h: Hash = Hash::default();
h.update(message);
h.update(
RistrettoPoint::multiscalar_mul(
&[signature.responses[j], reconstructed_c],
&[constants::RISTRETTO_BASEPOINT_POINT, signature.ring[j]],
)
.compress()
.as_bytes(),
);
h.update(
RistrettoPoint::multiscalar_mul(
&[signature.responses[j], reconstructed_c],
&[
RistrettoPoint::from_hash(
Hash::default().chain_update(signature.ring[j].compress().as_bytes()),
),
signature.key_image,
],
)
.compress()
.as_bytes(),
);
reconstructed_c = Scalar::from_hash(h);
}
signature.challenge == reconstructed_c
}
}
impl Link for BLSAG {
fn link(signature_1: BLSAG, signature_2: BLSAG) -> bool {
signature_1.key_image == signature_2.key_image
}
}
#[cfg(test)]
#[cfg(feature = "std")]
mod test {
extern crate blake2;
extern crate rand;
extern crate sha2;
extern crate sha3;
use super::*;
use blake2::Blake2b512;
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::scalar::Scalar;
use rand::rngs::OsRng;
use sha2::Sha512;
use sha3::Keccak512;
#[test]
fn blsag() {
let mut csprng = OsRng::default();
let k: Scalar = Scalar::random(&mut csprng);
let secret_index = 1;
let n = 2;
let ring: Vec<RistrettoPoint> = (0..(n - 1)) .map(|_| RistrettoPoint::random(&mut csprng))
.collect();
let message: Vec<u8> = b"This is the message".iter().cloned().collect();
{
let signature = BLSAG::sign::<Sha512, OsRng>(k, ring.clone(), secret_index, &message);
let result = BLSAG::verify::<Sha512>(signature, &message);
assert!(result);
}
{
let signature =
BLSAG::sign::<Keccak512, OsRng>(k, ring.clone(), secret_index, &message);
let result = BLSAG::verify::<Keccak512>(signature, &message);
assert!(result);
}
{
let signature =
BLSAG::sign::<Blake2b512, OsRng>(k, ring.clone(), secret_index, &message);
let result = BLSAG::verify::<Blake2b512>(signature, &message);
assert!(result);
}
let another_ring: Vec<RistrettoPoint> =
(0..(n - 1)) .map(|_| RistrettoPoint::random(&mut csprng))
.collect();
let another_message: Vec<u8> = b"This is another message".iter().cloned().collect();
let signature_1 = BLSAG::sign::<Blake2b512, OsRng>(
k,
another_ring.clone(),
secret_index,
&another_message,
);
let signature_2 = BLSAG::sign::<Blake2b512, OsRng>(k, ring.clone(), secret_index, &message);
let result = BLSAG::link(signature_1, signature_2);
assert!(result);
}
}