github.com/canhui/fabric_ca2_2@v2.0.0-alpha+incompatible/lib/client/credential/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 "encoding/json" 11 "fmt" 12 "net/http" 13 14 "github.com/cloudflare/cfssl/log" 15 "github.com/golang/protobuf/proto" 16 fp256bn "github.com/hyperledger/fabric-amcl/amcl/FP256BN" 17 "github.com/hyperledger/fabric-ca/api" 18 "github.com/hyperledger/fabric-ca/lib/common" 19 "github.com/hyperledger/fabric-ca/util" 20 "github.com/hyperledger/fabric/bccsp" 21 idemix "github.com/hyperledger/fabric/idemix" 22 "github.com/pkg/errors" 23 ) 24 25 const ( 26 // CredType is the string that represents Idemix credential type 27 CredType = "Idemix" 28 ) 29 30 // Client represents a client that will load/store an Idemix credential 31 type Client interface { 32 GetIssuerPubKey() (*idemix.IssuerPublicKey, error) 33 GetCSP() bccsp.BCCSP 34 } 35 36 // Credential represents an Idemix credential. Implements Credential interface 37 type Credential struct { 38 client Client 39 signerConfigFile string 40 val *SignerConfig 41 } 42 43 // NewCredential is constructor for idemix.Credential 44 func NewCredential(signerConfigFile string, c Client) *Credential { 45 return &Credential{ 46 c, signerConfigFile, nil, 47 } 48 } 49 50 // Type returns Idemix 51 func (cred *Credential) Type() string { 52 return CredType 53 } 54 55 // Val returns *SignerConfig associated with this Idemix credential 56 func (cred *Credential) Val() (interface{}, error) { 57 if cred.val == nil { 58 return nil, errors.New("Idemix credential value is not set") 59 } 60 return cred.val, nil 61 } 62 63 // EnrollmentID returns enrollment ID associated with this Idemix credential 64 func (cred *Credential) EnrollmentID() (string, error) { 65 if cred.val == nil { 66 return "", errors.New("Idemix credential value is not set") 67 } 68 return cred.val.EnrollmentID, nil 69 } 70 71 // SetVal sets *SignerConfig for this Idemix credential 72 func (cred *Credential) SetVal(val interface{}) error { 73 s, ok := val.(*SignerConfig) 74 if !ok { 75 return errors.New("The Idemix credential value must be of type *SignerConfig for idemix Credential") 76 } 77 cred.val = s 78 return nil 79 } 80 81 // Store stores this Idemix credential to the location specified by the 82 // signerConfigFile attribute 83 func (cred *Credential) Store() error { 84 val, err := cred.Val() 85 if err != nil { 86 return err 87 } 88 signerConfigBytes, err := json.Marshal(val) 89 if err != nil { 90 return errors.Wrapf(err, "Failed to marshal SignerConfig") 91 } 92 err = util.WriteFile(cred.signerConfigFile, signerConfigBytes, 0644) 93 if err != nil { 94 return errors.WithMessage(err, "Failed to store the Idemix credential") 95 } 96 log.Infof("Stored the Idemix credential at %s", cred.signerConfigFile) 97 return nil 98 } 99 100 // Load loads the Idemix credential from the location specified by the 101 // signerConfigFile attribute 102 func (cred *Credential) Load() error { 103 signerConfigBytes, err := util.ReadFile(cred.signerConfigFile) 104 if err != nil { 105 log.Debugf("No credential found at %s: %s", cred.signerConfigFile, err.Error()) 106 return err 107 } 108 val := SignerConfig{} 109 err = json.Unmarshal(signerConfigBytes, &val) 110 if err != nil { 111 return errors.Wrapf(err, fmt.Sprintf("Failed to unmarshal SignerConfig bytes from %s", cred.signerConfigFile)) 112 } 113 cred.val = &val 114 return nil 115 } 116 117 // CreateToken creates authorization token based on this Idemix credential 118 func (cred *Credential) CreateToken(req *http.Request, reqBody []byte) (string, error) { 119 enrollmentID, err := cred.EnrollmentID() 120 if err != nil { 121 return "", err 122 } 123 rng, err := idemix.GetRand() 124 if err != nil { 125 return "", errors.WithMessage(err, "Failed to get a random number while creating token") 126 } 127 // Get user's secret key 128 sk := fp256bn.FromBytes(cred.val.GetSk()) 129 130 // Get issuer public key 131 ipk, err := cred.client.GetIssuerPubKey() 132 if err != nil { 133 return "", errors.WithMessage(err, "Failed to get CA's Idemix public key while creating token") 134 } 135 136 // Generate a fresh Pseudonym (and a corresponding randomness) 137 nym, randNym := idemix.MakeNym(sk, ipk, rng) 138 139 b64body := util.B64Encode(reqBody) 140 b64uri := util.B64Encode([]byte(req.URL.RequestURI())) 141 msg := req.Method + "." + b64uri + "." + b64body 142 143 digest, digestError := cred.client.GetCSP().Hash([]byte(msg), &bccsp.SHAOpts{}) 144 if digestError != nil { 145 return "", errors.WithMessage(digestError, fmt.Sprintf("Failed to create token '%s'", msg)) 146 } 147 148 // A disclosure vector is formed (indicating that only enrollment ID from the credential is revealed) 149 disclosure := []byte{0, 0, 1, 0} 150 151 credential := idemix.Credential{} 152 err = proto.Unmarshal(cred.val.GetCred(), &credential) 153 if err != nil { 154 return "", errors.Wrapf(err, "Failed to unmarshal Idemix credential while creating token") 155 } 156 cri := idemix.CredentialRevocationInformation{} 157 err = proto.Unmarshal(cred.val.GetCredentialRevocationInformation(), &cri) 158 if err != nil { 159 return "", errors.Wrapf(err, "Failed to unmarshal Idemix CRI while creating token") 160 } 161 162 sig, err := idemix.NewSignature(&credential, sk, nym, randNym, ipk, disclosure, digest, 3, &cri, rng) 163 if err != nil { 164 return "", errors.Wrapf(err, "Failed to create signature while creating token") 165 } 166 sigBytes, err := proto.Marshal(sig) 167 token := "idemix." + common.IdemixTokenVersion1 + "." + enrollmentID + "." + util.B64Encode(sigBytes) 168 return token, nil 169 } 170 171 // RevokeSelf revokes this Idemix credential 172 func (cred *Credential) RevokeSelf() (*api.RevocationResponse, error) { 173 return nil, errors.New("Not implemented") //TODO 174 }