use crate::PublicKey;
use bls12_381_plus::{
multi_miller_loop, G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, Scalar,
};
use core::ops::BitOr;
use digest::Update;
use group::{Curve, Group, GroupEncoding};
use serde::{Deserialize, Serialize};
use signature_core::{constants::*, lib::*};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PokSignatureProof {
pub(crate) sigma_1: G1Projective,
pub(crate) sigma_2: G1Projective,
pub(crate) commitment: G2Projective,
#[serde(with = "VecSerializer")]
pub(crate) proof: Vec<Challenge, 130>,
}
impl PokSignatureProof {
pub fn to_bytes(&self, buffer: &mut [u8]) {
buffer[0..COMMITMENT_BYTES].copy_from_slice(&self.sigma_1.to_affine().to_compressed());
let mut offset = COMMITMENT_BYTES;
let mut end = offset + COMMITMENT_BYTES;
buffer[offset..end].copy_from_slice(&self.sigma_2.to_affine().to_compressed());
offset = end;
end += 2 * COMMITMENT_BYTES;
buffer[offset..end].copy_from_slice(&self.commitment.to_affine().to_compressed());
offset = end;
end += FIELD_BYTES;
for i in 0..self.proof.len() {
buffer[offset..end].copy_from_slice(&self.proof[i].to_bytes());
offset = end;
end += FIELD_BYTES;
}
}
pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Option<Self> {
let size = FIELD_BYTES * 3 + COMMITMENT_BYTES * 4;
let buffer = bytes.as_ref();
if buffer.len() < size {
return None;
}
if buffer.len() % FIELD_BYTES != 0 {
return None;
}
let hid_msg_cnt = (buffer.len() - COMMITMENT_BYTES * 4) / FIELD_BYTES;
let mut offset = COMMITMENT_BYTES;
let mut end = COMMITMENT_BYTES + COMMITMENT_BYTES;
let sigma_1 = G1Affine::from_compressed(slicer!(buffer, 0, offset, COMMITMENT_BYTES))
.map(G1Projective::from);
let sigma_2 = G1Affine::from_compressed(slicer!(buffer, offset, end, COMMITMENT_BYTES))
.map(G1Projective::from);
offset = end;
end += 2 * COMMITMENT_BYTES;
let commitment =
G2Affine::from_compressed(slicer!(buffer, offset, end, 2 * COMMITMENT_BYTES))
.map(G2Projective::from);
if sigma_1.is_none().unwrap_u8() == 1
|| sigma_2.is_none().unwrap_u8() == 1
|| commitment.is_none().unwrap_u8() == 1
{
return None;
}
offset = end;
end += FIELD_BYTES;
let mut proof = Vec::<Challenge, 130>::new();
for _ in 0..hid_msg_cnt {
let c = Challenge::from_bytes(slicer!(buffer, offset, end, FIELD_BYTES));
offset = end;
end = offset + FIELD_BYTES;
if c.is_none().unwrap_u8() == 1 {
return None;
}
proof.push(c.unwrap()).expect(ALLOC_MSG);
}
Some(Self {
sigma_1: sigma_1.unwrap(),
sigma_2: sigma_2.unwrap(),
commitment: commitment.unwrap(),
proof,
})
}
pub fn add_challenge_contribution(
&self,
public_key: &PublicKey,
rvl_msgs: &[(usize, Message)],
challenge: Challenge,
hasher: &mut impl Update,
) {
hasher.update(self.sigma_1.to_affine().to_uncompressed());
hasher.update(self.sigma_2.to_affine().to_uncompressed());
hasher.update(self.commitment.to_affine().to_uncompressed());
let mut points = Vec::<G2Projective, 130>::new();
points.push(G2Projective::generator()).expect(ALLOC_MSG);
points.push(public_key.w).expect(ALLOC_MSG);
let mut known = HashSet::new();
for (idx, _) in rvl_msgs {
known.insert(*idx);
}
for i in 0..public_key.y.len() {
if known.contains(&i) {
continue;
}
points.push(public_key.y[i]).expect(ALLOC_MSG);
}
points.push(self.commitment).expect(ALLOC_MSG);
let mut scalars: Vec<Scalar, 130> = self.proof.iter().map(|p| p.0).collect();
scalars.push(-challenge.0).expect(ALLOC_MSG);
let commitment = G2Projective::sum_of_products_in_place(points.as_ref(), scalars.as_mut());
hasher.update(commitment.to_affine().to_bytes());
}
pub fn verify(&self, rvl_msgs: &[(usize, Message)], public_key: &PublicKey) -> bool {
if self
.sigma_1
.is_identity()
.bitor(self.sigma_2.is_identity())
.unwrap_u8()
== 1
{
return false;
}
if public_key.y.len() < rvl_msgs.len() {
return false;
}
let mut points = Vec::<G2Projective, 130>::new();
let mut scalars = Vec::<Scalar, 130>::new();
for (idx, msg) in rvl_msgs {
if *idx > public_key.y.len() {
return false;
}
points.push(public_key.y[*idx]).expect(ALLOC_MSG);
scalars.push(msg.0).expect(ALLOC_MSG);
}
points.push(public_key.x).expect(ALLOC_MSG);
scalars.push(Scalar::one()).expect(ALLOC_MSG);
points.push(self.commitment).expect(ALLOC_MSG);
scalars.push(Scalar::one()).expect(ALLOC_MSG);
let j = G2Projective::sum_of_products_in_place(points.as_ref(), scalars.as_mut());
multi_miller_loop(&[
(&self.sigma_1.to_affine(), &G2Prepared::from(j.to_affine())),
(
&self.sigma_2.to_affine(),
&G2Prepared::from(-G2Affine::generator()),
),
])
.final_exponentiation()
.is_identity()
.unwrap_u8()
== 1
}
}