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 }