credx/knox/bbs/
pok_signature_proof.rs

1use crate::error::Error;
2use crate::knox::bbs::PublicKey;
3use crate::knox::short_group_sig_core::short_group_traits::ProofOfSignatureKnowledge;
4use crate::CredxResult;
5use blsful::inner_types::{
6    multi_miller_loop, G1Affine, G2Affine, G2Prepared, MillerLoopResult, PrimeCurveAffine, Scalar,
7};
8use bulletproofs::inner_types::G1Projective;
9use elliptic_curve::group::Curve;
10use elliptic_curve::{Group, PrimeField};
11use merlin::Transcript;
12use serde::{Deserialize, Serialize};
13use std::collections::{BTreeMap, BTreeSet};
14
15/// The actual proof that is sent from prover to verifier.
16#[derive(Debug, Clone, Deserialize, Serialize)]
17pub struct PokSignatureProof {
18    pub(crate) a_bar: G1Projective,
19    pub(crate) b_bar: G1Projective,
20    pub(crate) t: G1Projective,
21    pub(crate) proof: Vec<Scalar>,
22}
23
24impl ProofOfSignatureKnowledge for PokSignatureProof {
25    type PublicKey = PublicKey;
26
27    fn add_proof_contribution(
28        &self,
29        _public_key: &Self::PublicKey,
30        _revealed_messages: &[(usize, Scalar)],
31        _challenge: Scalar,
32        transcript: &mut Transcript,
33    ) {
34        transcript.append_message(b"a_bar", self.a_bar.to_compressed().as_ref());
35        transcript.append_message(b"b_bar", self.b_bar.to_compressed().as_ref());
36        transcript.append_message(b"commitment", self.t.to_compressed().as_ref());
37    }
38
39    fn verify(
40        &self,
41        public_key: &Self::PublicKey,
42        revealed_messages: &[(usize, Scalar)],
43        challenge: Scalar,
44    ) -> CredxResult<()> {
45        if (self.a_bar.is_identity() | self.b_bar.is_identity() | self.t.is_identity()).into() {
46            return Err(Error::General("Invalid proof - identity"));
47        }
48        if public_key.is_invalid().into() {
49            return Err(Error::General("Invalid public key"));
50        }
51        let mut points = Vec::with_capacity(public_key.y.len() + 3);
52        let mut msgs = Vec::with_capacity(revealed_messages.len());
53        let mut known = BTreeSet::new();
54        for (idx, msg) in revealed_messages {
55            if *idx >= public_key.y.len() {
56                continue;
57            }
58            known.insert(*idx);
59            points.push(public_key.y[*idx]);
60            msgs.push(*msg);
61        }
62        let lhs = -G1Projective::sum_of_products(&points, &msgs) - G1Projective::GENERATOR;
63        points.clear();
64        msgs.clear();
65
66        for (idx, y) in public_key.y.iter().enumerate() {
67            if known.contains(&idx) {
68                continue;
69            }
70            points.push(*y);
71        }
72
73        points.push(self.a_bar);
74        points.push(self.b_bar);
75        points.push(lhs);
76        let mut scalars = self.proof.clone();
77        scalars.push(-challenge);
78        let commitment = G1Projective::sum_of_products(&points, &scalars);
79        if self.t != commitment {
80            return Err(Error::General("Invalid proof - invalid messages"));
81        }
82
83        let res = multi_miller_loop(&[
84            (
85                &self.a_bar.to_affine(),
86                &G2Prepared::from(public_key.w.to_affine()),
87            ),
88            (
89                &self.b_bar.to_affine(),
90                &G2Prepared::from(-G2Affine::generator()),
91            ),
92        ])
93        .final_exponentiation()
94        .is_identity()
95        .unwrap_u8()
96            == 1;
97
98        if res {
99            Ok(())
100        } else {
101            Err(Error::General("Invalid proof - signature proof"))
102        }
103    }
104
105    fn get_hidden_message_proofs(
106        &self,
107        public_key: &Self::PublicKey,
108        rvl_msgs: &[(usize, Scalar)],
109    ) -> CredxResult<BTreeMap<usize, Scalar>> {
110        if public_key.y.len() < rvl_msgs.len() {
111            return Err(Error::General("Proof error"));
112        }
113        if public_key.is_invalid().unwrap_u8() == 1u8 {
114            return Err(Error::General("Proof error"));
115        }
116
117        let mut hidden = BTreeMap::new();
118        let mut j = 0;
119        for i in 0..public_key.y.len() {
120            if j < rvl_msgs.len() && rvl_msgs[j].0 == i {
121                j += 1;
122                continue;
123            }
124            let message = self
125                .proof
126                .get(i - j)
127                .ok_or(Error::General("invalid proof"))?;
128            hidden.insert(i, *message);
129        }
130
131        Ok(hidden)
132    }
133}
134
135impl PokSignatureProof {
136    /// Store the proof as a sequence of bytes
137    pub fn to_bytes(&self) -> Vec<u8> {
138        let mut buffer = Vec::with_capacity(48 * 3 + 32 * self.proof.len());
139
140        buffer.extend_from_slice(&self.a_bar.to_compressed());
141        buffer.extend_from_slice(&self.b_bar.to_compressed());
142        buffer.extend_from_slice(&self.t.to_compressed());
143        for scalar in &self.proof {
144            buffer.extend_from_slice(scalar.to_repr().as_ref());
145        }
146        buffer
147    }
148
149    /// Convert a byte sequence into a Signature Proof of Knowledge
150    pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Option<Self> {
151        const SIZE: usize = 32 * 3 + 48 * 3;
152        let buffer = bytes.as_ref();
153        if buffer.len() < SIZE {
154            return None;
155        }
156        if buffer.len() % 32 != 0 {
157            return None;
158        }
159
160        let hid_msg_cnt = (buffer.len() - 48 * 3) / 32;
161        let mut offset = 48;
162        let mut end = 96;
163        let a_bar = G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[..offset]).unwrap())
164            .map(G1Projective::from);
165        let b_bar = G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[offset..end]).unwrap())
166            .map(G1Projective::from);
167        offset = end;
168        end += 48;
169        let commitment =
170            G1Affine::from_compressed(&<[u8; 48]>::try_from(&buffer[offset..end]).unwrap())
171                .map(G1Projective::from);
172
173        if (a_bar.is_none() | b_bar.is_none() | commitment.is_none()).into() {
174            return None;
175        }
176
177        offset = end;
178        end += 32;
179
180        let mut proof = Vec::new();
181        for _ in 0..hid_msg_cnt {
182            let c = Scalar::from_be_bytes(&<[u8; 32]>::try_from(&buffer[offset..end]).unwrap());
183            offset = end;
184            end = offset + 32;
185            if c.is_none().unwrap_u8() == 1 {
186                return None;
187            }
188
189            proof.push(c.unwrap());
190        }
191        Some(Self {
192            a_bar: a_bar.unwrap(),
193            b_bar: b_bar.unwrap(),
194            t: commitment.unwrap(),
195            proof,
196        })
197    }
198}