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  }