github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/cloudflare/cfssl/ocsp/ocsp.go (about)

     1  /*
     2  
     3  Package ocsp exposes OCSP signing functionality, much like the signer
     4  package does for certificate signing.  It also provies a basic OCSP
     5  responder stack for serving pre-signed OCSP responses.
     6  
     7  */
     8  package ocsp
     9  
    10  import (
    11  	"bytes"
    12  	"crypto"
    13  	"github.com/hellobchain/newcryptosm"
    14  	"github.com/hellobchain/newcryptosm/x509"
    15  	"github.com/hellobchain/newcryptosm/x509/pkix"
    16  	"io/ioutil"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  
    21  	cferr "github.com/hellobchain/third_party/cloudflare/cfssl/errors"
    22  	"github.com/hellobchain/third_party/cloudflare/cfssl/helpers"
    23  	"github.com/hellobchain/third_party/cloudflare/cfssl/log"
    24  	"github.com/hellobchain/third_party/ocsp"
    25  )
    26  
    27  // revocationReasonCodes is a map between string reason codes
    28  // to integers as defined in RFC 5280
    29  var revocationReasonCodes = map[string]int{
    30  	"unspecified":          ocsp.Unspecified,
    31  	"keycompromise":        ocsp.KeyCompromise,
    32  	"cacompromise":         ocsp.CACompromise,
    33  	"affiliationchanged":   ocsp.AffiliationChanged,
    34  	"superseded":           ocsp.Superseded,
    35  	"cessationofoperation": ocsp.CessationOfOperation,
    36  	"certificatehold":      ocsp.CertificateHold,
    37  	"removefromcrl":        ocsp.RemoveFromCRL,
    38  	"privilegewithdrawn":   ocsp.PrivilegeWithdrawn,
    39  	"aacompromise":         ocsp.AACompromise,
    40  }
    41  
    42  // StatusCode is a map between string statuses sent by cli/api
    43  // to ocsp int statuses
    44  var StatusCode = map[string]int{
    45  	"good":    ocsp.Good,
    46  	"revoked": ocsp.Revoked,
    47  	"unknown": ocsp.Unknown,
    48  }
    49  
    50  // SignRequest represents the desired contents of a
    51  // specific OCSP response.
    52  type SignRequest struct {
    53  	Certificate *x509.Certificate
    54  	Status      string
    55  	Reason      int
    56  	RevokedAt   time.Time
    57  	Extensions  []pkix.Extension
    58  	// IssuerHash is the hashing function used to hash the issuer subject and public key
    59  	// in the OCSP response. Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384,
    60  	// and crypto.SHA512. If zero, the default is crypto.SHA1.
    61  	IssuerHash newcryptosm.Hash
    62  	// If provided ThisUpdate will override the default usage of time.Now().Truncate(time.Hour)
    63  	ThisUpdate *time.Time
    64  	// If provided NextUpdate will override the default usage of ThisUpdate.Add(signerInterval)
    65  	NextUpdate *time.Time
    66  }
    67  
    68  // Signer represents a general signer of OCSP responses.  It is
    69  // responsible for populating all fields in the OCSP response that
    70  // are not reflected in the SignRequest.
    71  type Signer interface {
    72  	Sign(req SignRequest) ([]byte, error)
    73  }
    74  
    75  // StandardSigner is the default concrete type of OCSP signer.
    76  // It represents a single responder (represented by a key and certificate)
    77  // speaking for a single issuer (certificate).  It is assumed that OCSP
    78  // responses are issued at a regular interval, which is used to compute
    79  // the nextUpdate value based on the current time.
    80  type StandardSigner struct {
    81  	issuer    *x509.Certificate
    82  	responder *x509.Certificate
    83  	key       crypto.Signer
    84  	interval  time.Duration
    85  }
    86  
    87  // ReasonStringToCode tries to convert a reason string to an integer code
    88  func ReasonStringToCode(reason string) (reasonCode int, err error) {
    89  	// default to 0
    90  	if reason == "" {
    91  		return 0, nil
    92  	}
    93  
    94  	reasonCode, present := revocationReasonCodes[strings.ToLower(reason)]
    95  	if !present {
    96  		reasonCode, err = strconv.Atoi(reason)
    97  		if err != nil {
    98  			return
    99  		}
   100  		if reasonCode >= ocsp.AACompromise || reasonCode <= ocsp.Unspecified {
   101  			return 0, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
   102  		}
   103  	}
   104  
   105  	return
   106  }
   107  
   108  // NewSignerFromFile reads the issuer cert, the responder cert and the responder key
   109  // from PEM files, and takes an interval in seconds
   110  func NewSignerFromFile(issuerFile, responderFile, keyFile string, interval time.Duration) (Signer, error) {
   111  	log.Debug("Loading issuer cert: ", issuerFile)
   112  	issuerBytes, err := helpers.ReadBytes(issuerFile)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	log.Debug("Loading responder cert: ", responderFile)
   117  	responderBytes, err := ioutil.ReadFile(responderFile)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	log.Debug("Loading responder key: ", keyFile)
   122  	keyBytes, err := ioutil.ReadFile(keyFile)
   123  	if err != nil {
   124  		return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err)
   125  	}
   126  
   127  	issuerCert, err := helpers.ParseCertificatePEM(issuerBytes)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	responderCert, err := helpers.ParseCertificatePEM(responderBytes)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	key, err := helpers.ParsePrivateKeyPEM(keyBytes)
   138  	if err != nil {
   139  		log.Debug("Malformed private key %v", err)
   140  		return nil, err
   141  	}
   142  
   143  	return NewSigner(issuerCert, responderCert, key, interval)
   144  }
   145  
   146  // NewSigner simply constructs a new StandardSigner object from the inputs,
   147  // taking the interval in seconds
   148  func NewSigner(issuer, responder *x509.Certificate, key crypto.Signer, interval time.Duration) (Signer, error) {
   149  	return &StandardSigner{
   150  		issuer:    issuer,
   151  		responder: responder,
   152  		key:       key,
   153  		interval:  interval,
   154  	}, nil
   155  }
   156  
   157  // Sign is used with an OCSP signer to request the issuance of
   158  // an OCSP response.
   159  func (s StandardSigner) Sign(req SignRequest) ([]byte, error) {
   160  	if req.Certificate == nil {
   161  		return nil, cferr.New(cferr.OCSPError, cferr.ReadFailed)
   162  	}
   163  
   164  	// Verify that req.Certificate is issued under s.issuer
   165  	if bytes.Compare(req.Certificate.RawIssuer, s.issuer.RawSubject) != 0 {
   166  		return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
   167  	}
   168  	if req.Certificate.CheckSignatureFrom(s.issuer) != nil {
   169  		return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
   170  	}
   171  
   172  	var thisUpdate, nextUpdate time.Time
   173  	if req.ThisUpdate != nil {
   174  		thisUpdate = *req.ThisUpdate
   175  	} else {
   176  		// Round thisUpdate times down to the nearest hour
   177  		thisUpdate = time.Now().Truncate(time.Hour)
   178  	}
   179  	if req.NextUpdate != nil {
   180  		nextUpdate = *req.NextUpdate
   181  	} else {
   182  		nextUpdate = thisUpdate.Add(s.interval)
   183  	}
   184  
   185  	status, ok := StatusCode[req.Status]
   186  	if !ok {
   187  		return nil, cferr.New(cferr.OCSPError, cferr.InvalidStatus)
   188  	}
   189  
   190  	// If the OCSP responder is the same as the issuer, there is no need to
   191  	// include any certificate in the OCSP response, which decreases the byte size
   192  	// of OCSP responses dramatically.
   193  	certificate := s.responder
   194  	if s.issuer == s.responder || bytes.Equal(s.issuer.Raw, s.responder.Raw) {
   195  		certificate = nil
   196  	}
   197  
   198  	template := ocsp.Response{
   199  		Status:          status,
   200  		SerialNumber:    req.Certificate.SerialNumber,
   201  		ThisUpdate:      thisUpdate,
   202  		NextUpdate:      nextUpdate,
   203  		Certificate:     certificate,
   204  		ExtraExtensions: req.Extensions,
   205  		IssuerHash:      req.IssuerHash,
   206  	}
   207  
   208  	if status == ocsp.Revoked {
   209  		template.RevokedAt = req.RevokedAt
   210  		template.RevocationReason = req.Reason
   211  	}
   212  
   213  	return ocsp.CreateResponse(s.issuer, s.responder, template, s.key)
   214  }