noah_crypto/
anon_creds.rs

1//! Anonymous credentials enable a credential issuer to issue a credential (with some attributes)
2//! to a user, and the user can later, with anonymity, selectively disclose some attributes.
3
4use crate::{basic::matrix_sigma::SigmaTranscript, confidential_anon_creds::CACTranscript};
5use merlin::Transcript;
6use noah_algebra::{prelude::*, traits::Pairing};
7use serde_derive::{Deserialize, Serialize};
8
9pub(crate) const REVEAL_PROOF_DOMAIN: &[u8] = b"AC Reveal PoK";
10pub(crate) const REVEAL_PROOF_NEW_TRANSCRIPT_INSTANCE: &[u8] = b"AC Reveal PoK Instance";
11pub(crate) const COMMIT_NEW_TRANSCRIPT_INSTANCE: &[u8] = b"AC Commit SoK Instance";
12pub(crate) const POK_LABEL: &[u8] = b"Signature Message";
13
14/// Credential issuer public key (`ipk`).
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct CredentialIssuerPK<G1, G2> {
17    /// The public generator in `G2`.
18    pub gen2: G2,
19    /// The public parameter `x G2`.
20    pub xx2: G2,
21    /// The public parameter `z G1`.
22    pub zz1: G1,
23    /// The public parameter `x G2`.
24    pub zz2: G2,
25    /// The public parameter for each attribute, `y[i] G2`.
26    pub yy2: Vec<G2>,
27}
28
29impl<G1: Group, G2: Group> CredentialIssuerPK<G1, G2> {
30    /// Return the number of attributes supported by this credential issuer's public key.
31    pub fn num_attrs(&self) -> usize {
32        self.yy2.len()
33    }
34}
35
36/// Credential issue secret key (`isk`).
37#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
38pub struct CredentialIssuerSK<G1, S> {
39    /// The secret generator in `G1`.
40    pub gen1: G1,
41    /// The secret value `x`.
42    pub x: S,
43    /// The secret key for individual attributes.
44    pub y: Vec<S>,
45}
46
47/// Credential signature (`\sigma`).
48#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
49pub struct CredentialSig<G1> {
50    /// First element of the signature.
51    pub sigma1: G1,
52    /// Second element of the signature.
53    pub sigma2: G1,
54}
55
56impl<G: Group> Default for CredentialSig<G> {
57    fn default() -> CredentialSig<G> {
58        CredentialSig {
59            sigma1: G::get_identity(),
60            sigma2: G::get_identity(),
61        }
62    }
63}
64
65/// Credential data structure: credential signature, attribute, and the issuer public key.
66#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
67pub struct Credential<G1, G2, AttrType> {
68    /// The credential signature.
69    pub sig: CredentialSig<G1>,
70    /// The list of all attributes.
71    pub attrs: Vec<AttrType>,
72    /// The issuer public key.
73    pub ipk: CredentialIssuerPK<G1, G2>,
74}
75
76impl<G1: Group, G2: Group, AttrType: Copy> Credential<G1, G2, AttrType> {
77    /// Apply the reveal map to get revealed attributes.
78    pub fn get_revealed_attributes(&self, reveal_map: &[bool]) -> Result<Vec<AttrType>> {
79        if reveal_map.len() != self.attrs.len() {
80            return Err(eg!(NoahError::ParameterError));
81        }
82        Ok(self
83            .attrs
84            .iter()
85            .zip(reveal_map)
86            .filter(|(_, b)| *(*b))
87            .map(|(a, _)| *a)
88            .collect())
89    }
90}
91
92/// Credential commitment attached to specific data (`cm`),
93/// which is a randomized version of the signature.
94#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
95pub struct CredentialComm<G1>(pub(crate) CredentialSig<G1>);
96
97impl<G1: Group> Default for CredentialComm<G1> {
98    fn default() -> Self {
99        Self(CredentialSig::<G1>::default())
100    }
101}
102
103impl<G1: Group> CredentialComm<G1> {
104    /// Derive the commitment from the credential signature and the randomizer.
105    pub fn new(sig: &CredentialSig<G1>, rand: &CredentialCommRandomizer<G1::ScalarType>) -> Self {
106        let sigma1_r = sig.sigma1.mul(&rand.r);
107        let sigma2_r = sig.sigma2.add(&sig.sigma1.mul(&rand.t)).mul(&rand.r);
108        // sigma2: r * (sigma2 + t * sigma1)
109
110        Self(CredentialSig::<G1> {
111            sigma1: sigma1_r,
112            sigma2: sigma2_r,
113        })
114    }
115}
116
117/// User public key (`upk`).
118#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
119pub struct CredentialUserPK<G1>(pub(crate) G1);
120
121/// User secret key (`usk`).
122#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
123pub struct CredentialUserSK<S>(pub(crate) S);
124
125/// Proof of selective disclosure of the attributes inside a signature `\sigma`.
126#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
127pub struct CredentialSigOpenProof<G1, G2, S> {
128    /// The credential commitment.
129    pub cm: CredentialComm<G1>,
130    /// The opening proof.
131    pub proof_open: CredentialPoK<G2, S>,
132}
133
134/// Proof that revealed attributes verify a credential commitment signature.
135pub type CredentialCommOpenProof<G2, S> = CredentialPoK<G2, S>;
136
137/// Proof of knowledge for t, sk (UserSecretKey), and hidden attributes that satisfy a
138/// certain relation..
139#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
140pub struct CredentialPoK<G2, S> {
141    pub(crate) blinding: G2, // r_t * G2 + r_sk * Z2 + sum_{a_i in hidden attrs} r_{a_i} * Y2_i
142    pub(crate) response_t: S, // c * t + r_t
143    pub(crate) response_sk: S, // c * sk + r_sk
144    pub(crate) response_attrs: Vec<S>, // {c * a_i + r_{a_i}; a_i in hidden}
145}
146
147#[derive(Clone)]
148/// An attribute in the anonymous credential scheme.
149pub enum Attribute<AttrType: Copy> {
150    /// A revealed attribute.
151    Revealed(AttrType),
152    /// A hidden attribute.
153    /// The prover must provide the attribute value, but the verifier does not.
154    Hidden(Option<AttrType>),
155}
156
157/// Randomizer used in the commitment scheme for credentials.
158#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
159pub struct CredentialCommRandomizer<S> {
160    /// The first randomizer value.
161    pub r: S,
162    /// The second randomizer value.
163    pub t: S,
164}
165
166/// The commitment scheme output.
167pub type CommOutput<G1, G2, S> = (
168    CredentialComm<G1>,
169    CredentialPoK<G2, S>,
170    Option<CredentialCommRandomizer<S>>,
171);
172
173/// Each credential issuer can generate a pair of keys, where the issuer secret key `isk` is used to
174/// issue attributes to a specific user, and the issuer public key `ipk` is used by the public to
175/// verify the issued attributes of a given user.
176pub fn issuer_keygen<R: CryptoRng + RngCore, P: Pairing>(
177    prng: &mut R,
178    num_attrs: usize,
179) -> (
180    CredentialIssuerSK<P::G1, P::ScalarField>,
181    CredentialIssuerPK<P::G1, P::G2>,
182) {
183    let x = P::ScalarField::random(prng);
184    let z = P::ScalarField::random(prng);
185    let gen1: P::G1 = P::G1::random(prng);
186    let gen2 = P::G2::random(prng);
187    let mut y = vec![];
188    let mut yy2 = vec![];
189    for _ in 0..num_attrs {
190        let yi = P::ScalarField::random(prng);
191        yy2.push(gen2.mul(&yi));
192        y.push(yi);
193    }
194    let xx2 = gen2.mul(&x);
195    let zz1 = gen1.mul(&z);
196    let zz2 = gen2.mul(&z);
197    (
198        CredentialIssuerSK { gen1, x, y },
199        CredentialIssuerPK {
200            gen2,
201            xx2,
202            zz1,
203            zz2,
204            yy2,
205        },
206    )
207}
208
209/// Each user can create a pair of keys `(usk, upk)` under a specific issuer. The user secret key
210/// `usk` is used to claim ownership of an issued credential. The user public key `upk` is used by
211/// the public to verify such a claim.
212pub fn user_keygen<R: CryptoRng + RngCore, P: Pairing>(
213    prng: &mut R,
214    ipk: &CredentialIssuerPK<P::G1, P::G2>,
215) -> (CredentialUserSK<P::ScalarField>, CredentialUserPK<P::G1>) {
216    let sk = P::ScalarField::random(prng);
217    let pk = ipk.zz1.mul(&sk);
218    (CredentialUserSK(sk), CredentialUserPK(pk))
219}
220
221/// The credential issuer can use the issuer secret key `isk` to grant a number of attributes (the
222/// contents of the attributes are described in `\vec{attrs}` to a user, given this user's public
223/// key `upk`.
224pub fn grant_credential<R: CryptoRng + RngCore, P: Pairing>(
225    prng: &mut R,
226    isk: &CredentialIssuerSK<P::G1, P::ScalarField>,
227    upk: &CredentialUserPK<P::G1>,
228    attrs: &[P::ScalarField],
229) -> Result<CredentialSig<P::G1>> {
230    let number_attributes_from_issuer_sk = isk.y.len();
231    let n = attrs.len();
232    if number_attributes_from_issuer_sk != n {
233        return Err(eg!(NoahError::AnonymousCredentialSignError));
234    }
235
236    let u = P::ScalarField::random(prng);
237    let mut exponent = isk.x;
238    for (attr, yi) in attrs.iter().zip(isk.y.iter()) {
239        exponent = exponent.add(&attr.mul(yi));
240    }
241    let cc = isk.gen1.mul(&exponent);
242    Ok(CredentialSig::<P::G1> {
243        sigma1: isk.gen1.mul(&u),
244        sigma2: upk.0.add(&cc).mul(&u),
245    })
246}
247
248/// Selectively reveal the attributes within the credential that is granted by the credential issuer
249/// with public key `ipk`.
250pub fn open_credential<R: CryptoRng + RngCore, P: Pairing>(
251    prng: &mut R,
252    usk: &CredentialUserSK<P::ScalarField>,
253    credential: &Credential<P::G1, P::G2, P::ScalarField>,
254    reveal_map: &[bool],
255) -> Result<CredentialSigOpenProof<P::G1, P::G2, P::ScalarField>> {
256    let rand = randomizer_gen::<_, P>(prng);
257    let cm = CredentialComm::<P::G1>::new(&credential.sig, &rand);
258
259    let proof_open = open_comm::<_, P>(prng, usk, credential, &cm, &rand, reveal_map)?;
260
261    Ok(CredentialSigOpenProof { cm, proof_open })
262}
263
264/// Sample a randomizer used to generate a commitment from the signature.
265pub fn randomizer_gen<R: CryptoRng + RngCore, P: Pairing>(
266    prng: &mut R,
267) -> CredentialCommRandomizer<P::ScalarField> {
268    CredentialCommRandomizer {
269        r: P::ScalarField::random(prng),
270        t: P::ScalarField::random(prng),
271    }
272}
273
274/// Credential commitment associated with a message.
275pub fn commit_without_randomizer<R: CryptoRng + RngCore, P: Pairing>(
276    prng: &mut R,
277    usk: &CredentialUserSK<P::ScalarField>,
278    credential: &Credential<P::G1, P::G2, P::ScalarField>,
279    m: &[u8],
280) -> Result<CommOutput<P::G1, P::G2, P::ScalarField>> {
281    let rand = randomizer_gen::<_, P>(prng);
282    let output = commit::<_, P>(prng, usk, credential, &rand, m).c(d!())?;
283    let cm = output.0;
284    let proof_valid = output.1;
285
286    Ok((cm, proof_valid, Some(rand)))
287}
288
289/// Commit a credential over a message `m` under the credential `\sigma`, the user secret key `usk`,
290/// and randomizer `rand`. The signature `\sigma` corresponds to the attributes in `\vec{attrs}`,
291/// granted by the credential issuer with public key `ipk`. The output is a commitment `cm` and a
292/// validity proof `\pi_{valid}`.
293pub fn commit<R: CryptoRng + RngCore, P: Pairing>(
294    prng: &mut R,
295    usk: &CredentialUserSK<P::ScalarField>,
296    credential: &Credential<P::G1, P::G2, P::ScalarField>,
297    rand: &CredentialCommRandomizer<P::ScalarField>,
298    m: &[u8],
299) -> Result<CommOutput<P::G1, P::G2, P::ScalarField>> {
300    let hidden_attrs = credential
301        .attrs
302        .iter()
303        .map(|attr| Attribute::Hidden(Some(*attr)))
304        .collect_vec(); // all hidden
305
306    let cm = CredentialComm::<P::G1>::new(&credential.sig, rand);
307    let mut transcript = Transcript::new(COMMIT_NEW_TRANSCRIPT_INSTANCE);
308    init_pok_transcript::<P>(&mut transcript, &credential.ipk, &cm);
309    transcript.append_message(POK_LABEL, m);
310    let proof_valid = prove_pok::<_, P>(
311        &mut transcript,
312        prng,
313        usk,
314        &credential.ipk,
315        &rand.t,
316        hidden_attrs.as_slice(),
317    )
318    .c(d!())?;
319
320    Ok((cm, proof_valid, None))
321}
322
323/// Check if a commitment is valid; that is, if it commits to a credential issued by `ipk`.
324pub fn check_comm<P: Pairing>(
325    ipk: &CredentialIssuerPK<P::G1, P::G2>,
326    cm: &CredentialComm<P::G1>,
327    proof_valid: &CredentialPoK<P::G2, P::ScalarField>,
328    m: &[u8],
329) -> Result<()> {
330    let mut transcript = Transcript::new(COMMIT_NEW_TRANSCRIPT_INSTANCE);
331    init_pok_transcript::<P>(&mut transcript, ipk, cm);
332    transcript.append_message(POK_LABEL, m);
333
334    transcript.append_proof_commitment(&proof_valid.blinding);
335    let challenge = transcript.get_challenge::<P::ScalarField>();
336
337    let attrs: Vec<Attribute<P::ScalarField>> = vec![Attribute::Hidden(None); ipk.num_attrs()];
338
339    verify_pok::<P>(ipk, cm, proof_valid, attrs.as_slice(), &challenge)
340}
341
342/// Selectively reveal some attributes of the credential previously committed, where `\sigma` is
343/// the signature of attributes signed by the credential provider, `rand` is the randomizer,
344/// `\vec{attrs}` represents the user's attributes, and `\vec{reveal_map}` is the binary vector
345/// indicating whether or not an attribute is revealed.
346pub fn open_comm<R: CryptoRng + RngCore, P: Pairing>(
347    prng: &mut R,
348    usk: &CredentialUserSK<P::ScalarField>,
349    credential: &Credential<P::G1, P::G2, P::ScalarField>,
350    cm: &CredentialComm<P::G1>,
351    rand: &CredentialCommRandomizer<P::ScalarField>,
352    reveal_map: &[bool],
353) -> Result<CredentialCommOpenProof<P::G2, P::ScalarField>> {
354    if credential.attrs.len() != reveal_map.len() {
355        return Err(eg!(NoahError::ParameterError));
356    }
357
358    let revealed_attrs = credential
359        .attrs
360        .iter()
361        .zip(reveal_map.iter())
362        .map(|(attr, b)| {
363            if *b {
364                Attribute::Revealed(*attr)
365            } else {
366                Attribute::Hidden(Some(*attr))
367            }
368        })
369        .collect_vec();
370
371    let mut transcript = Transcript::new(REVEAL_PROOF_NEW_TRANSCRIPT_INSTANCE);
372    init_pok_transcript::<P>(&mut transcript, &credential.ipk, &cm); // public parameters
373    let pok = prove_pok::<_, P>(
374        &mut transcript,
375        prng,
376        usk,
377        &credential.ipk,
378        &rand.t,
379        revealed_attrs.as_slice(),
380    )
381    .c(d!())?;
382
383    Ok(pok)
384}
385
386/// Given a commitment `cm`, the message `m`, the revealing proof `\pi`, the claimed attributes
387/// `\vec{attrs}`, and a vector indicating revealed attributes `\vec{reveal_map}`, this function
388/// checks if the claimed attributes are indeed signed by the credential issuer (with the public
389/// key `ipk`) over the credential committed in `cm`.
390pub fn verify_open<P: Pairing>(
391    ipk: &CredentialIssuerPK<P::G1, P::G2>,
392    cm: &CredentialComm<P::G1>,
393    proof_open: &CredentialCommOpenProof<P::G2, P::ScalarField>,
394    attrs: &[Attribute<P::ScalarField>],
395) -> Result<()> {
396    let mut transcript = Transcript::new(REVEAL_PROOF_NEW_TRANSCRIPT_INSTANCE);
397    init_pok_transcript::<P>(&mut transcript, ipk, cm);
398
399    transcript.append_proof_commitment(&proof_open.blinding);
400    let challenge = transcript.get_challenge::<P::ScalarField>();
401
402    verify_pok::<P>(ipk, cm, proof_open, attrs, &challenge)
403}
404
405pub(super) fn init_pok_transcript<P: Pairing>(
406    transcript: &mut Transcript,
407    ipk: &CredentialIssuerPK<P::G1, P::G2>,
408    cm: &CredentialComm<P::G1>,
409) {
410    let g1 = P::G1::get_base();
411    let g2 = P::G2::get_base();
412    let g1_elems = vec![&g1, &ipk.zz1, &cm.0.sigma1, &cm.0.sigma2];
413    let mut g2_elems = vec![&g2, &ipk.gen2, &ipk.xx2, &ipk.zz2];
414    for e in ipk.yy2.iter() {
415        g2_elems.push(e);
416    }
417    transcript.init_sigma_pairing::<P>(REVEAL_PROOF_DOMAIN, &g1_elems[..], g2_elems.as_slice());
418}
419
420/// Internal function for generating a proof of knowledge.
421fn prove_pok<R: CryptoRng + RngCore, P: Pairing>(
422    transcript: &mut Transcript,
423    prng: &mut R,
424    usk: &CredentialUserSK<P::ScalarField>,
425    ipk: &CredentialIssuerPK<P::G1, P::G2>,
426    t: &P::ScalarField,
427    attrs: &[Attribute<P::ScalarField>],
428) -> Result<CredentialPoK<P::G2, P::ScalarField>> {
429    let beta1 = P::ScalarField::random(prng);
430    let beta2 = P::ScalarField::random(prng);
431    let mut gamma = vec![];
432    let mut blinding = ipk.gen2.mul(&beta1).add(&ipk.zz2.mul(&beta2));
433    for (yy2i, attr) in ipk.yy2.iter().zip(attrs) {
434        match attr {
435            Attribute::Hidden(Some(_)) => {
436                let gamma_i = P::ScalarField::random(prng);
437                let elem = yy2i.mul(&gamma_i);
438                blinding = blinding.add(&elem);
439                gamma.push(gamma_i);
440            }
441            Attribute::Hidden(None) => {
442                return Err(eg!(NoahError::ParameterError));
443            }
444            _ => {}
445        }
446    }
447    transcript.append_proof_commitment(&blinding);
448    let challenge = transcript.get_challenge::<P::ScalarField>();
449    let response_t = challenge.mul(t).add(&beta1); // challenge*t + beta1
450    let response_sk = challenge.mul(&usk.0).add(&beta2);
451    let mut response_attrs = vec![];
452    let mut gamma_iter = gamma.iter();
453    for attr_enum in attrs {
454        if let Attribute::Hidden(Some(attr)) = attr_enum {
455            let gamma = gamma_iter.next().unwrap(); // safe unwrap()
456            let resp_attr_i = challenge.mul(*attr).add(gamma);
457            response_attrs.push(resp_attr_i);
458        }
459    }
460    Ok(CredentialPoK {
461        blinding,
462        response_t,
463        response_sk,
464        response_attrs,
465    })
466}
467
468/// Internal function for verify a proof of knowledge.
469pub(crate) fn verify_pok<P: Pairing>(
470    ipk: &CredentialIssuerPK<P::G1, P::G2>,
471    cm: &CredentialComm<P::G1>,
472    proof_open: &CredentialPoK<P::G2, P::ScalarField>,
473    attrs: &[Attribute<P::ScalarField>],
474    challenge: &P::ScalarField,
475) -> Result<()> {
476    // p = X_2*c - proof_blinding + &G2 * r_t + Z2 * r_sk + \sum r_attr_i * Y2_i;
477    let minus_one: P::ScalarField = P::ScalarField::one().neg();
478    let mut scalars = vec![
479        &proof_open.response_t,  // G2
480        challenge,               // X2
481        &proof_open.response_sk, // Z2
482        &minus_one,              // Commitment
483    ];
484
485    let mut resp_attr_iter = proof_open.response_attrs.iter();
486
487    let attrs_times_challenge = attrs
488        .iter()
489        .map(|attr| match attr {
490            Attribute::Revealed(attr) => Some(attr.mul(challenge)),
491            _ => None,
492        })
493        .collect_vec();
494    for attr in attrs_times_challenge.iter() {
495        match attr {
496            Some(a) => {
497                scalars.push(a);
498            }
499            None => {
500                let response = resp_attr_iter.next().c(d!(NoahError::ParameterError))?;
501                scalars.push(response);
502            }
503        }
504    }
505    let mut elems = vec![&ipk.gen2, &ipk.xx2, &ipk.zz2, &proof_open.blinding];
506
507    for y in ipk.yy2.iter() {
508        elems.push(y);
509    }
510    let p = P::G2::multi_exp(scalars.as_slice(), elems.as_slice());
511
512    let lhs = P::pairing(&cm.0.sigma1, &p);
513    let rhs = P::pairing(&cm.0.sigma2.mul(challenge), &ipk.gen2);
514
515    if lhs == rhs {
516        Ok(())
517    } else {
518        Err(eg!(NoahError::IdentityRevealVerifyError))
519    }
520}
521
522#[cfg(test)]
523pub(crate) mod credentials_tests {
524    use super::*;
525    use crate::anon_creds::Attribute::{Hidden, Revealed};
526    use noah_algebra::bls12_381::BLSPairingEngine;
527
528    fn check_signatures<P: Pairing>(n: usize) {
529        let mut prng = test_rng();
530
531        let ikeypair = issuer_keygen::<_, P>(&mut prng, n);
532        let isk = &ikeypair.0;
533        let ipk = &ikeypair.1;
534        let (_, upk) = user_keygen::<_, P>(&mut prng, ipk);
535
536        let mut attrs = vec![];
537        for _ in 0..n {
538            attrs.push(P::ScalarField::random(&mut prng));
539        }
540
541        let sig = grant_credential::<_, P>(&mut prng, &isk, &upk, &attrs);
542        assert!(sig.is_ok());
543
544        if n > 1 {
545            let error = grant_credential::<_, P>(&mut prng, &isk, &upk, &attrs[0..0]);
546            assert!(error.is_err());
547        }
548    }
549
550    #[test]
551    fn test_issuer_keygen() {
552        let mut prng = test_rng();
553
554        let _ = issuer_keygen::<_, BLSPairingEngine>(&mut prng, 5);
555        let _ = issuer_keygen::<_, BLSPairingEngine>(&mut prng, 0);
556    }
557
558    #[test]
559    fn test_signing() {
560        for n in 0..16 {
561            check_signatures::<BLSPairingEngine>(n);
562        }
563    }
564
565    fn reveal(reveal_map: &[bool]) {
566        type P = BLSPairingEngine;
567        let n = reveal_map.len();
568        let mut prng = test_rng();
569
570        let ikeypair = issuer_keygen::<_, P>(&mut prng, n);
571        let isk = &ikeypair.0;
572        let ipk = &ikeypair.1;
573        let (usk, upk) = user_keygen::<_, P>(&mut prng, ipk);
574
575        let mut attrs = vec![];
576        for _ in reveal_map.iter() {
577            attrs.push(<P as Pairing>::ScalarField::random(&mut prng));
578        }
579
580        let sig = grant_credential::<_, P>(&mut prng, &isk, &upk, attrs.as_slice()).unwrap();
581
582        let credential = Credential {
583            sig,
584            attrs,
585            ipk: ipk.clone(),
586        };
587
588        let reveal_sig = open_credential::<_, P>(&mut prng, &usk, &credential, reveal_map).unwrap();
589
590        let revealed_attrs = credential
591            .attrs
592            .iter()
593            .zip(reveal_map.iter())
594            .map(|(a, b)| if *b { Revealed(*a) } else { Hidden(None) })
595            .collect_vec();
596
597        assert_eq!(
598            true,
599            verify_open::<P>(
600                &ipk,
601                &reveal_sig.cm,
602                &reveal_sig.proof_open,
603                revealed_attrs.as_slice()
604            )
605            .is_ok()
606        )
607    }
608
609    pub(crate) fn no_attributes() {
610        reveal(&[]);
611    }
612
613    pub(crate) fn single_attribute() {
614        reveal(&[false]);
615        reveal(&[true]);
616    }
617
618    pub(crate) fn two_attributes() {
619        reveal(&[false, false]);
620        reveal(&[true, false]);
621        reveal(&[false, true]);
622        reveal(&[true, true]);
623    }
624
625    pub(crate) fn ten_attributes() {
626        reveal(&[false; 10]);
627        reveal(&[
628            true, false, true, false, true, false, true, false, true, false,
629        ]);
630        reveal(&[
631            false, true, false, true, false, true, false, true, false, true,
632        ]);
633        reveal(&[true; 10]);
634    }
635
636    #[test]
637    pub(crate) fn test_attributes() {
638        no_attributes();
639        single_attribute();
640        two_attributes();
641        ten_attributes();
642    }
643}