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  }