ecdsa_proof/
lib.rs

1//! This is a simple implementation of the work done by Ubique using
2//! ZKattest to prove that an ECDSA signature can be verified using a commitment
3//! on a public key stored in a BBS credential.
4//!
5//! The test at the end shows how to set up a system and check that the verification
6//! is correct.
7//! For a longer explanation, see our [Github Repo](https://github.com/c4dt/how-2025-06-eID)
8//! with Jupyter notebooks and more information.
9//!
10//! WARNING: This is a work of a couple of days for a hands-on workshop.
11//! While the general gist of how keys are generated, signatures created and verified,
12//! ZKP written and verified is correct, I'm sure that there are:
13//!
14//! - errors in this re-arrangement of @Lovesh's excellent work from
15//! [docknetwork/crypt](https://github.com/docknetwork/crypto/blob/main/equality_across_groups/src/pok_ecdsa_pubkey.rs#L462)
16//! - not optimal and not secure ways of treating private keys and other random secrets
17//! - commitment - BBS proof: nonce and context are static - it could directly integrate the
18//! revealed values in the proof.
19//!
20//! So while this work has been done to the best of my knowledge, I definietly took a lot
21//! of shortcuts and definitely valued simplicity for a hands-on-workshop over cryptographic
22//! correctness.
23//!
24//! May [Rogaway, Chaum and Goldwasser](https://politics.media.mit.edu/papers/Rogoway_Moral_Cryptography.pdf)
25//! have mercy on me :)
26
27use ark_bls12_381::{Bls12_381, Fr as BlsFr, G1Affine as BlsG1Affine};
28use ark_ec::{AffineRepr, CurveGroup, short_weierstrass::Affine};
29use ark_ff::PrimeField;
30use ark_secp256r1::{Affine as SecP256Affine, Fq, Fr as SecP256Fr};
31use ark_serialize::{CanonicalSerialize, Compress, SerializationError};
32use ark_std::{
33    UniformRand,
34    io::Write,
35    rand::{SeedableRng, rngs::StdRng},
36    vec::Vec,
37};
38use base64::prelude::*;
39use bbs_plus::{
40    error::BBSPlusError,
41    prelude::SignatureG1,
42    proof::{PoKOfSignatureG1Proof, PoKOfSignatureG1Protocol},
43    setup::{KeypairG2, PublicKeyG2, SignatureParamsG1},
44};
45use blake2::Blake2b512;
46use bulletproofs_plus_plus::prelude::SetupParams as BppSetupParams;
47use dock_crypto_utils::{
48    commitment::PedersenCommitmentKey,
49    signature::MessageOrBlinding,
50    transcript::{Transcript, new_merlin_transcript},
51};
52use equality_across_groups::{
53    ec::commitments::{
54        PointCommitment, PointCommitmentWithOpening, from_base_field_to_scalar_field,
55    },
56    eq_across_groups::ProofLargeWitness,
57    pok_ecdsa_pubkey::{
58        PoKEcdsaSigCommittedPublicKey, PoKEcdsaSigCommittedPublicKeyProtocol, TransformedEcdsaSig,
59    },
60    tom256::{Affine as Tom256Affine, Config as Tom256Config},
61};
62pub use kvac::bbs_sharp::ecdsa;
63use proof_system::{
64    prelude::{
65        EqualWitnesses, MetaStatement, MetaStatements, Witness, WitnessRef, Witnesses,
66        bbs_plus::{PoKBBSSignatureG1Prover, PoKBBSSignatureG1Verifier},
67    },
68    proof::Proof,
69    proof_spec::ProofSpec,
70    statement::{Statements, ped_comm::PedersenCommitment as PedersenCommitmentStmt},
71    witness::PoKBBSSignatureG1,
72};
73
74use serde::ser::SerializeStruct;
75use serde::{Serialize, Serializer};
76use sha2::{Digest, Sha256};
77use std::{
78    collections::{BTreeMap, BTreeSet, HashMap},
79    fmt,
80};
81
82/// The [Issuer] represents here the government issuer in Swiyu, which can create new
83/// credentials for the Holder ([MobilePhone]s).
84/// Each new credential is bound to the public key sent by the holder.
85/// An [Issuer] has a private key to sign credentials for the holders.
86/// The certificate of the issuer, the public key, can be used by the [Verifier] to
87/// check that a certificate has been created by the [Issuer]
88#[derive(Debug)]
89pub struct Issuer {
90    /// Common setup.
91    setup: PublicSetup,
92    /// Most secret key to sign new credentials.
93    keypair: KeypairG2<Bls12_381>,
94}
95
96/// A [Verifier] in this demo only has to create random messages, and then verify that the
97/// [ECDSAProof] is correct.
98#[derive(Debug)]
99pub struct Verifier {
100    /// Common setup.
101    setup: PublicSetup,
102    /// Certificate of the [Issuer] - it's public key
103    certificate: PublicKeyG2<Bls12_381>,
104}
105
106/// The [MobilePhone] represents a holder. It has:
107///
108/// - [MobilePhone::secure_element()] to manage private keys and create signatures
109/// - [MobilePhone::swiyu()] as a simple representation of the Swiyu application
110///
111/// To use the Swiyu app, it first needs to be [MobilePhone::install_swiyu()].
112#[derive(Debug)]
113pub struct MobilePhone {
114    /// Common setup.
115    setup: PublicSetup,
116    /// Very secure and hardened element of the phone which creates keypairs, but only
117    /// shares the public key. Can sign messages, but will not reveal the private key.
118    secure_element: SecureElement,
119    /// The wallet created by the Swiss government - of course this implementation misses
120    /// a lot of functionality :)
121    swiyu: Option<Swiyu>,
122}
123
124/// A [VerifiedCredential] is created by the [Issuer] and holds the public key of the
125/// [MobilePhone] in a secure way.
126/// We suppose that the first two positions are the x and y value of the holder's
127/// public key.
128#[derive(CanonicalSerialize, Debug, Clone)]
129pub struct VerifiedCredential {
130    /// The messages are the values hashed to a BlsFr scalar and are secret.
131    /// Only the [Issuer] and the [MobilePhone] should know them.
132    /// Except for the revealed messages, of course.
133    messages: Vec<BlsFr>,
134    /// Signature of the issuer on this credential.
135    signature: SignatureG1<Bls12_381>,
136    /// Contents of the messages which hold strings
137    message_strings: BTreeMap<usize, String>,
138}
139
140/// The [SecureElement] is a specially hardened part of the [MobilePhone] which can create
141/// keypairs. However, the private key is inaccessible to the applications. An application
142/// can only request a signature from one of the private keys, but not access it directly.
143/// While this makes it much more secure, it makes it also much more difficult to create
144/// useful cryptographic algorithms.
145#[derive(Default, Debug)]
146pub struct SecureElement {
147    /// Very secure key storage - only the private keys are kept.
148    /// The ID for signing directly indexes the [Vec].
149    /// In a real SecureElement, the IDs are of course also tied to the
150    /// applications, so application X cannot use the ID of the private key
151    /// of application Y to sign something.
152    keys: Vec<SecP256Fr>,
153}
154
155/// A simple representation of the [Swiyu] app. It needs to be set up correctly by the
156/// user, which of course usually is done automatically.
157/// But for our example we want to see how the public key gets transferred from the
158/// [SecureElement] to the [Swiyu] app.
159/// In the same way you'll have to add the [VerifiedCredential] manually.
160#[derive(Debug)]
161pub struct Swiyu {
162    /// Common Setup.
163    setup: PublicSetup,
164    /// The key-id of the secure element to be used in signing.
165    key_id: Option<usize>,
166    /// The [VerifiedCredential] received from the [Issuer].
167    vcs: Option<VerifiedCredential>,
168}
169
170/// A [BBSPresentation] is a proof of knowledge of the [VerifiedCredential]. It contains
171/// a proof that the presentation has been signed by the [Issuer], and can be
172/// verified by the certificate of the [Issuer].
173#[derive(Clone, CanonicalSerialize, Debug)]
174pub struct BBSPresentation {
175    /// The proof of knowledge of the [VerifiedCredential], which can be verified
176    /// using the certificate of the [Issuer].
177    proof: PoKOfSignatureG1Proof<Bls12_381>,
178    /// Revealed messages of the [VerifiedCredential], which will prove that the
179    /// [BBSPresentation::message_strings] are part of the credential.
180    revealed: BTreeMap<usize, BlsFr>,
181    /// Strings used to hash to the messages being _revealed_.
182    message_strings: BTreeMap<usize, String>,
183    /// Commitment to the x-coordinate of the public key
184    commitment_pub_x: PublicCommitment,
185    /// Commitment to the y-coordinate of the public key
186    commitment_pub_y: PublicCommitment,
187}
188
189/// Use an enum to show that the commitments can be open, including the random value,
190/// or closed, which is secure.
191#[derive(Clone, Debug)]
192pub enum PublicCommitment {
193    /// An Open commitment includes the random value, which is the [BlsFr] here.
194    /// It should never be sent to an untrusted party, as they can get some knowledge
195    /// of the original value with it.
196    Open(BlsFr, BlsG1Affine),
197    /// A Closed commitment is stripped of its random value and can safely be shared
198    /// with untrusted parties.
199    Closed(BlsG1Affine),
200}
201
202impl CanonicalSerialize for PublicCommitment {
203    fn serialize_with_mode<W: Write>(
204        &self,
205        mut writer: W,
206        mode: Compress,
207    ) -> Result<(), SerializationError> {
208        match self {
209            PublicCommitment::Open(fp, affine) => {
210                fp.serialize_with_mode(&mut writer, mode)?;
211                affine.serialize_with_mode(&mut writer, mode)?;
212            }
213            PublicCommitment::Closed(affine) => affine.serialize_with_mode(&mut writer, mode)?,
214        }
215        Ok(())
216    }
217
218    fn serialized_size(&self, mode: Compress) -> usize {
219        match self {
220            PublicCommitment::Open(fp, affine) => {
221                fp.serialized_size(mode) + affine.serialized_size(mode)
222            }
223            PublicCommitment::Closed(affine) => affine.serialized_size(mode),
224        }
225    }
226}
227
228/// An [ECDSAProof] links the ECDSA signature from the [SecureElement] to the [BBSPresentation::close]
229/// messages in zero-knowledge.
230/// The commitments are randomized representations of the holder's public key.
231/// The proofs link these commitments to the message to be signed, and between each other.
232#[derive(Clone)]
233pub struct ECDSAProof {
234    /// A commitment of the public key of the holder to the Tom256 curve.
235    comm_pk: PointCommitment<Tom256Config>,
236    /// A proof that [ECDSAProof::comm_pk] can be used to verify the `message` of the verifier.
237    proof: PoKEcdsaSigCommittedPublicKey<{ ECDSAProof::NUM_REPS_SCALAR_MULT }>,
238    /// A proof that the x value of the public key of the holder is the same as [BBSPresentation::commitment_pub_x]
239    proof_eq_pk_x: ProofEqDL,
240    /// A proof that the y value of the public key of the holder is the same as [BBSPresentation::commitment_pub_y]
241    proof_eq_pk_y: ProofEqDL,
242    /// A proof that the commitments used in the above proofs is equal to the x- and y-
243    /// coordinates of the public key stored in the BBS proof.
244    proof_eq_pk_bbs: Proof<Bls12_381>,
245}
246
247impl Issuer {
248    /// Create a new issuer with a new keypair.
249    pub fn new(mut setup: PublicSetup) -> Self {
250        Self {
251            keypair: KeypairG2::<Bls12_381>::generate_using_rng(
252                &mut setup.rng,
253                &setup.sig_params_g1,
254            ),
255            setup,
256        }
257    }
258
259    /// Returns the certificate of the [Issuer] - in this case the public key.
260    pub fn get_certificate(&self) -> PublicKeyG2<Bls12_381> {
261        self.keypair.public_key.clone()
262    }
263
264    /// Creates a new BBS credential using the coordinates of the holder's public key as the first two
265    /// messages.
266    /// The real issuer would request a proof that the holder has been authenticated and has the right
267    /// to request a credential.
268    /// In our case it adds a `first` and `last` to the messages.
269    pub fn new_credential(&mut self, key_pub: SecP256Affine) -> VerifiedCredential {
270        let pk_x = from_base_field_to_scalar_field::<Fq, BlsFr>(key_pub.x().unwrap());
271        let pk_y = from_base_field_to_scalar_field::<Fq, BlsFr>(key_pub.y().unwrap());
272        let mut generator = names::Generator::default();
273        let (first, last) = (generator.next().unwrap(), generator.next().unwrap());
274        let message_strings = BTreeMap::from([
275            (VerifiedCredential::FIELD_FIRSTNAME, first.clone()),
276            (VerifiedCredential::FIELD_LASTNAME, last.clone()),
277        ]);
278        let messages = vec![
279            pk_x,
280            pk_y,
281            (&VerifierMessage(first)).into(),
282            (&VerifierMessage(last)).into(),
283        ];
284        VerifiedCredential {
285            signature: SignatureG1::<Bls12_381>::new(
286                &mut self.setup.rng,
287                &messages,
288                &self.keypair.secret_key,
289                &self.setup.sig_params_g1,
290            )
291            .unwrap(),
292            messages,
293            message_strings,
294        }
295    }
296}
297
298/// Numbering the fields in the VerifiedCredential.
299impl VerifiedCredential {
300    pub const FIELD_PUB_X: usize = 0;
301    pub const FIELD_PUB_Y: usize = 1;
302    pub const FIELD_FIRSTNAME: usize = 2;
303    pub const FIELD_LASTNAME: usize = 3;
304    pub const FIELD_COUNT: u32 = 4;
305
306    /// Reeturns the string of the given message. Only works for the first
307    /// and last name. Returns `None` for the other fields.
308    pub fn get_message_str(&self, field: usize) -> Option<String> {
309        return self.message_strings.get(&field).cloned();
310    }
311}
312
313impl Verifier {
314    /// A verifier needs the certificate of the [Issuer] to know how to verify incoming
315    /// [BBSPresentation]s.
316    pub fn new(setup: PublicSetup, certificate: PublicKeyG2<Bls12_381>) -> Self {
317        Self { setup, certificate }
318    }
319
320    /// Returns a time-based changing message which is used to avoid reply-attacks.
321    pub fn create_message(&mut self) -> VerifierMessage {
322        VerifierMessage::new("Verifier")
323    }
324
325    /// Checks that the given [BBSPresentation] and [ECDSAProof] correspond to the [VerifierMessage].
326    /// It does the following checks:
327    /// - is the [BBSPresentation] correctly signed by the [Issuer]?
328    /// - does the [ECDSAProof] validate that the commitment to the public key in the [BBSPresentation]
329    /// verifies the signature?
330    pub fn check_proof(
331        &self,
332        verifier_message: VerifierMessage,
333        bbs_presentation: BBSPresentation,
334        ecdsa_proof: ECDSAProof,
335    ) {
336        bbs_presentation
337            .verify(
338                self.setup.clone(),
339                self.certificate.clone(),
340                &(&verifier_message).into(),
341            )
342            .expect("Verification of BBS proof failed");
343        ecdsa_proof.verify(
344            self.setup.clone(),
345            (&verifier_message).into(),
346            &bbs_presentation,
347            &self.certificate,
348        );
349    }
350}
351
352/// The [VerifierMessage] is used both for the signature from the
353/// [SecureElement] and in the [BBSPresentation].
354/// This is a very simple implementation allowing to create scalars
355/// for SecP256 and Bls12-381.
356#[derive(Debug, Clone)]
357pub struct VerifierMessage(String);
358
359impl VerifierMessage {
360    /// Create a new message with some kind of domain separation.
361    pub fn new(domain: &str) -> Self {
362        Self(format!(
363            "{domain}: Proof request at {}",
364            chrono::Utc::now().to_rfc3339()
365        ))
366    }
367}
368
369/// Allow to create a Bls12-381 scalar.
370impl Into<BlsFr> for &VerifierMessage {
371    fn into(self) -> BlsFr {
372        let hash = Sha256::digest(self.0.as_bytes());
373        let mut bytes = [0u8; 32];
374        bytes.copy_from_slice(&hash[..32]);
375        BlsFr::from_le_bytes_mod_order(&bytes)
376    }
377}
378
379/// Allow to create a SecP256 scalar.
380impl Into<SecP256Fr> for &VerifierMessage {
381    fn into(self) -> SecP256Fr {
382        let hash = Sha256::digest(self.0.as_bytes());
383        let mut bytes = [0u8; 32];
384        bytes.copy_from_slice(&hash[..32]);
385        SecP256Fr::from_le_bytes_mod_order(&bytes)
386    }
387}
388
389impl MobilePhone {
390    /// Creates a new mobile phone with an empty [SecureElement] and no [Swiyu] app
391    /// installed.
392    pub fn new(setup: PublicSetup) -> Self {
393        Self {
394            setup,
395            secure_element: SecureElement::default(),
396            swiyu: None,
397        }
398    }
399
400    /// Fills in the [Swiyu] app - engineer humor...
401    pub fn install_swiyu(&mut self) {
402        if self.swiyu.is_some() {
403            panic!("Swiyu is already installed");
404        }
405        self.swiyu = Some(Swiyu::new(self.setup.clone()))
406    }
407
408    /// Access the [SecureElement]. This is mostly to avoid explaining
409    /// to students [Option]s and stuff.
410    pub fn secure_element(&mut self) -> &mut SecureElement {
411        &mut self.secure_element
412    }
413
414    /// Access the [Swiyu] app. This is mostly to avoid explaining
415    /// to students [Option]s and stuff.
416    /// This panics if the [Swiyu] app has not been installed.
417    pub fn swiyu(&mut self) -> &mut Swiyu {
418        match self.swiyu.as_mut() {
419            Some(swiyu) => swiyu,
420            None => panic!("Swiyu is not yet installed"),
421        }
422    }
423}
424
425impl SecureElement {
426    /// Creates a new keypair and stores the private key in the internal [Vec].
427    /// It returns the key and an id.
428    pub fn create_kp(&mut self) -> SEKeypair {
429        let sk = SecP256Fr::rand(&mut StdRng::seed_from_u64(0u64));
430        let key_pub = (ecdsa::Signature::generator() * sk).into_affine();
431        self.keys.push(sk);
432        SEKeypair {
433            id: self.keys.len() - 1,
434            key_pub,
435        }
436    }
437
438    /// Signs a message with the private key referenced by id.
439    /// No checks are done to make sure this id exists or is tied to the current
440    /// application.
441    pub fn sign(&self, id: usize, msg: VerifierMessage) -> ecdsa::Signature {
442        ecdsa::Signature::new_prehashed(
443            &mut StdRng::seed_from_u64(0u64),
444            (&msg).into(),
445            self.keys[id],
446        )
447    }
448}
449
450/// A [SecureElement] keypair, with the private key stored in the
451/// [SecureElement] only, and only accessible via its id.
452#[derive(Debug)]
453pub struct SEKeypair {
454    pub id: usize,
455    pub key_pub: SecP256Affine,
456}
457
458impl Swiyu {
459    /// Create a new [Swiyu] app. Of course the real app will do so much
460    /// more to initialize itself. But here it's mostly a placeholder to
461    /// show which actor does which parts.
462    pub fn new(setup: PublicSetup) -> Self {
463        Self {
464            setup,
465            key_id: None,
466            vcs: None,
467        }
468    }
469
470    /// Add a [VerifiedCredential].
471    /// In this simple implementation, only one [VerifiedCredential] can exist.
472    pub fn add_vc(&mut self, key_id: usize, credential: VerifiedCredential) {
473        if self.vcs.is_some() {
474            panic!("Can only have one credential");
475        }
476        self.key_id = Some(key_id);
477        self.vcs = Some(credential);
478    }
479
480    /// Returns a [BBSPresentation] which is a proof that the holder knows the
481    /// messages signed by the [Issuer].
482    /// In addition to the normal protocol, this adds two [PublicCommitment]s
483    /// to the proof, which hide the public key of the holder.
484    /// When creating a [BBSPresentation], the [PublicCommitment]s are Open, meaning
485    /// that their random values are accessible.
486    /// Before sending a [BBSPresentation] to an untrusted entity, one must call
487    /// [BBSPresentation::close()] to remove the random values.
488    pub fn bbs_presentation(
489        &mut self,
490        message: VerifierMessage,
491        reveal: Vec<usize>,
492    ) -> BBSPresentation {
493        let vc = self.vcs.as_ref().expect("No credential yet!");
494        let mut messages_and_blindings = vec![
495            MessageOrBlinding::BlindMessageRandomly(&vc.messages[0]),
496            MessageOrBlinding::BlindMessageRandomly(&vc.messages[1]),
497        ];
498
499        // Add the first- or last-name, depending on the request.
500        let mut message_strings = BTreeMap::new();
501        let mut revealed = BTreeMap::new();
502        for idx in [
503            VerifiedCredential::FIELD_FIRSTNAME,
504            VerifiedCredential::FIELD_LASTNAME,
505        ] {
506            let msg = &vc.messages[idx];
507            if reveal.contains(&idx) {
508                messages_and_blindings.push(MessageOrBlinding::RevealMessage(msg));
509                message_strings.insert(idx, vc.message_strings[&idx].clone());
510                revealed.insert(idx, msg.clone());
511            } else {
512                messages_and_blindings.push(MessageOrBlinding::BlindMessageRandomly(msg));
513            }
514        }
515
516        let proof = PoKOfSignatureG1Protocol::init(
517            &mut self.setup.rng,
518            &vc.signature,
519            &self.setup.sig_params_g1,
520            messages_and_blindings,
521        )
522        .unwrap()
523        .gen_proof(&(&message).into())
524        .unwrap();
525
526        BBSPresentation {
527            proof,
528            revealed,
529            message_strings,
530            commitment_pub_x: PublicCommitment::from_message(
531                &mut self.setup,
532                &vc.messages[VerifiedCredential::FIELD_PUB_X],
533            ),
534            commitment_pub_y: PublicCommitment::from_message(
535                &mut self.setup,
536                &vc.messages[VerifiedCredential::FIELD_PUB_Y],
537            ),
538        }
539    }
540}
541
542impl PublicCommitment {
543    /// Create a new commitment from a scalar. This returns an Open commitment,
544    /// including the random value.
545    pub fn from_message(setup: &mut PublicSetup, message: &BlsFr) -> Self {
546        let randomness = BlsFr::rand(&mut setup.rng);
547        Self::Open(randomness, setup.comm_key_bls.commit(message, &randomness))
548    }
549
550    /// Close this commitment by discarding the random value.
551    /// For security, this consumes the [PublicCommitment].
552    pub fn close(self) -> Self {
553        match self {
554            PublicCommitment::Open(_, affine) => PublicCommitment::Closed(affine),
555            _ => self,
556        }
557    }
558
559    /// Returns the random value of this commitment.
560    /// If the commitment is closed, it panics.
561    pub fn rand(&self) -> BlsFr {
562        match self {
563            PublicCommitment::Open(fp, _) => fp.clone(),
564            _ => panic!("No random value in closed commitment"),
565        }
566    }
567
568    /// Returns the affine, public, value of this commitment, which is always available.
569    pub fn affine(&self) -> BlsG1Affine {
570        match self {
571            PublicCommitment::Open(_, affine) => affine.clone(),
572            PublicCommitment::Closed(affine) => affine.clone(),
573        }
574    }
575
576    /// Make sure the commitment has been closed.
577    pub fn assert_closed(&self) {
578        if let Self::Open(_, _) = self {
579            panic!("PublicCommitment is open and leaks random value!");
580        }
581    }
582}
583
584/// Globally known public values.
585/// These values can be created by anyone and are needed by all actors.
586#[derive(Clone, Debug)]
587pub struct PublicSetup {
588    /// Random Number Generator
589    rng: StdRng,
590    /// Parameters for the BBS signatures
591    sig_params_g1: SignatureParamsG1<Bls12_381>,
592    /// Bulletproof setup parameters - usually created by the verifier and then sent to the prover.
593    bpp_setup_params: BppSetupParams<Affine<Tom256Config>>,
594    /// Commitment generators for the Tom-256 keys
595    comm_key_tom: PedersenCommitmentKey<Tom256Affine>,
596    /// Commitment generators for the SecP256 keys
597    comm_key_secp: PedersenCommitmentKey<SecP256Affine>,
598    /// Commitment generators for the Bls12-381 keys
599    comm_key_bls: PedersenCommitmentKey<BlsG1Affine>,
600}
601
602impl PublicSetup {
603    // These constants define also the security level of the proofs created in [BBSPresentation].
604    const WITNESS_BIT_SIZE: usize = 64;
605    const CHALLENGE_BIT_SIZE: usize = 180;
606    const ABORT_PARAM: usize = 8;
607    const RESPONSE_BYTE_SIZE: usize = 32;
608    const NUM_REPS: usize = 1;
609    const NUM_CHUNKS: usize = 4;
610    const BPP_BASE: u16 = 2;
611
612    /// Create a new [PublicSetup]. It can also be [Clone]d, or created new.
613    pub fn new() -> Self {
614        let comm_key_tom = PedersenCommitmentKey::<Tom256Affine>::new::<Blake2b512>(b"test2");
615
616        // Bulletproofs++ setup
617        let base = PublicSetup::BPP_BASE;
618        let mut bpp_setup_params =
619            BppSetupParams::<Tom256Affine>::new_for_perfect_range_proof::<Blake2b512>(
620                b"test",
621                base,
622                PublicSetup::WITNESS_BIT_SIZE as u16,
623                PublicSetup::NUM_CHUNKS as u32,
624            );
625        bpp_setup_params.G = comm_key_tom.g;
626        bpp_setup_params.H_vec[0] = comm_key_tom.h;
627
628        Self {
629            sig_params_g1: SignatureParamsG1::<Bls12_381>::new::<Blake2b512>(
630                "eid-demo".as_bytes(),
631                VerifiedCredential::FIELD_COUNT,
632            ),
633            rng: StdRng::seed_from_u64(0u64),
634            bpp_setup_params,
635            comm_key_tom,
636            comm_key_secp: PedersenCommitmentKey::<SecP256Affine>::new::<Blake2b512>(b"test1"),
637            comm_key_bls: PedersenCommitmentKey::<BlsG1Affine>::new::<Blake2b512>(b"test3"),
638        }
639    }
640
641    pub fn to_json(&self) -> String {
642        serde_json::to_string(self).expect("JSON conversion error")
643    }
644
645    /// For the proofs it is important to enter all variables in the transcript, and then hash
646    /// them to create a challenge.
647    /// If the prover can modify values which are not part of the transcript, they might get
648    /// an advantage and manage to create a wrong proof.
649    fn append_transcript<T: Transcript + Clone + Write>(&self, pt: &mut T) {
650        pt.append(b"comm_key_secp", &self.comm_key_secp);
651        pt.append(b"comm_key_tom", &self.comm_key_tom);
652        pt.append(b"comm_key_bls", &self.comm_key_bls);
653        pt.append(b"bpp_setup_params", &self.bpp_setup_params);
654    }
655}
656
657/// Parametrization of the discreet log equality proof.
658type ProofEqDL = ProofLargeWitness<
659    Tom256Affine,
660    BlsG1Affine,
661    { PublicSetup::NUM_CHUNKS },
662    { PublicSetup::WITNESS_BIT_SIZE },
663    { PublicSetup::CHALLENGE_BIT_SIZE },
664    { PublicSetup::ABORT_PARAM },
665    { PublicSetup::RESPONSE_BYTE_SIZE },
666    { PublicSetup::NUM_REPS },
667>;
668
669impl ECDSAProof {
670    const NUM_REPS_SCALAR_MULT: usize = 128;
671
672    /// Create a new proof that the public key stored in a commitment can be used
673    /// to verify a signature.
674    /// This follows the excellent work done by Lovesh in
675    /// [docknetwork/crypto](https://github.com/docknetwork/crypto/blob/main/equality_across_groups/src/pok_ecdsa_pubkey.rs#L462).
676    pub fn new(
677        mut setup: PublicSetup,
678        holder_pub: SecP256Affine,
679        vc: VerifiedCredential,
680        bbs_presentation: BBSPresentation,
681        ecdsa_signature: ecdsa::Signature,
682        verifier_message: VerifierMessage,
683    ) -> Self {
684        // Commit to ECDSA public key on Tom-256 curve
685        let comm_pk =
686            PointCommitmentWithOpening::new(&mut setup.rng, &holder_pub, &setup.comm_key_tom)
687                .unwrap();
688
689        let transformed_sig =
690            TransformedEcdsaSig::new(&ecdsa_signature, (&verifier_message).into(), holder_pub)
691                .unwrap();
692        transformed_sig
693            .verify_prehashed((&verifier_message).into(), holder_pub)
694            .unwrap();
695
696        // Create the transcript which will be used for the challenge.
697        // All variables which can be chosen by the prover must find their way into the transcript,
698        // else the prover can have an advantage in creating a wrong proof.
699        let mut prover_transcript = new_merlin_transcript(b"test");
700        setup.append_transcript(&mut prover_transcript);
701        Self::append_transcript(
702            &mut prover_transcript,
703            &comm_pk.comm,
704            &bbs_presentation,
705            &(&verifier_message).into(),
706        );
707
708        // Create a protocol to prove that the `transformed_sig` on the `verifier_message`
709        // can be verified using the commitment `comm_pk`.
710        let protocol =
711            PoKEcdsaSigCommittedPublicKeyProtocol::<{ ECDSAProof::NUM_REPS_SCALAR_MULT }>::init(
712                &mut setup.rng,
713                transformed_sig,
714                (&verifier_message).into(),
715                holder_pub,
716                comm_pk.clone(),
717                &setup.comm_key_secp,
718                &setup.comm_key_tom,
719            )
720            .unwrap();
721        protocol
722            .challenge_contribution(&mut prover_transcript)
723            .unwrap();
724        let challenge_prover = prover_transcript.challenge_scalar(b"challenge");
725        let proof = protocol.gen_proof(&challenge_prover);
726
727        // Proof that the x coordinate is same in both Tom-256 and BLS12-381 commitments
728        let proof_eq_pk_x = ProofEqDL::new(
729            &mut setup.rng,
730            &comm_pk.x,
731            comm_pk.r_x,
732            bbs_presentation.commitment_pub_x.rand(),
733            &setup.comm_key_tom,
734            &setup.comm_key_bls,
735            PublicSetup::BPP_BASE,
736            setup.bpp_setup_params.clone(),
737            &mut prover_transcript,
738        )
739        .unwrap();
740
741        // Proof that the y coordinate is same in both Tom-256 and BLS12-381 commitments
742        let proof_eq_pk_y = ProofEqDL::new(
743            &mut setup.rng,
744            &comm_pk.y,
745            comm_pk.r_y,
746            bbs_presentation.commitment_pub_y.rand(),
747            &setup.comm_key_tom,
748            &setup.comm_key_bls,
749            PublicSetup::BPP_BASE,
750            setup.bpp_setup_params.clone(),
751            &mut prover_transcript,
752        )
753        .unwrap();
754
755        // Proof that the commitments to x- and y- coordinates correspond to the BBS signature.
756        let mut statements = Statements::<Bls12_381>::new();
757        statements.add(PedersenCommitmentStmt::new_statement_from_params(
758            vec![setup.comm_key_bls.g, setup.comm_key_bls.h],
759            bbs_presentation.commitment_pub_x.affine(),
760        ));
761        statements.add(PedersenCommitmentStmt::new_statement_from_params(
762            vec![setup.comm_key_bls.g, setup.comm_key_bls.h],
763            bbs_presentation.commitment_pub_y.affine(),
764        ));
765        statements.add(
766            PoKBBSSignatureG1Prover::<Bls12_381>::new_statement_from_params(
767                setup.sig_params_g1.clone(),
768                BTreeMap::new(),
769            ),
770        );
771
772        let mut meta_statements = MetaStatements::new();
773        meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
774            vec![(0, 0), (2, 0)]
775                .into_iter()
776                .collect::<BTreeSet<WitnessRef>>(),
777        )));
778        meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
779            vec![(1, 0), (2, 1)]
780                .into_iter()
781                .collect::<BTreeSet<WitnessRef>>(),
782        )));
783
784        let mut witnesses = Witnesses::new();
785        witnesses.add(Witness::PedersenCommitment(vec![
786            vc.messages[0].clone(),
787            bbs_presentation.commitment_pub_x.rand(),
788        ]));
789        witnesses.add(Witness::PedersenCommitment(vec![
790            vc.messages[1].clone(),
791            bbs_presentation.commitment_pub_y.rand(),
792        ]));
793        witnesses.add(PoKBBSSignatureG1::new_as_witness(
794            vc.signature,
795            BTreeMap::from([
796                (0, vc.messages[0]),
797                (1, vc.messages[1]),
798                (2, vc.messages[2]),
799                (3, vc.messages[3]),
800            ]),
801        ));
802
803        let context = Some(b"test".to_vec());
804        let proof_spec = ProofSpec::new(
805            statements.clone(),
806            meta_statements.clone(),
807            vec![],
808            context.clone(),
809        );
810        proof_spec.validate().unwrap();
811
812        let nonce_eq_pk = Some(b"test nonce".to_vec());
813        let proof_eq_pk_bbs = Proof::new::<StdRng, Blake2b512>(
814            &mut setup.rng,
815            proof_spec.clone(),
816            witnesses.clone(),
817            nonce_eq_pk.clone(),
818            Default::default(),
819        )
820        .unwrap()
821        .0;
822
823        Self {
824            comm_pk: comm_pk.comm,
825            proof,
826            proof_eq_pk_x,
827            proof_eq_pk_y,
828            proof_eq_pk_bbs,
829        }
830    }
831
832    /// Verifies that the commitments to the signature and the public key of the holder
833    /// verify the `message` from the verifier. The [BBSPresentation] is used for the commitments
834    /// to the holder's public key.
835    pub fn verify(
836        &self,
837        mut setup: PublicSetup,
838        message: SecP256Fr,
839        presentation: &BBSPresentation,
840        issuer_pub: &PublicKeyG2<Bls12_381>,
841    ) {
842        presentation.assert_closed();
843        let mut verifier_transcript = new_merlin_transcript(b"test");
844        setup.append_transcript(&mut verifier_transcript);
845        Self::append_transcript(
846            &mut verifier_transcript,
847            &self.comm_pk,
848            presentation,
849            &message,
850        );
851        self.proof
852            .challenge_contribution(&mut verifier_transcript)
853            .unwrap();
854
855        let challenge_verifier = verifier_transcript.challenge_scalar(b"challenge");
856
857        // Exercise for the reader: verify_using_randomized_mult_checker can be used to make it much faster.
858        // See [docknetwork/crypto](https://github.com/docknetwork/crypto/blob/main/equality_across_groups/src/pok_ecdsa_pubkey.rs#L434)
859        self.proof
860            .verify(
861                message,
862                &self.comm_pk,
863                &challenge_verifier,
864                &setup.comm_key_secp,
865                &setup.comm_key_tom,
866            )
867            .expect("ECDSA proof failed");
868
869        // Verify the proof for the equality between the Tom-256 commitment and the BBS-commitment:
870        // x-coordinate
871        self.proof_eq_pk_x
872            .verify(
873                &self.comm_pk.x,
874                &presentation.commitment_pub_x.affine(),
875                &setup.comm_key_tom,
876                &setup.comm_key_bls,
877                &setup.bpp_setup_params,
878                &mut verifier_transcript,
879            )
880            .expect("DlEQ proof for x position failed");
881
882        // Verify the proof for the equality between the Tom-256 commitment and the BBS-commitment:
883        // y-coordinate
884        self.proof_eq_pk_y
885            .verify(
886                &self.comm_pk.y,
887                &presentation.commitment_pub_y.affine(),
888                &setup.comm_key_tom,
889                &setup.comm_key_bls,
890                &setup.bpp_setup_params,
891                &mut verifier_transcript,
892            )
893            .expect("DlEQ proff for y position failed");
894
895        // And verify that the given commitments equal the coordinates of the public key
896        // in the BBS signature.
897        let mut verifier_statements = Statements::<Bls12_381>::new();
898        verifier_statements.add(PedersenCommitmentStmt::new_statement_from_params(
899            vec![setup.comm_key_bls.g, setup.comm_key_bls.h],
900            presentation.commitment_pub_x.affine(),
901        ));
902        verifier_statements.add(PedersenCommitmentStmt::new_statement_from_params(
903            vec![setup.comm_key_bls.g, setup.comm_key_bls.h],
904            presentation.commitment_pub_y.affine(),
905        ));
906        verifier_statements.add(PoKBBSSignatureG1Verifier::new_statement_from_params(
907            setup.sig_params_g1.clone(),
908            issuer_pub.clone(),
909            BTreeMap::new(),
910        ));
911
912        let mut meta_statements = MetaStatements::new();
913        meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
914            vec![(0, 0), (2, 0)]
915                .into_iter()
916                .collect::<BTreeSet<WitnessRef>>(),
917        )));
918        meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
919            vec![(1, 0), (2, 1)]
920                .into_iter()
921                .collect::<BTreeSet<WitnessRef>>(),
922        )));
923
924        let context = Some(b"test".to_vec());
925        let verifier_proof_spec = ProofSpec::new(
926            verifier_statements.clone(),
927            meta_statements.clone(),
928            vec![],
929            context.clone(),
930        );
931        verifier_proof_spec.validate().unwrap();
932
933        let nonce_eq_pk = Some(b"test nonce".to_vec());
934        self.proof_eq_pk_bbs
935            .clone()
936            .verify::<StdRng, Blake2b512>(
937                &mut setup.rng,
938                verifier_proof_spec,
939                nonce_eq_pk.clone(),
940                Default::default(),
941            )
942            .expect("Verify Point Equality Proof");
943    }
944
945    /// Returns the sizes of the different proofs.
946    pub fn get_sizes(&self) -> HashMap<&str, usize> {
947        HashMap::from([
948            ("proof_add", self.proof.proof_add.compressed_size()),
949            ("proof_scalar", self.proof.proof_minus_zR.compressed_size()),
950            ("proof_eqdl_x", self.proof_eq_pk_x.compressed_size()),
951            ("proof_eqdl_y", self.proof_eq_pk_y.compressed_size()),
952            ("proof_eq_comm_bbs", self.proof_eq_pk_bbs.compressed_size()),
953        ])
954    }
955
956    /// Returns the json string of the structure.
957    pub fn to_json(&self) -> String {
958        serde_json::to_string(self).expect("While serializing to JSON")
959    }
960
961    // Put the relevant entries in the transcript.
962    fn append_transcript<T: Transcript + Clone + Write>(
963        pt: &mut T,
964        comm_pk: &PointCommitment<Tom256Config>,
965        presentation: &BBSPresentation,
966        message: &SecP256Fr,
967    ) {
968        pt.append(b"comm_pk", comm_pk);
969        pt.append(b"bls_comm_pk_x", &presentation.commitment_pub_x.affine());
970        pt.append(b"bls_comm_pk_y", &presentation.commitment_pub_y.affine());
971        pt.append(b"message", message);
972    }
973}
974
975impl BBSPresentation {
976    /// Make sure the BBSPresentation is closed - a better version would use the
977    /// `*Protocol` and `*Proof` like in the rest of docknetwork/crypto library.
978    pub fn assert_closed(&self) {
979        self.commitment_pub_x.assert_closed();
980        self.commitment_pub_y.assert_closed();
981    }
982
983    /// Close the commitments by removing the random values.
984    pub fn close(self) -> Self {
985        Self {
986            proof: self.proof,
987            revealed: self.revealed,
988            message_strings: self.message_strings,
989            commitment_pub_x: self.commitment_pub_x.close(),
990            commitment_pub_y: self.commitment_pub_y.close(),
991        }
992    }
993
994    /// Verify that:
995    /// - the signature can be verified by the certificate
996    /// - the signature matches the messages
997    /// - the message_strings match the messages
998    /// - the BBS signature matches the commitment_pub
999    pub fn verify(
1000        &self,
1001        setup: PublicSetup,
1002        issuer_certificate: PublicKeyG2<Bls12_381>,
1003        verifier_message: &BlsFr,
1004    ) -> Result<(), BBSPlusError> {
1005        // Is the BBS proof valid?
1006        self.proof.verify(
1007            &self.revealed,
1008            verifier_message,
1009            issuer_certificate,
1010            setup.sig_params_g1.clone(),
1011        )?;
1012
1013        // Check all the revealed message strings to match the BBS messages.
1014        for (rev_id, bbs_msg) in &self.revealed {
1015            match self.message_strings.get(rev_id) {
1016                Some(msg_str) => {
1017                    if bbs_msg != &(&VerifierMessage(msg_str.clone())).into() {
1018                        return Err(BBSPlusError::InvalidSignature);
1019                    }
1020                }
1021                None => return Err(BBSPlusError::IncorrectNoOfShares(*rev_id, 0)),
1022            }
1023        }
1024
1025        Ok(())
1026    }
1027
1028    /// Returns the optional message_string at position `idx`.
1029    /// If `Verify` has not been called, this is not reliable!
1030    pub fn message_string(&self, idx: usize) -> Option<String> {
1031        self.message_strings.get(&idx).cloned()
1032    }
1033
1034    /// Returns the size of the BBS proof.
1035    pub fn get_sizes(&self) -> HashMap<&str, usize> {
1036        HashMap::from([("proof_bbs", self.proof.compressed_size())])
1037    }
1038
1039    /// Returns the JSON string.
1040    pub fn to_json(&self) -> String {
1041        serde_json::to_string(self).expect("Error while serializing to json")
1042    }
1043}
1044
1045/// Thanks go to ChatGPT for this code...
1046impl Serialize for BBSPresentation {
1047    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1048    where
1049        S: Serializer,
1050    {
1051        // Helper to serialize ark-serialize types as base64
1052        fn serialize_ark<T: CanonicalSerialize>(t: &T) -> String {
1053            let mut buf = Vec::new();
1054            t.serialize_compressed(&mut buf).unwrap();
1055            BASE64_STANDARD.encode(buf)
1056        }
1057
1058        // Serialize revealed map: idx -> base64(BlsFr)
1059        let revealed: BTreeMap<usize, String> = self
1060            .revealed
1061            .iter()
1062            .map(|(k, v)| (*k, serialize_ark(v)))
1063            .collect();
1064
1065        // Serialize message_strings as is
1066        let message_strings = &self.message_strings;
1067
1068        // Serialize commitments
1069        fn serialize_commitment(c: &PublicCommitment) -> serde_json::Value {
1070            match c {
1071                PublicCommitment::Open(rand, affine) => {
1072                    serde_json::json!({
1073                        "type": "open",
1074                        "rand": serialize_ark(rand),
1075                        "affine": serialize_ark(affine),
1076                    })
1077                }
1078                PublicCommitment::Closed(affine) => {
1079                    serde_json::json!({
1080                        "type": "closed",
1081                        "affine": serialize_ark(affine),
1082                    })
1083                }
1084            }
1085        }
1086
1087        // Serialize PoK proof
1088        let proof_bytes = {
1089            let mut buf = Vec::new();
1090            self.proof.serialize_compressed(&mut buf).unwrap();
1091            BASE64_STANDARD.encode(buf)
1092        };
1093
1094        let mut state = serializer.serialize_struct("BBSPresentation", 5)?;
1095        state.serialize_field("proof", &proof_bytes)?;
1096        state.serialize_field("revealed", &revealed)?;
1097        state.serialize_field("message_strings", message_strings)?;
1098        state.serialize_field(
1099            "commitment_pub_x",
1100            &serialize_commitment(&self.commitment_pub_x),
1101        )?;
1102        state.serialize_field(
1103            "commitment_pub_y",
1104            &serialize_commitment(&self.commitment_pub_y),
1105        )?;
1106        state.end()
1107    }
1108}
1109
1110/// Thanks to ChatGPT for this serialization stub.
1111impl Serialize for PublicSetup {
1112    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1113    where
1114        S: Serializer,
1115    {
1116        let mut state = serializer.serialize_struct("PublicSetup", 5)?;
1117        state.serialize_field("sig_params_g1", &self.sig_params_g1)?;
1118        // Serialize bpp_setup_params as base64-encoded compressed bytes
1119        let mut bpp_bytes = Vec::new();
1120        self.bpp_setup_params
1121            .serialize_compressed(&mut bpp_bytes)
1122            .unwrap();
1123        let bpp_base64 = BASE64_STANDARD.encode(bpp_bytes);
1124        state.serialize_field("bpp_setup_params", &bpp_base64)?;
1125        state.serialize_field("comm_key_tom", &self.comm_key_tom)?;
1126        state.serialize_field("comm_key_secp", &self.comm_key_secp)?;
1127        state.serialize_field("comm_key_bls", &self.comm_key_bls)?;
1128        state.end()
1129    }
1130}
1131
1132/// Thanks to ChatGPT for this serialization stub.
1133impl Serialize for ECDSAProof {
1134    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1135    where
1136        S: Serializer,
1137    {
1138        // Helper to serialize ark-serialize types as base64
1139        fn serialize_ark<T: CanonicalSerialize>(t: &T) -> String {
1140            let mut buf = Vec::new();
1141            t.serialize_compressed(&mut buf).unwrap();
1142            BASE64_STANDARD.encode(buf)
1143        }
1144
1145        let comm_pk_bytes = serialize_ark(&self.comm_pk);
1146        let proof_bytes = serialize_ark(&self.proof);
1147        let proof_eq_pk_x_bytes = serialize_ark(&self.proof_eq_pk_x);
1148        let proof_eq_pk_y_bytes = serialize_ark(&self.proof_eq_pk_y);
1149
1150        let mut state = serializer.serialize_struct("ECDSAProof", 4)?;
1151        state.serialize_field("comm_pk", &comm_pk_bytes)?;
1152        state.serialize_field("proof", &proof_bytes)?;
1153        state.serialize_field("proof_eq_pk_x", &proof_eq_pk_x_bytes)?;
1154        state.serialize_field("proof_eq_pk_y", &proof_eq_pk_y_bytes)?;
1155        state.end()
1156    }
1157}
1158
1159impl fmt::Display for BBSPresentation {
1160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1161        match serde_json::to_string(self) {
1162            Ok(json) => write!(f, "BBSPresentation: {json}"),
1163            Err(_) => Err(fmt::Error),
1164        }
1165    }
1166}
1167
1168impl fmt::Display for ECDSAProof {
1169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1170        match serde_json::to_string(self) {
1171            Ok(json) => write!(f, "ECDSAProof: {json}"),
1172            Err(_) => Err(fmt::Error),
1173        }
1174    }
1175}
1176
1177impl fmt::Display for Issuer {
1178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1179        write!(f, "Issuer: {:?}", self)
1180    }
1181}
1182
1183impl fmt::Display for MobilePhone {
1184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1185        write!(f, "MobilePhone: {:?}", self)
1186    }
1187}
1188
1189impl fmt::Display for PublicSetup {
1190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1191        match serde_json::to_string(self) {
1192            Ok(json) => write!(f, "PublicSetup: {json}"),
1193            Err(_) => Err(fmt::Error),
1194        }
1195    }
1196}
1197
1198impl fmt::Display for SecureElement {
1199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1200        write!(f, "SecureElement: {:?}", self)
1201    }
1202}
1203
1204impl fmt::Display for SEKeypair {
1205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1206        write!(f, "SEKeypair: {:?}", self)
1207    }
1208}
1209
1210impl fmt::Display for Swiyu {
1211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1212        write!(f, "Swiyu app: {:?}", self)
1213    }
1214}
1215
1216impl fmt::Display for VerifiedCredential {
1217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1218        write!(f, "VerifiedCredential: {:?}", self)
1219    }
1220}
1221
1222impl fmt::Display for Verifier {
1223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1224        write!(f, "Verifier: {:?}", self)
1225    }
1226}
1227
1228impl fmt::Display for VerifierMessage {
1229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1230        write!(f, "VerifierMessage: {:?}", self)
1231    }
1232}
1233
1234#[cfg(test)]
1235mod test {
1236    use std::{
1237        collections::{BTreeMap, BTreeSet},
1238        error::Error,
1239    };
1240
1241    use super::*;
1242    use proof_system::{
1243        prelude::bbs_plus::{PoKBBSSignatureG1Prover, PoKBBSSignatureG1Verifier},
1244        witness::PoKBBSSignatureG1,
1245    };
1246
1247    // A complete test of the ECDSA signature verification.
1248    #[test]
1249    fn sign_verify_msg() -> Result<(), Box<dyn Error>> {
1250        // Set up parties
1251        let setup = PublicSetup::new();
1252        let mut issuer = Issuer::new(setup.clone());
1253        let mut holder = MobilePhone::new(setup.clone());
1254        let mut verifier = Verifier::new(setup.clone(), issuer.get_certificate());
1255
1256        // Install the swiyu app and add a credential
1257        holder.install_swiyu();
1258        let se_kp = holder.secure_element().create_kp();
1259        let credential = issuer.new_credential(se_kp.key_pub);
1260        holder.swiyu().add_vc(se_kp.id, credential.clone());
1261
1262        // Verifier requests a presentation from the holder
1263        let message = verifier.create_message();
1264
1265        // Holder creates a presentation with a proof of knowledge and zero or more revealed messages.
1266        // This is of course all done in the Swiyu app, but here we do it step-by-step.
1267        let presentation = holder
1268            .swiyu()
1269            .bbs_presentation(message.clone(), vec![VerifiedCredential::FIELD_FIRSTNAME]);
1270        let signature = holder.secure_element().sign(0, message.clone());
1271        let proof = ECDSAProof::new(
1272            setup,
1273            se_kp.key_pub,
1274            credential.clone(),
1275            presentation.clone(),
1276            signature,
1277            message.clone(),
1278        );
1279        let presentation_closed = presentation.close();
1280
1281        // Holder sends the proof to the verifier, which checks it.
1282        verifier.check_proof(message, presentation_closed.clone(), proof.clone());
1283        assert_eq!(
1284            presentation_closed.message_string(VerifiedCredential::FIELD_FIRSTNAME),
1285            Some(credential.message_strings[&VerifiedCredential::FIELD_FIRSTNAME].clone())
1286        );
1287        assert_eq!(
1288            presentation_closed.message_string(VerifiedCredential::FIELD_LASTNAME),
1289            None
1290        );
1291
1292        Ok(())
1293    }
1294
1295    /// Proving a pedersen commitment to a scalar in BBS.
1296    #[test]
1297    fn verify_bbs() -> Result<(), Box<dyn Error>> {
1298        let mut rng = StdRng::seed_from_u64(0u64);
1299        let holder_sk = SecP256Fr::rand(&mut StdRng::seed_from_u64(0u64));
1300        let holder_pub = (ecdsa::Signature::generator() * holder_sk).into_affine();
1301        let message_pet = (&VerifierMessage(format!("goldfish"))).into();
1302        let message_pk_x = from_base_field_to_scalar_field::<Fq, BlsFr>(holder_pub.x().unwrap());
1303        let message_pk_y = from_base_field_to_scalar_field::<Fq, BlsFr>(holder_pub.y().unwrap());
1304        let messages = vec![message_pk_x, message_pk_y, message_pet];
1305        let sig_params_g1 = SignatureParamsG1::<Bls12_381>::new::<Blake2b512>(
1306            "eid-demo".as_bytes(),
1307            messages.len() as u32,
1308        );
1309        let issuer_keypair = KeypairG2::<Bls12_381>::generate_using_rng(&mut rng, &sig_params_g1);
1310
1311        let signature_issuer = SignatureG1::<Bls12_381>::new(
1312            &mut rng,
1313            &messages,
1314            &issuer_keypair.secret_key,
1315            &sig_params_g1,
1316        )
1317        .unwrap();
1318
1319        let challenge = BlsFr::rand(&mut rng);
1320        let proof_bbs = PoKOfSignatureG1Protocol::init(
1321            &mut rng,
1322            &signature_issuer,
1323            &sig_params_g1,
1324            vec![
1325                MessageOrBlinding::BlindMessageRandomly(&message_pk_x),
1326                MessageOrBlinding::BlindMessageRandomly(&message_pk_y),
1327                MessageOrBlinding::RevealMessage(&message_pet),
1328            ],
1329        )
1330        .unwrap()
1331        .gen_proof(&challenge)
1332        .unwrap();
1333
1334        let comm_key_bls = PedersenCommitmentKey::<BlsG1Affine>::new::<Blake2b512>(b"test3");
1335        let randomness_pub_x = BlsFr::rand(&mut rng);
1336        let commitment_pub_x = comm_key_bls.commit(&message_pk_x, &randomness_pub_x);
1337        let randomness_pub_y = BlsFr::rand(&mut rng);
1338        let commitment_pub_y = comm_key_bls.commit(&message_pk_y, &randomness_pub_y);
1339
1340        let mut statements = Statements::<Bls12_381>::new();
1341        statements.add(PedersenCommitmentStmt::new_statement_from_params(
1342            vec![comm_key_bls.g, comm_key_bls.h],
1343            commitment_pub_x,
1344        ));
1345        statements.add(PedersenCommitmentStmt::new_statement_from_params(
1346            vec![comm_key_bls.g, comm_key_bls.h],
1347            commitment_pub_y,
1348        ));
1349        statements.add(
1350            PoKBBSSignatureG1Prover::<Bls12_381>::new_statement_from_params(
1351                sig_params_g1.clone(),
1352                BTreeMap::from([(2, message_pet)]),
1353            ),
1354        );
1355
1356        let mut meta_statements = MetaStatements::new();
1357        meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
1358            vec![(0, 0), (2, 0)]
1359                .into_iter()
1360                .collect::<BTreeSet<WitnessRef>>(),
1361        )));
1362        meta_statements.add(MetaStatement::WitnessEquality(EqualWitnesses(
1363            vec![(1, 0), (2, 1)]
1364                .into_iter()
1365                .collect::<BTreeSet<WitnessRef>>(),
1366        )));
1367
1368        let mut witnesses = Witnesses::new();
1369        witnesses.add(Witness::PedersenCommitment(vec![
1370            message_pk_x.clone(),
1371            randomness_pub_x,
1372        ]));
1373        witnesses.add(Witness::PedersenCommitment(vec![
1374            message_pk_y.clone(),
1375            randomness_pub_y,
1376        ]));
1377        witnesses.add(PoKBBSSignatureG1::new_as_witness(
1378            signature_issuer,
1379            BTreeMap::from([(0, message_pk_x), (1, message_pk_y)]),
1380        ));
1381
1382        let context = Some(b"test".to_vec());
1383        let proof_spec = ProofSpec::new(
1384            statements.clone(),
1385            meta_statements.clone(),
1386            vec![],
1387            context.clone(),
1388        );
1389        proof_spec.validate().unwrap();
1390
1391        let nonce_eq_pk = Some(b"test nonce".to_vec());
1392        let proof_eq_pk = Proof::new::<StdRng, Blake2b512>(
1393            &mut rng,
1394            proof_spec.clone(),
1395            witnesses.clone(),
1396            nonce_eq_pk.clone(),
1397            Default::default(),
1398        )
1399        .unwrap()
1400        .0;
1401
1402        proof_bbs
1403            .verify(
1404                &BTreeMap::from([(2, message_pet)]),
1405                &challenge,
1406                issuer_keypair.public_key.clone(),
1407                sig_params_g1.clone(),
1408            )
1409            .expect("Verify BBS proof");
1410
1411        let mut verifier_statements = Statements::<Bls12_381>::new();
1412        verifier_statements.add(PedersenCommitmentStmt::new_statement_from_params(
1413            vec![comm_key_bls.g, comm_key_bls.h],
1414            commitment_pub_x,
1415        ));
1416        verifier_statements.add(PedersenCommitmentStmt::new_statement_from_params(
1417            vec![comm_key_bls.g, comm_key_bls.h],
1418            commitment_pub_y,
1419        ));
1420        verifier_statements.add(PoKBBSSignatureG1Verifier::new_statement_from_params(
1421            sig_params_g1.clone(),
1422            issuer_keypair.public_key.clone(),
1423            BTreeMap::from([(2, message_pet)]),
1424        ));
1425        let verifier_proof_spec = ProofSpec::new(
1426            verifier_statements.clone(),
1427            meta_statements.clone(),
1428            vec![],
1429            context.clone(),
1430        );
1431        verifier_proof_spec.validate().unwrap();
1432
1433        proof_eq_pk
1434            .verify::<StdRng, Blake2b512>(
1435                &mut rng,
1436                verifier_proof_spec,
1437                nonce_eq_pk.clone(),
1438                Default::default(),
1439            )
1440            .expect("Verify Point Equality Proof");
1441
1442        Ok(())
1443    }
1444}