github.com/bestbeforetoday/fabric-ca@v2.0.0-alpha+incompatible/lib/tcert/tcert.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package tcert
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"crypto/hmac"
    22  	"crypto/rand"
    23  	"crypto/sha512"
    24  	"crypto/x509"
    25  	"crypto/x509/pkix"
    26  	"encoding/asn1"
    27  	"encoding/base64"
    28  	"fmt"
    29  	"math/big"
    30  	"strconv"
    31  	"time"
    32  
    33  	"github.com/cloudflare/cfssl/log"
    34  	"github.com/hyperledger/fabric-ca/api"
    35  	"github.com/hyperledger/fabric-ca/util"
    36  	"github.com/hyperledger/fabric/bccsp"
    37  	cspsigner "github.com/hyperledger/fabric/bccsp/signer"
    38  )
    39  
    40  var (
    41  	// TCertEncTCertIndex is the ASN1 object identifier of the TCert index.
    42  	TCertEncTCertIndex = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 7}
    43  
    44  	// TCertEncEnrollmentID is the ASN1 object identifier of the enrollment id.
    45  	TCertEncEnrollmentID = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 8}
    46  
    47  	// TCertAttributesHeaders is the ASN1 object identifier of attributes header.
    48  	TCertAttributesHeaders = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 9}
    49  
    50  	// Padding for encryption.
    51  	Padding = []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
    52  
    53  	// tcertSubject is the subject name placed in all generated TCerts
    54  	tcertSubject = pkix.Name{CommonName: "Fabric Transaction Certificate"}
    55  )
    56  
    57  // LoadMgr is the constructor for a TCert manager given key and certificate file names
    58  // @parameter caKeyFile is the file name for the CA's key
    59  // @parameter caCertFile is the file name for the CA's cert
    60  func LoadMgr(caKeyFile, caCertFile string, myCSP bccsp.BCCSP) (*Mgr, error) {
    61  	_, caKey, caCert, err := util.GetSignerFromCertFile(caCertFile, myCSP)
    62  	if err != nil && caCert == nil {
    63  		return nil, fmt.Errorf("Failed to load cert [%s]", err)
    64  	}
    65  	if err != nil {
    66  		// Fallback: attempt to read out of keyFile and import
    67  		log.Debugf("No key found in BCCSP keystore, attempting fallback")
    68  		key, err := util.ImportBCCSPKeyFromPEM(caKeyFile, myCSP, true)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  		signer, err := cspsigner.New(myCSP, key)
    73  		if err != nil {
    74  			return nil, fmt.Errorf("Failed initializing CryptoSigner [%s]", err)
    75  		}
    76  		caKey = signer
    77  	}
    78  
    79  	return NewMgr(caKey, caCert)
    80  }
    81  
    82  // NewMgr is the constructor for a TCert manager given a key and an x509 certificate
    83  // @parameter caKey is used for signing a certificate request
    84  // @parameter caCert is used for extracting CA data to associate with issued certificates
    85  func NewMgr(caKey interface{}, caCert *x509.Certificate) (*Mgr, error) {
    86  	mgr := new(Mgr)
    87  	mgr.CAKey = caKey
    88  	mgr.CACert = caCert
    89  	mgr.ValidityPeriod = time.Hour * 24 * 365 // default to 1 year
    90  	mgr.MaxAllowedBatchSize = 1000
    91  	return mgr, nil
    92  }
    93  
    94  // Mgr is the manager for the TCert library
    95  type Mgr struct {
    96  	// CAKey is used for signing a certificate request
    97  	CAKey interface{}
    98  	// CACert is used for extracting CA data to associate with issued certificates
    99  	CACert *x509.Certificate
   100  	// ValidityPeriod is the duration that the issued certificate will be valid
   101  	// unless the user requests a shorter validity period.
   102  	// The default value is 1 year.
   103  	ValidityPeriod time.Duration
   104  	// MaxAllowedBatchSize is the maximum number of TCerts which can be requested at a time.
   105  	// The default value is 1000.
   106  	MaxAllowedBatchSize int
   107  }
   108  
   109  // GetBatch gets a batch of TCerts
   110  // @parameter req Is the TCert batch request
   111  // @parameter ecert Is the enrollment certificate of the caller
   112  func (tm *Mgr) GetBatch(req *GetTCertBatchRequest, ecert *x509.Certificate) (*api.GetTCertBatchResponse, error) {
   113  
   114  	log.Debugf("GetBatch req=%+v", req)
   115  
   116  	// Set numTCertsInBatch to the number of TCerts to get.
   117  	// If 0 are requested, retrieve the maximum allowable;
   118  	// otherwise, retrieve the number requested it not too many.
   119  	var numTCertsInBatch int
   120  	if req.Count == 0 {
   121  		numTCertsInBatch = int(tm.MaxAllowedBatchSize)
   122  	} else if req.Count <= tm.MaxAllowedBatchSize {
   123  		numTCertsInBatch = int(req.Count)
   124  	} else {
   125  		return nil, fmt.Errorf("You may not request %d TCerts; the maximum is %d",
   126  			req.Count, tm.MaxAllowedBatchSize)
   127  	}
   128  
   129  	// Certs are valid for the min of requested and configured max
   130  	vp := tm.ValidityPeriod
   131  	if req.ValidityPeriod > 0 && req.ValidityPeriod < vp {
   132  		vp = req.ValidityPeriod
   133  	}
   134  
   135  	// Create a template from which to create all other TCerts.
   136  	// Since a TCert is anonymous and unlinkable, do not include
   137  	template := &x509.Certificate{
   138  		Subject: tcertSubject,
   139  	}
   140  	template.NotBefore = time.Now()
   141  	template.NotAfter = template.NotBefore.Add(vp)
   142  	template.IsCA = false
   143  	template.KeyUsage = x509.KeyUsageDigitalSignature
   144  	template.SubjectKeyId = []byte{1, 2, 3, 4}
   145  
   146  	// Generate nonce for TCertIndex
   147  	nonce := make([]byte, 16) // 8 bytes rand, 8 bytes timestamp
   148  	rand.Reader.Read(nonce[:8])
   149  
   150  	pub := ecert.PublicKey.(*ecdsa.PublicKey)
   151  
   152  	mac := hmac.New(sha512.New384, []byte(createHMACKey()))
   153  	raw, _ := x509.MarshalPKIXPublicKey(pub)
   154  	mac.Write(raw)
   155  	kdfKey := mac.Sum(nil)
   156  
   157  	var set []api.TCert
   158  
   159  	for i := 0; i < numTCertsInBatch; i++ {
   160  		tcertid, uuidError := GenerateIntUUID()
   161  		if uuidError != nil {
   162  			return nil, fmt.Errorf("Failure generating UUID: %s", uuidError)
   163  		}
   164  		// Compute TCertIndex
   165  		tidx := []byte(strconv.Itoa(2*i + 1))
   166  		tidx = append(tidx[:], nonce[:]...)
   167  		tidx = append(tidx[:], Padding...)
   168  
   169  		mac := hmac.New(sha512.New384, kdfKey)
   170  		mac.Write([]byte{1})
   171  		extKey := mac.Sum(nil)[:32]
   172  
   173  		mac = hmac.New(sha512.New384, kdfKey)
   174  		mac.Write([]byte{2})
   175  		mac = hmac.New(sha512.New384, mac.Sum(nil))
   176  		mac.Write(tidx)
   177  
   178  		one := new(big.Int).SetInt64(1)
   179  		k := new(big.Int).SetBytes(mac.Sum(nil))
   180  		k.Mod(k, new(big.Int).Sub(pub.Curve.Params().N, one))
   181  		k.Add(k, one)
   182  
   183  		tmpX, tmpY := pub.ScalarBaseMult(k.Bytes())
   184  		txX, txY := pub.Curve.Add(pub.X, pub.Y, tmpX, tmpY)
   185  		txPub := ecdsa.PublicKey{Curve: pub.Curve, X: txX, Y: txY}
   186  
   187  		// Compute encrypted TCertIndex
   188  		encryptedTidx, encryptErr := CBCPKCS7Encrypt(extKey, tidx)
   189  		if encryptErr != nil {
   190  			return nil, encryptErr
   191  		}
   192  
   193  		extensions, ks, extensionErr := generateExtensions(tcertid, encryptedTidx, ecert, req)
   194  
   195  		if extensionErr != nil {
   196  			return nil, extensionErr
   197  		}
   198  
   199  		template.PublicKey = txPub
   200  		template.Extensions = extensions
   201  		template.ExtraExtensions = extensions
   202  		template.SerialNumber = tcertid
   203  
   204  		raw, err := x509.CreateCertificate(rand.Reader, template, tm.CACert, &txPub, tm.CAKey)
   205  		if err != nil {
   206  			return nil, fmt.Errorf("Failed in TCert x509.CreateCertificate: %s", err)
   207  		}
   208  
   209  		pem := ConvertDERToPEM(raw, "CERTIFICATE")
   210  
   211  		set = append(set, api.TCert{Cert: pem, Keys: ks})
   212  	}
   213  
   214  	tcertID, randNumErr := GenNumber(big.NewInt(20))
   215  	if randNumErr != nil {
   216  		return nil, randNumErr
   217  	}
   218  
   219  	tcertResponse := &api.GetTCertBatchResponse{
   220  		ID:     tcertID,
   221  		TS:     time.Now(),
   222  		Key:    kdfKey,
   223  		TCerts: set,
   224  	}
   225  
   226  	return tcertResponse, nil
   227  
   228  }
   229  
   230  /**
   231  *  Create HMAC Key
   232  *  returns HMAC String
   233   */
   234  func createHMACKey() string {
   235  	key := make([]byte, 49)
   236  	rand.Reader.Read(key)
   237  	var cooked = base64.StdEncoding.EncodeToString(key)
   238  	return cooked
   239  }
   240  
   241  // Generate encrypted extensions to be included into the TCert (TCertIndex, EnrollmentID and attributes).
   242  func generateExtensions(tcertid *big.Int, tidx []byte, enrollmentCert *x509.Certificate, batchRequest *GetTCertBatchRequest) ([]pkix.Extension, map[string][]byte, error) {
   243  	// For each TCert we need to store and retrieve to the user the list of Ks used to encrypt the EnrollmentID and the attributes.
   244  	ks := make(map[string][]byte)
   245  	attrs := batchRequest.Attrs
   246  	extensions := make([]pkix.Extension, len(attrs))
   247  
   248  	preK1 := batchRequest.PreKey
   249  	mac := hmac.New(sha512.New384, []byte(preK1))
   250  	mac.Write(tcertid.Bytes())
   251  	preK0 := mac.Sum(nil)
   252  
   253  	// Compute encrypted EnrollmentID
   254  	mac = hmac.New(sha512.New384, preK0)
   255  	mac.Write([]byte("enrollmentID"))
   256  	enrollmentIDKey := mac.Sum(nil)[:32]
   257  
   258  	enrollmentID := []byte(GetEnrollmentIDFromCert(enrollmentCert))
   259  	enrollmentID = append(enrollmentID, Padding...)
   260  
   261  	encEnrollmentID, err := CBCPKCS7Encrypt(enrollmentIDKey, enrollmentID)
   262  	if err != nil {
   263  		return nil, nil, err
   264  	}
   265  
   266  	// save k used to encrypt EnrollmentID
   267  	ks["enrollmentId"] = enrollmentIDKey
   268  
   269  	attributeIdentifierIndex := 9
   270  	count := 0
   271  	attributesHeader := make(map[string]int)
   272  
   273  	// Append attributes to the extensions slice
   274  	for i := 0; i < len(attrs); i++ {
   275  		count++
   276  		name := attrs[i].Name
   277  		value := []byte(attrs[i].Value)
   278  
   279  		// Save the position of the attribute extension on the header.
   280  		attributesHeader[name] = count
   281  
   282  		// Encrypt attribute if enabled
   283  		if batchRequest.EncryptAttrs {
   284  			mac = hmac.New(sha512.New384, preK0)
   285  			mac.Write([]byte(name))
   286  			attributeKey := mac.Sum(nil)[:32]
   287  
   288  			value = append(value, Padding...)
   289  			value, err = CBCPKCS7Encrypt(attributeKey, value)
   290  			if err != nil {
   291  				return nil, nil, err
   292  			}
   293  
   294  			// Save the key used to encrypt the attribute
   295  			ks[name] = attributeKey
   296  		}
   297  
   298  		// Generate an ObjectIdentifier for the extension holding the attribute
   299  		TCertEncAttributes := asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, attributeIdentifierIndex + count}
   300  
   301  		// Add the attribute extension to the extensions array
   302  		extensions[count-1] = pkix.Extension{Id: TCertEncAttributes, Critical: false, Value: value}
   303  	}
   304  
   305  	// Append the TCertIndex to the extensions
   306  	extensions = append(extensions, pkix.Extension{Id: TCertEncTCertIndex, Critical: true, Value: tidx})
   307  
   308  	// Append the encrypted EnrollmentID to the extensions
   309  	extensions = append(extensions, pkix.Extension{Id: TCertEncEnrollmentID, Critical: false, Value: encEnrollmentID})
   310  
   311  	// Append the attributes header if there was attributes to include in the TCert
   312  	if len(attrs) > 0 {
   313  		extensions = append(extensions, pkix.Extension{Id: TCertAttributesHeaders, Critical: false, Value: buildAttributesHeader(attributesHeader)})
   314  	}
   315  
   316  	return extensions, ks, nil
   317  }
   318  
   319  func buildAttributesHeader(attributesHeader map[string]int) []byte {
   320  	var headerString string
   321  	for k, v := range attributesHeader {
   322  		headerString = headerString + k + "->" + strconv.Itoa(v) + "#"
   323  	}
   324  	return []byte(headerString)
   325  }