...

Source file src/github.com/Microsoft/hcsshim/internal/cosesign1/create.go

Documentation: github.com/Microsoft/hcsshim/internal/cosesign1

     1  package cosesign1
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/rand"
     6  	"crypto/x509"
     7  	"encoding/pem"
     8  	"fmt"
     9  	"io"
    10  
    11  	"github.com/sirupsen/logrus"
    12  
    13  	"github.com/veraison/go-cose"
    14  )
    15  
    16  func pem2der(chainPem []byte) []byte {
    17  	block, rest := pem.Decode(chainPem)
    18  	if block != nil && block.Bytes != nil {
    19  		r := block.Bytes
    20  
    21  		for len(rest) != 0 {
    22  			block, rest = pem.Decode(rest)
    23  			if block != nil && block.Bytes != nil {
    24  				r = append(r, block.Bytes...)
    25  			}
    26  		}
    27  		return r
    28  	}
    29  	return nil
    30  }
    31  
    32  // CreateCoseSign1 returns a COSE Sign1 document as an array of bytes. Takes
    33  // `payloadBlob` and places it inside the envelope.
    34  // `issuer` is an arbitrary string, placed in the protected header along with
    35  // the other strings. Typically, this might be a did:x509 that identifies the
    36  // party that published the document.
    37  // `feed` is another arbitrary string. Typically, it is an identifier for the
    38  // object stored in the document.
    39  // `contentType` is a string to describe the payload content, e.g. "application/rego"
    40  // or "application/json".
    41  // `chainPem` is a byte slice containing the certificate chain. That chain is
    42  // stored and used by a receiver to validate the signature. The leaf  cert must
    43  // match the private key.
    44  // `keyPem` is a byte slice (PEM format) containing the private key used to sign
    45  // the document. Acceptable private key formats: EC, PKCS8, PKCS1.
    46  func CreateCoseSign1(payloadBlob []byte, issuer string, feed string, contentType string, chainPem []byte, keyPem []byte, saltType string, algo cose.Algorithm) (result []byte, err error) {
    47  	var signingKey any
    48  	keyDer, _ := pem.Decode(keyPem) // discard remaining bytes
    49  	if keyDer == nil {
    50  		return nil, fmt.Errorf("failed to find key in PEM")
    51  	}
    52  	var keyBytes = keyDer.Bytes
    53  
    54  	// try parsing the various likely key types in turn
    55  	signingKey, err = x509.ParseECPrivateKey(keyBytes)
    56  	if err == nil {
    57  		logrus.WithField("key", signingKey).Debug("parsed EC signing (private) key")
    58  	}
    59  
    60  	if err != nil {
    61  		signingKey, err = x509.ParsePKCS8PrivateKey(keyBytes)
    62  		if err == nil {
    63  			logrus.WithField("key", signingKey).Debug("parsed PKCS8 signing (private) key")
    64  		}
    65  	}
    66  
    67  	if err != nil {
    68  		signingKey, err = x509.ParsePKCS1PrivateKey(keyBytes)
    69  		if err == nil {
    70  			logrus.WithField("key", signingKey).Debug("parsed PKCS1 signing (private) key")
    71  		}
    72  	}
    73  
    74  	if err != nil {
    75  		logrus.WithError(err).Debug("failed to decode a key")
    76  		return nil, err
    77  	}
    78  
    79  	chainDER := pem2der(chainPem)
    80  	if chainDER == nil {
    81  		return nil, fmt.Errorf("failed to parse chainPem")
    82  	}
    83  
    84  	var chainCerts []*x509.Certificate
    85  	chainCerts, err = x509.ParseCertificates(chainDER)
    86  
    87  	if err == nil {
    88  		logrus.WithField("leaf cert", fmt.Sprintf("%v", *chainCerts[0])).Debug("parsed cert chain for leaf")
    89  	} else {
    90  		logrus.WithError(err).Debug("cert parsing failed")
    91  		return nil, err
    92  	}
    93  
    94  	chainDERArray := make([][]byte, len(chainCerts))
    95  	for i, cert := range chainCerts {
    96  		chainDERArray[i] = cert.Raw
    97  	}
    98  
    99  	var saltReader io.Reader
   100  	if saltType == "rand" {
   101  		saltReader = rand.Reader
   102  	} else {
   103  		saltReader = NewFixedReader(0)
   104  	}
   105  
   106  	cryptoSigner, ok := signingKey.(crypto.Signer)
   107  	if !ok {
   108  		return nil, fmt.Errorf("signingKey must be of type crypto.Signer")
   109  	}
   110  
   111  	var signer cose.Signer
   112  	signer, err = cose.NewSigner(algo, cryptoSigner)
   113  	if err != nil {
   114  		logrus.WithError(err).Error("failed to initialize cose signer")
   115  		return nil, err
   116  	}
   117  
   118  	// See https://www.iana.org/assignments/cose/cose.xhtml#:~:text=COSE%20Header%20Parameters%20%20%20%20Name%20,algorithm%20to%20use%20%2019%20more%20rows
   119  	headers := cose.Headers{
   120  		Protected: cose.ProtectedHeader{
   121  			cose.HeaderLabelAlgorithm:   algo,
   122  			cose.HeaderLabelContentType: contentType,
   123  			cose.HeaderLabelX5Chain:     chainDERArray,
   124  		},
   125  	}
   126  
   127  	// see https://ietf-scitt.github.io/draft-birkholz-scitt-architecture/draft-birkholz-scitt-architecture.html#name-envelope-and-claim-format
   128  	// Use of strings here to match PRSS COSE Sign1 service
   129  	if len(issuer) > 0 {
   130  		headers.Protected["iss"] = issuer
   131  	}
   132  	if len(feed) > 0 {
   133  		headers.Protected["feed"] = feed
   134  	}
   135  
   136  	result, err = cose.Sign1(saltReader, signer, headers, payloadBlob, nil)
   137  	if err != nil {
   138  		logrus.WithError(err).Debug("failed to create cose.Sign1")
   139  		return nil, err
   140  	}
   141  
   142  	return result, nil
   143  }
   144  

View as plain text