github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/lib/server/idemix/enroll.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  	"fmt"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/cloudflare/cfssl/log"
    15  	proto "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/server/user"
    19  	"github.com/hyperledger/fabric-ca/util"
    20  	"github.com/hyperledger/fabric/idemix"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  // EnrollmentResponse is the idemix enrollment response from the server
    25  type EnrollmentResponse struct {
    26  	// Base64 encoding of idemix Credential
    27  	Credential string
    28  	// Attribute name-value pairs
    29  	Attrs map[string]interface{}
    30  	// Base64 encoding of Credential Revocation information
    31  	CRI string
    32  	// Base64 encoding of the issuer nonce
    33  	Nonce string
    34  }
    35  
    36  // EnrollRequestHandler is the handler for Idemix enroll request
    37  type EnrollRequestHandler struct {
    38  	Ctx          ServerRequestCtx
    39  	EnrollmentID string
    40  	Issuer       MyIssuer
    41  	IdmxLib      Lib
    42  }
    43  
    44  // HandleRequest handles processing for Idemix enroll
    45  func (h *EnrollRequestHandler) HandleRequest() (*EnrollmentResponse, error) {
    46  	err := h.Authenticate()
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	var req api.IdemixEnrollmentRequestNet
    52  	err = h.Ctx.ReadBody(&req)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	if req.CredRequest == nil {
    58  		nonce, err := h.Issuer.NonceManager().GetNonce()
    59  		if err != nil {
    60  			return nil, errors.New("Failed to generate nonce")
    61  		}
    62  
    63  		resp := &EnrollmentResponse{
    64  			Nonce: util.B64Encode(idemix.BigToBytes(nonce)),
    65  		}
    66  		return resp, nil
    67  	}
    68  
    69  	ik, err := h.Issuer.IssuerCredential().GetIssuerKey()
    70  	if err != nil {
    71  		log.Errorf("Failed to get Idemix issuer key for the CA %s: %s", h.Issuer.Name(), err.Error())
    72  		return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get Idemix issuer key for the CA: %s",
    73  			h.Issuer.Name()))
    74  	}
    75  
    76  	caller, err := h.Ctx.GetCaller()
    77  	if err != nil {
    78  		log.Errorf("Failed to get caller of the request: %s", err.Error())
    79  		return nil, err
    80  	}
    81  
    82  	nonce := fp256bn.FromBytes(req.GetIssuerNonce())
    83  	err = h.Issuer.NonceManager().CheckNonce(nonce)
    84  	if err != nil {
    85  		return nil, errors.WithMessage(err, "Invalid nonce")
    86  	}
    87  
    88  	// Check the if credential request is valid
    89  	err = req.CredRequest.Check(ik.GetIpk())
    90  	if err != nil {
    91  		log.Errorf("Invalid Idemix credential request: %s", err.Error())
    92  		return nil, errors.WithMessage(err, "Invalid Idemix credential request")
    93  	}
    94  
    95  	// Get revocation handle for the credential
    96  	rh, err := h.Issuer.RevocationAuthority().GetNewRevocationHandle()
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	// Get attributes for the identity
   102  	attrMap, attrs, err := h.GetAttributeValues(caller, ik.GetIpk(), rh)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	cred, err := h.IdmxLib.NewCredential(ik, req.CredRequest, attrs, h.Issuer.IdemixRand())
   108  	if err != nil {
   109  		log.Errorf("Issuer '%s' failed to create new Idemix credential for identity '%s': %s",
   110  			h.Issuer.Name(), h.EnrollmentID, err.Error())
   111  		return nil, errors.New("Failed to create new Idemix credential")
   112  	}
   113  	credBytes, err := proto.Marshal(cred)
   114  	if err != nil {
   115  		return nil, errors.New("Failed to marshal Idemix credential to bytes")
   116  	}
   117  	b64CredBytes := util.B64Encode(credBytes)
   118  
   119  	rhstr := util.B64Encode(idemix.BigToBytes(rh))
   120  
   121  	// Store the credential in the database
   122  	err = h.Issuer.CredDBAccessor().InsertCredential(CredRecord{
   123  		CALabel:          h.Issuer.Name(),
   124  		ID:               caller.GetName(),
   125  		Status:           "good",
   126  		Cred:             b64CredBytes,
   127  		RevocationHandle: rhstr,
   128  	})
   129  	if err != nil {
   130  		log.Errorf("Failed to store the Idemix credential for identity '%s' in the database: %s", caller.GetName(), err.Error())
   131  		return nil, errors.New("Failed to store the Idemix credential")
   132  	}
   133  
   134  	// Get CRL from revocation authority of the CA
   135  	cri, err := h.Issuer.RevocationAuthority().CreateCRI()
   136  	if err != nil {
   137  		log.Errorf("Failed to generate CRI while processing idemix/credential request: %s", err.Error())
   138  		return nil, errors.New("Failed to generate CRI")
   139  	}
   140  	criBytes, err := proto.Marshal(cri)
   141  	if err != nil {
   142  		return nil, errors.New("Failed to marshal CRI to bytes")
   143  	}
   144  	b64CriBytes := util.B64Encode(criBytes)
   145  	resp := &EnrollmentResponse{
   146  		Credential: b64CredBytes,
   147  		Attrs:      attrMap,
   148  		CRI:        b64CriBytes,
   149  	}
   150  
   151  	if h.Ctx.IsBasicAuth() {
   152  		err = caller.LoginComplete()
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  	}
   157  
   158  	// Success
   159  	return resp, nil
   160  }
   161  
   162  // Authenticate authenticates the Idemix enroll request
   163  func (h *EnrollRequestHandler) Authenticate() error {
   164  	var err error
   165  	if h.Ctx.IsBasicAuth() {
   166  		h.EnrollmentID, err = h.Ctx.BasicAuthentication()
   167  		if err != nil {
   168  			return err
   169  		}
   170  	} else {
   171  		h.EnrollmentID, err = h.Ctx.TokenAuthentication()
   172  		if err != nil {
   173  			return err
   174  		}
   175  	}
   176  	return nil
   177  }
   178  
   179  // GenerateNonce generates a nonce for an Idemix enroll request
   180  func (h *EnrollRequestHandler) GenerateNonce() (*fp256bn.BIG, error) {
   181  	return h.IdmxLib.RandModOrder(h.Issuer.IdemixRand())
   182  }
   183  
   184  // GetAttributeValues returns attribute values of the caller of Idemix enroll request
   185  func (h *EnrollRequestHandler) GetAttributeValues(caller user.User, ipk *idemix.IssuerPublicKey,
   186  	rh *fp256bn.BIG) (map[string]interface{}, []*fp256bn.BIG, error) {
   187  	rc := []*fp256bn.BIG{}
   188  	attrMap := make(map[string]interface{})
   189  	for _, attrName := range ipk.AttributeNames {
   190  		if attrName == AttrEnrollmentID {
   191  			idBytes := []byte(caller.GetName())
   192  			rc = append(rc, idemix.HashModOrder(idBytes))
   193  			attrMap[attrName] = caller.GetName()
   194  		} else if attrName == AttrOU {
   195  			ou := []string{}
   196  			for _, aff := range caller.GetAffiliationPath() {
   197  				ou = append(ou, aff)
   198  			}
   199  			ouVal := strings.Join(ou, ".")
   200  			ouBytes := []byte(ouVal)
   201  			rc = append(rc, idemix.HashModOrder(ouBytes))
   202  			attrMap[attrName] = ouVal
   203  		} else if attrName == AttrRevocationHandle {
   204  			rc = append(rc, rh)
   205  			attrMap[attrName] = util.B64Encode(idemix.BigToBytes(rh))
   206  		} else if attrName == AttrRole {
   207  			role := MEMBER.getValue()
   208  			attrObj, err := caller.GetAttribute("role")
   209  			if err == nil {
   210  				role, err = strconv.Atoi(attrObj.GetValue())
   211  				if err != nil {
   212  					log.Debugf("role attribute of user %s must be a integer value", caller.GetName())
   213  				}
   214  			}
   215  			rc = append(rc, fp256bn.NewBIGint(role))
   216  			attrMap[attrName] = role
   217  		} else {
   218  			attrObj, err := caller.GetAttribute(attrName)
   219  			if err != nil {
   220  				log.Errorf("Failed to get attribute %s for user %s: %s", attrName, caller.GetName(), err.Error())
   221  			} else {
   222  				attrBytes := []byte(attrObj.GetValue())
   223  				rc = append(rc, idemix.HashModOrder(attrBytes))
   224  				attrMap[attrName] = attrObj.GetValue()
   225  			}
   226  		}
   227  	}
   228  	return attrMap, rc, nil
   229  }