github.com/kaituanwang/hyperledger@v2.0.1+incompatible/idemix/credential.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package idemix 8 9 import ( 10 "github.com/hyperledger/fabric-amcl/amcl" 11 "github.com/hyperledger/fabric-amcl/amcl/FP256BN" 12 "github.com/pkg/errors" 13 ) 14 15 // Identity Mixer Credential is a list of attributes certified (signed) by the issuer 16 // A credential also contains a user secret key blindly signed by the issuer 17 // Without the secret key the credential cannot be used 18 19 // Credential issuance is an interactive protocol between a user and an issuer 20 // The issuer takes its secret and public keys and user attribute values as input 21 // The user takes the issuer public key and user secret as input 22 // The issuance protocol consists of the following steps: 23 // 1) The issuer sends a random nonce to the user 24 // 2) The user creates a Credential Request using the public key of the issuer, user secret, and the nonce as input 25 // The request consists of a commitment to the user secret (can be seen as a public key) and a zero-knowledge proof 26 // of knowledge of the user secret key 27 // The user sends the credential request to the issuer 28 // 3) The issuer verifies the credential request by verifying the zero-knowledge proof 29 // If the request is valid, the issuer issues a credential to the user by signing the commitment to the secret key 30 // together with the attribute values and sends the credential back to the user 31 // 4) The user verifies the issuer's signature and stores the credential that consists of 32 // the signature value, a randomness used to create the signature, the user secret, and the attribute values 33 34 // NewCredential issues a new credential, which is the last step of the interactive issuance protocol 35 // All attribute values are added by the issuer at this step and then signed together with a commitment to 36 // the user's secret key from a credential request 37 func NewCredential(key *IssuerKey, m *CredRequest, attrs []*FP256BN.BIG, rng *amcl.RAND) (*Credential, error) { 38 // check the credential request that contains 39 err := m.Check(key.Ipk) 40 if err != nil { 41 return nil, err 42 } 43 44 if len(attrs) != len(key.Ipk.AttributeNames) { 45 return nil, errors.Errorf("incorrect number of attribute values passed") 46 } 47 48 // Place a BBS+ signature on the user key and the attribute values 49 // (For BBS+, see e.g. "Constant-Size Dynamic k-TAA" by Man Ho Au, Willy Susilo, Yi Mu) 50 // or http://eprint.iacr.org/2016/663.pdf, Sec. 4.3. 51 52 // For a credential, a BBS+ signature consists of the following three elements: 53 // 1. E, random value in the proper group 54 // 2. S, random value in the proper group 55 // 3. A as B^Exp where B=g_1 \cdot h_r^s \cdot h_sk^sk \cdot \prod_{i=1}^L h_i^{m_i} and Exp = \frac{1}{e+x} 56 // Notice that: 57 // h_r is h_0 in http://eprint.iacr.org/2016/663.pdf, Sec. 4.3. 58 59 // Pick randomness E and S 60 E := RandModOrder(rng) 61 S := RandModOrder(rng) 62 63 // Set B as g_1 \cdot h_r^s \cdot h_sk^sk \cdot \prod_{i=1}^L h_i^{m_i} and Exp = \frac{1}{e+x} 64 B := FP256BN.NewECP() 65 B.Copy(GenG1) // g_1 66 Nym := EcpFromProto(m.Nym) 67 B.Add(Nym) // in this case, recall Nym=h_sk^sk 68 B.Add(EcpFromProto(key.Ipk.HRand).Mul(S)) // h_r^s 69 70 // Append attributes 71 // Use Mul2 instead of Mul as much as possible for efficiency reasones 72 for i := 0; i < len(attrs)/2; i++ { 73 B.Add( 74 // Add two attributes in one shot 75 EcpFromProto(key.Ipk.HAttrs[2*i]).Mul2( 76 attrs[2*i], 77 EcpFromProto(key.Ipk.HAttrs[2*i+1]), 78 attrs[2*i+1], 79 ), 80 ) 81 } 82 // Check for residue in case len(attrs)%2 is odd 83 if len(attrs)%2 != 0 { 84 B.Add(EcpFromProto(key.Ipk.HAttrs[len(attrs)-1]).Mul(attrs[len(attrs)-1])) 85 } 86 87 // Set Exp as \frac{1}{e+x} 88 Exp := Modadd(FP256BN.FromBytes(key.GetIsk()), E, GroupOrder) 89 Exp.Invmodp(GroupOrder) 90 // Finalise A as B^Exp 91 A := B.Mul(Exp) 92 // The signature is now generated. 93 94 // Notice that here we release also B, this does not harm security cause 95 // it can be compute publicly from the BBS+ signature itself. 96 CredAttrs := make([][]byte, len(attrs)) 97 for index, attribute := range attrs { 98 CredAttrs[index] = BigToBytes(attribute) 99 } 100 101 return &Credential{ 102 A: EcpToProto(A), 103 B: EcpToProto(B), 104 E: BigToBytes(E), 105 S: BigToBytes(S), 106 Attrs: CredAttrs}, nil 107 } 108 109 // Ver cryptographically verifies the credential by verifying the signature 110 // on the attribute values and user's secret key 111 func (cred *Credential) Ver(sk *FP256BN.BIG, ipk *IssuerPublicKey) error { 112 // Validate Input 113 114 // - parse the credential 115 A := EcpFromProto(cred.GetA()) 116 B := EcpFromProto(cred.GetB()) 117 E := FP256BN.FromBytes(cred.GetE()) 118 S := FP256BN.FromBytes(cred.GetS()) 119 120 // - verify that all attribute values are present 121 for i := 0; i < len(cred.GetAttrs()); i++ { 122 if cred.Attrs[i] == nil { 123 return errors.Errorf("credential has no value for attribute %s", ipk.AttributeNames[i]) 124 } 125 } 126 127 // - verify cryptographic signature on the attributes and the user secret key 128 BPrime := FP256BN.NewECP() 129 BPrime.Copy(GenG1) 130 BPrime.Add(EcpFromProto(ipk.HSk).Mul2(sk, EcpFromProto(ipk.HRand), S)) 131 for i := 0; i < len(cred.Attrs)/2; i++ { 132 BPrime.Add( 133 EcpFromProto(ipk.HAttrs[2*i]).Mul2( 134 FP256BN.FromBytes(cred.Attrs[2*i]), 135 EcpFromProto(ipk.HAttrs[2*i+1]), 136 FP256BN.FromBytes(cred.Attrs[2*i+1]), 137 ), 138 ) 139 } 140 if len(cred.Attrs)%2 != 0 { 141 BPrime.Add(EcpFromProto(ipk.HAttrs[len(cred.Attrs)-1]).Mul(FP256BN.FromBytes(cred.Attrs[len(cred.Attrs)-1]))) 142 } 143 if !B.Equals(BPrime) { 144 return errors.Errorf("b-value from credential does not match the attribute values") 145 } 146 147 // Verify BBS+ signature. Namely: e(w \cdot g_2^e, A) =? e(g_2, B) 148 a := GenG2.Mul(E) 149 a.Add(Ecp2FromProto(ipk.W)) 150 a.Affine() 151 152 left := FP256BN.Fexp(FP256BN.Ate(a, A)) 153 right := FP256BN.Fexp(FP256BN.Ate(GenG2, B)) 154 155 if !left.Equals(right) { 156 return errors.Errorf("credential is not cryptographically valid") 157 } 158 159 return nil 160 }