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