123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- // Implementation of CMZ14 credentials (GGM version, which is more
- // efficient, but makes a stronger security assumption): "Algebraic MACs
- // and Keyed-Verification Anonymous Credentials" (Chase, Meiklejohn,
- // and Zaverucha, CCS 2014)
- // The notation follows that of the paper "Hyphae: Social Secret
- // Sharing" (Lovecruft and de Valence, 2017), Section 4.
- #![allow(non_snake_case)]
- #![allow(non_camel_case_types)]
- use sha2::Sha512;
- use curve25519_dalek::constants as dalek_constants;
- use curve25519_dalek::ristretto::RistrettoPoint;
- use curve25519_dalek::ristretto::RistrettoBasepointTable;
- use curve25519_dalek::scalar::Scalar;
- use curve25519_dalek::traits::IsIdentity;
- use zkp::CompactProof;
- use zkp::ProofError;
- use zkp::Transcript;
- use lazy_static::lazy_static;
- lazy_static! {
- pub static ref CMZ_A: RistrettoPoint =
- RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
- pub static ref CMZ_B: RistrettoPoint =
- dalek_constants::RISTRETTO_BASEPOINT_POINT;
- pub static ref CMZ_A_TABLE: RistrettoBasepointTable =
- RistrettoBasepointTable::create(&CMZ_A);
- pub static ref CMZ_B_TABLE: RistrettoBasepointTable =
- dalek_constants::RISTRETTO_BASEPOINT_TABLE;
- }
- #[derive(Clone,Debug)]
- pub struct IssuerPrivKey {
- x0tilde: Scalar,
- x: Vec<Scalar>,
- }
- impl IssuerPrivKey {
- // Create an IssuerPrivKey for credentials with the given number of
- // attributes.
- pub fn new(n: u16) -> IssuerPrivKey {
- let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
- let x0tilde: Scalar = Scalar::random(&mut rng);
- let mut x: Vec<Scalar> = Vec::with_capacity((n+1) as usize);
- // Set x to a vector of n+1 random Scalars
- x.resize_with((n+1) as usize, || { Scalar::random(&mut rng) });
- IssuerPrivKey { x0tilde, x }
- }
- }
- #[derive(Clone,Debug)]
- pub struct IssuerPubKey {
- X: Vec<RistrettoPoint>,
- }
- impl IssuerPubKey {
- // Create an IssuerPubKey from the corresponding IssuerPrivKey
- pub fn new(privkey: &IssuerPrivKey) -> IssuerPubKey {
- let Atable : &RistrettoBasepointTable = &CMZ_A_TABLE;
- let Btable : &RistrettoBasepointTable = &CMZ_B_TABLE;
- let n_plus_one: usize = privkey.x.len();
- let mut X: Vec<RistrettoPoint> = Vec::with_capacity(n_plus_one);
- // The first element is a special case; it is
- // X[0] = x0tilde*A + x[0]*B
- X.push(&privkey.x0tilde * Atable + &privkey.x[0] * Btable);
- // The other elements (1 through n) are X[i] = x[i]*A
- for i in 1..n_plus_one {
- X.push(&privkey.x[i] * Atable);
- }
- IssuerPubKey { X }
- }
- }
- #[derive(Debug)]
- pub struct Issuer {
- privkey: IssuerPrivKey,
- pub pubkey: IssuerPubKey,
- }
- define_proof! {
- issue_nonblind_5,
- "Nonblind 5 issuing proof",
- (x0, x0tilde, x1, x2, x3, x4, x5),
- (P, Q, X0, X1, X2, X3, X4, X5, P1, P2, P3, P4, P5),
- (A, B) :
- X1 = (x1*A),
- X2 = (x2*A),
- X3 = (x3*A),
- X4 = (x4*A),
- X5 = (x5*A),
- X0 = (x0*B + x0tilde*A),
- Q = (x0*P + x1*P1 + x2*P2 + x3*P3 + x4*P4 + x5*P5) }
- impl Issuer {
- // Create an issuer for credentials with the given number of
- // attributes
- pub fn new(n: u16) -> Issuer {
- let privkey = IssuerPrivKey::new(n);
- let pubkey = IssuerPubKey::new(&privkey);
- Issuer { privkey, pubkey }
- }
- // Issue a credential with (for example) 5 given attributes. In
- // this (nonblinded) version, the issuer sees all of the attributes.
- pub fn issue_nonblind_5(&self, req: &CredentialRequest_Nonblind_5)
- -> CredentialResponse_Nonblind_5 {
- let A : &RistrettoPoint = &CMZ_A;
- let B : &RistrettoPoint = &CMZ_B;
- let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
- let b: Scalar = Scalar::random(&mut rng);
- let P: RistrettoPoint = b * B;
- // There is a typo in the Hyphae paper: in Section 4.1, Q should
- // also have an x0*P term (also in Q'). (You can see that term
- // in Section 4.2.)
- let Q: RistrettoPoint = (self.privkey.x[0] + (
- self.privkey.x[1] * req.m1 +
- self.privkey.x[2] * req.m2 +
- self.privkey.x[3] * req.m3 +
- self.privkey.x[4] * req.m4 +
- self.privkey.x[5] * req.m5)) * P;
- let mut transcript = Transcript::new(b"Nonblind 5 issuing proof");
- let pi: CompactProof = issue_nonblind_5::prove_compact(
- &mut transcript,
- issue_nonblind_5::ProveAssignments {
- A: &A,
- B: &B,
- P: &P,
- Q: &Q,
- X0: &self.pubkey.X[0],
- X1: &self.pubkey.X[1],
- X2: &self.pubkey.X[2],
- X3: &self.pubkey.X[3],
- X4: &self.pubkey.X[4],
- X5: &self.pubkey.X[5],
- P1: &(&req.m1 * &P),
- P2: &(&req.m2 * &P),
- P3: &(&req.m3 * &P),
- P4: &(&req.m4 * &P),
- P5: &(&req.m5 * &P),
- x0: &self.privkey.x[0],
- x1: &self.privkey.x[1],
- x2: &self.privkey.x[2],
- x3: &self.privkey.x[3],
- x4: &self.privkey.x[4],
- x5: &self.privkey.x[5],
- x0tilde: &self.privkey.x0tilde
- }).0;
- CredentialResponse_Nonblind_5 { P, Q, pi }
- }
- }
- #[derive(Debug)]
- pub struct CredentialRequest_Nonblind_5 {
- m1: Scalar,
- m2: Scalar,
- m3: Scalar,
- m4: Scalar,
- m5: Scalar,
- }
- pub struct CredentialResponse_Nonblind_5 {
- P: RistrettoPoint,
- Q: RistrettoPoint,
- pi: CompactProof,
- }
- #[derive(Debug)]
- pub struct Credential_5 {
- P: RistrettoPoint,
- Q: RistrettoPoint,
- m1: Scalar,
- m2: Scalar,
- m3: Scalar,
- m4: Scalar,
- m5: Scalar,
- }
- pub fn request_nonblind_5(m1: &Scalar, m2: &Scalar, m3: &Scalar,
- m4: &Scalar, m5: &Scalar) -> CredentialRequest_Nonblind_5 {
- // For nonblind requests, just send the attributes in the clear
- CredentialRequest_Nonblind_5 {
- m1: *m1,
- m2: *m2,
- m3: *m3,
- m4: *m4,
- m5: *m5
- }
- }
- pub fn verify_nonblind_5(req: &CredentialRequest_Nonblind_5,
- resp: CredentialResponse_Nonblind_5, pubkey: &IssuerPubKey)
- -> Result<Credential_5, ProofError> {
- let A : &RistrettoPoint = &CMZ_A;
- let B : &RistrettoPoint = &CMZ_B;
- if resp.P.is_identity() {
- return Err(ProofError::VerificationFailure);
- }
- let mut transcript = Transcript::new(b"Nonblind 5 issuing proof");
- issue_nonblind_5::verify_compact(
- &resp.pi,
- &mut transcript,
- issue_nonblind_5::VerifyAssignments {
- A: &A.compress(),
- B: &B.compress(),
- P: &resp.P.compress(),
- Q: &resp.Q.compress(),
- X0: &pubkey.X[0].compress(),
- X1: &pubkey.X[1].compress(),
- X2: &pubkey.X[2].compress(),
- X3: &pubkey.X[3].compress(),
- X4: &pubkey.X[4].compress(),
- X5: &pubkey.X[5].compress(),
- P1: &(&req.m1 * &resp.P).compress(),
- P2: &(&req.m2 * &resp.P).compress(),
- P3: &(&req.m3 * &resp.P).compress(),
- P4: &(&req.m4 * &resp.P).compress(),
- P5: &(&req.m5 * &resp.P).compress(),
- }
- )?;
- Ok(Credential_5 {
- P: resp.P,
- Q: resp.Q,
- m1: req.m1,
- m2: req.m2,
- m3: req.m3,
- m4: req.m4,
- m5: req.m5,
- })
- }
|