Browse Source

Blind issuing and verification of both the client's and issuer's proofs

Ian Goldberg 3 years ago
parent
commit
69fea6b907
2 changed files with 309 additions and 1 deletions
  1. 293 1
      src/ggm.rs
  2. 16 0
      tests/ggm.rs

+ 293 - 1
src/ggm.rs

@@ -98,7 +98,8 @@ define_proof! {
     X4 = (x4*A),
     X4 = (x4*A),
     X5 = (x5*A),
     X5 = (x5*A),
     X0 = (x0*B + x0tilde*A),
     X0 = (x0*B + x0tilde*A),
-    Q = (x0*P + x1*P1 + x2*P2 + x3*P3 + x4*P4 + x5*P5) }
+    Q = (x0*P + x1*P1 + x2*P2 + x3*P3 + x4*P4 + x5*P5)
+}
 
 
 impl Issuer {
 impl Issuer {
     // Create an issuer for credentials with the given number of
     // Create an issuer for credentials with the given number of
@@ -176,6 +177,41 @@ pub struct CredentialResponse_Nonblind_5 {
     pi: CompactProof,
     pi: CompactProof,
 }
 }
 
 
+// Example of a 5-attribute credential where the issuer sees attributes
+// 3 and 5, but attributes 1, 2, and 4 are blinded.
+pub struct CredentialRequest_Blind124_5 {
+    D: RistrettoPoint,
+    Encm1B: (RistrettoPoint, RistrettoPoint),
+    Encm2B: (RistrettoPoint, RistrettoPoint),
+    m3: Scalar,
+    Encm4B: (RistrettoPoint, RistrettoPoint),
+    m5: Scalar,
+    piUserBlinding: CompactProof,
+}
+
+#[derive(Debug)]
+pub struct CredentialRequestState_Blind124_5 {
+    d: Scalar,
+    D: RistrettoPoint,
+    Encm1B: (RistrettoPoint, RistrettoPoint),
+    Encm2B: (RistrettoPoint, RistrettoPoint),
+    Encm4B: (RistrettoPoint, RistrettoPoint),
+    m1: Scalar,
+    m2: Scalar,
+    m3: Scalar,
+    m4: Scalar,
+    m5: Scalar,
+}
+
+pub struct CredentialResponse_Blind124_5 {
+    P: RistrettoPoint,
+    EncQ: (RistrettoPoint, RistrettoPoint),
+    T1: RistrettoPoint,
+    T2: RistrettoPoint,
+    T4: RistrettoPoint,
+    piBlindIssue: CompactProof,
+}
+
 #[derive(Debug)]
 #[derive(Debug)]
 pub struct Credential_5 {
 pub struct Credential_5 {
     P: RistrettoPoint,
     P: RistrettoPoint,
@@ -240,3 +276,259 @@ pub fn verify_nonblind_5(req: &CredentialRequest_Nonblind_5,
         m5: req.m5,
         m5: req.m5,
     })
     })
 }
 }
+
+// The client-created proof that the blinded attributes in the request
+// to issue a credential are well formed.  If you want the client to
+// prove other statements about the blinded attributes (m1, m2, m4 in
+// this example), this is where to add them (and in the code that
+// creates and verifies this proof of course).
+define_proof! {
+    userblinding_124_5,
+    "Blind124 5 userblind proof",
+    (d, e1, e2, e4, m1, m2, m4),
+    (Encm1B0, Encm1B1, Encm2B0, Encm2B1, Encm4B0, Encm4B1, D),
+    (B) :
+    Encm1B0 = (e1*B),
+    Encm1B1 = (m1*B + e1*D),
+    Encm2B0 = (e2*B),
+    Encm2B1 = (m2*B + e2*D),
+    Encm4B0 = (e4*B),
+    Encm4B1 = (m4*B + e4*D),
+    D = (d*B)
+}
+
+// The issuer-created proof for the same scenario
+define_proof! {
+    blindissue_124_5,
+    "Blind124 5 issuing proof",
+    (x0, x0tilde, x1, x2, x3, x4, x5, s, b, t1, t2, t4),
+    (P, EncQ0, EncQ1, X0, X1, X2, X3, X4, X5, P3, P5, T1, T2, T4, D,
+        Encm1B0, Encm1B1, Encm2B0, Encm2B1, Encm4B0, Encm4B1),
+    (A, B) :
+    X1 = (x1*A),
+    X2 = (x2*A),
+    X3 = (x3*A),
+    X4 = (x4*A),
+    X5 = (x5*A),
+    X0 = (x0*B + x0tilde*A),
+    P = (b*B),
+    T1 = (b*X1),
+    T2 = (b*X2),
+    T4 = (b*X4),
+    T1 = (t1*A),
+    T2 = (t2*A),
+    T4 = (t4*A),
+    EncQ0 = (s*B + t1*Encm1B0 + t2*Encm2B0 + t4*Encm4B0),
+    EncQ1 = (s*D + t1*Encm1B1 + t2*Encm2B1 + t4*Encm4B1 +
+            x0*P + x3*P3 + x5*P5)
+}
+
+pub fn request_blind124_5(m1: &Scalar, m2: &Scalar, m3: &Scalar,
+        m4: &Scalar, m5: &Scalar) -> (CredentialRequest_Blind124_5,
+        CredentialRequestState_Blind124_5) {
+    let B : &RistrettoPoint = &CMZ_B;
+    let Btable : &RistrettoBasepointTable = &CMZ_B_TABLE;
+
+    // Pick an ElGamal keypair
+    let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
+    let d: Scalar = Scalar::random(&mut rng);
+    let D: RistrettoPoint = &d * Btable;
+
+    let e1: Scalar = Scalar::random(&mut rng);
+    let e2: Scalar = Scalar::random(&mut rng);
+    let e4: Scalar = Scalar::random(&mut rng);
+    let Encm1B = (&e1 * Btable, m1 * Btable + &e1 * D);
+    let Encm2B = (&e2 * Btable, m2 * Btable + &e2 * D);
+    let Encm4B = (&e4 * Btable, m4 * Btable + &e4 * D);
+
+    let mut transcript = Transcript::new(b"Blind124 5 userblind proof");
+    let piUserBlinding: CompactProof = userblinding_124_5::prove_compact(
+        &mut transcript,
+        userblinding_124_5::ProveAssignments {
+            B: &B,
+            Encm1B0: &Encm1B.0,
+            Encm1B1: &Encm1B.1,
+            Encm2B0: &Encm2B.0,
+            Encm2B1: &Encm2B.1,
+            Encm4B0: &Encm4B.0,
+            Encm4B1: &Encm4B.1,
+            D: &D,
+            d: &d,
+            e1: &e1,
+            e2: &e2,
+            e4: &e4,
+            m1: &m1,
+            m2: &m2,
+            m4: &m4,
+        }).0;
+    (
+        CredentialRequest_Blind124_5 {
+            D: D,
+            Encm1B, Encm2B, Encm4B, piUserBlinding,
+            m3: *m3,
+            m5: *m5,
+        },
+        CredentialRequestState_Blind124_5 {
+            d, D, Encm1B, Encm2B, Encm4B,
+            m1: *m1,
+            m2: *m2,
+            m3: *m3,
+            m4: *m4,
+            m5: *m5,
+        }
+    )
+}
+
+impl Issuer {
+    // Issue a credential with 5 attributes, of which attributes 1, 2,
+    // and 4 are blinded from the issuer, and 3 and 5 are visible.
+    pub fn issue_blind124_5(&self, req: &CredentialRequest_Blind124_5)
+            -> Result<CredentialResponse_Blind124_5, ProofError> {
+        let A : &RistrettoPoint = &CMZ_A;
+        let B : &RistrettoPoint = &CMZ_B;
+
+        // First check the proof in the request
+        let mut transcript = Transcript::new(b"Blind124 5 userblind proof");
+        userblinding_124_5::verify_compact(
+            &req.piUserBlinding,
+            &mut transcript,
+            userblinding_124_5::VerifyAssignments {
+                B: &B.compress(),
+                Encm1B0: &req.Encm1B.0.compress(),
+                Encm1B1: &req.Encm1B.1.compress(),
+                Encm2B0: &req.Encm2B.0.compress(),
+                Encm2B1: &req.Encm2B.1.compress(),
+                Encm4B0: &req.Encm4B.0.compress(),
+                Encm4B1: &req.Encm4B.1.compress(),
+                D: &req.D.compress(),
+            }
+        )?;
+
+        // Compute the MAC on the visible attributes
+        let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
+        let b: Scalar = Scalar::random(&mut rng);
+        let P: RistrettoPoint = b * B;
+        let QHc: RistrettoPoint = (self.privkey.x[0] + (
+            self.privkey.x[3] * req.m3 +
+            self.privkey.x[5] * req.m5)) * P;
+
+        // El Gamal encrypt it to the public key req.D
+        let s: Scalar = Scalar::random(&mut rng);
+        let EncQHc = (s*B, QHc + s*req.D);
+
+        // Homomorphically compute the part of the MAC corresponding to
+        // the blinded attributes
+        let t1 = self.privkey.x[1] * b;
+        let T1 = t1 * A;
+        let EncQ1 = ( t1 * req.Encm1B.0, t1 * req.Encm1B.1 );
+        let t2 = self.privkey.x[2] * b;
+        let T2 = t2 * A;
+        let EncQ2 = ( t2 * req.Encm2B.0, t2 * req.Encm2B.1 );
+        let t4 = self.privkey.x[4] * b;
+        let T4 = t4 * A;
+        let EncQ4 = ( t4 * req.Encm4B.0, t4 * req.Encm4B.1 );
+
+        let EncQ = ( EncQHc.0 + EncQ1.0 + EncQ2.0 + EncQ4.0,
+                     EncQHc.1 + EncQ1.1 + EncQ2.1 + EncQ4.1 );
+
+        let mut transcript = Transcript::new(b"Blind124 5 issuing proof");
+        let piBlindIssue: CompactProof = blindissue_124_5::prove_compact(
+            &mut transcript,
+            blindissue_124_5::ProveAssignments {
+                A: &A,
+                B: &B,
+                P: &P,
+                EncQ0: &EncQ.0,
+                EncQ1: &EncQ.1,
+                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],
+                P3: &(&req.m3 * &P),
+                P5: &(&req.m5 * &P),
+                T1: &T1,
+                T2: &T2,
+                T4: &T4,
+                D: &req.D,
+                Encm1B0: &req.Encm1B.0,
+                Encm1B1: &req.Encm1B.1,
+                Encm2B0: &req.Encm2B.0,
+                Encm2B1: &req.Encm2B.1,
+                Encm4B0: &req.Encm4B.0,
+                Encm4B1: &req.Encm4B.1,
+                x0: &self.privkey.x[0],
+                x0tilde: &self.privkey.x0tilde,
+                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],
+                s: &s,
+                b: &b,
+                t1: &t1,
+                t2: &t2,
+                t4: &t4
+            }).0;
+
+        Ok(CredentialResponse_Blind124_5 {
+            P, EncQ, T1, T2, T4, piBlindIssue
+        })
+    }
+}
+
+pub fn verify_blind124_5(state: CredentialRequestState_Blind124_5,
+        resp: CredentialResponse_Blind124_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"Blind124 5 issuing proof");
+    blindissue_124_5::verify_compact(
+        &resp.piBlindIssue,
+        &mut transcript,
+        blindissue_124_5::VerifyAssignments {
+            A: &A.compress(),
+            B: &B.compress(),
+            P: &resp.P.compress(),
+            EncQ0: &resp.EncQ.0.compress(),
+            EncQ1: &resp.EncQ.1.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(),
+            P3: &(&state.m3 * &resp.P).compress(),
+            P5: &(&state.m5 * &resp.P).compress(),
+            T1: &resp.T1.compress(),
+            T2: &resp.T2.compress(),
+            T4: &resp.T4.compress(),
+            D: &state.D.compress(),
+            Encm1B0: &state.Encm1B.0.compress(),
+            Encm1B1: &state.Encm1B.1.compress(),
+            Encm2B0: &state.Encm2B.0.compress(),
+            Encm2B1: &state.Encm2B.1.compress(),
+            Encm4B0: &state.Encm4B.0.compress(),
+            Encm4B1: &state.Encm4B.1.compress(),
+        }
+    )?;
+
+    // Decrypt EncQ
+    let Q = &resp.EncQ.1 - (state.d * &resp.EncQ.0);
+
+    Ok(Credential_5 {
+        P: resp.P,
+        Q: Q,
+        m1: state.m1,
+        m2: state.m2,
+        m3: state.m3,
+        m4: state.m4,
+        m5: state.m5,
+    })
+}

+ 16 - 0
tests/ggm.rs

@@ -47,3 +47,19 @@ fn nonblind_5_test() {
     let result = verify_nonblind_5(&req, resp, &issuer.pubkey);
     let result = verify_nonblind_5(&req, resp, &issuer.pubkey);
     assert!(result.is_ok());
     assert!(result.is_ok());
 }
 }
+
+#[test]
+fn blind124_5_test() {
+    let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
+    let issuer = Issuer::new(5);
+    let m1 = Scalar::random(&mut rng);
+    let m2 = Scalar::random(&mut rng);
+    let m3 = Scalar::random(&mut rng);
+    let m4 = Scalar::random(&mut rng);
+    let m5 = Scalar::random(&mut rng);
+    let (req,state) = request_blind124_5(&m1, &m2, &m3, &m4, &m5);
+    let resp = issuer.issue_blind124_5(&req);
+    assert!(resp.is_ok());
+    let result = verify_blind124_5(state, resp.unwrap(), &issuer.pubkey);
+    assert!(result.is_ok());
+}