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  }