// 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::(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, } 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 = 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, } 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 = 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 { 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, }) }