Fix golangci-lint warnings

main
Jan Dittberner 2 years ago
parent a525cd0416
commit 8dbfc208b9

@ -25,10 +25,12 @@ linters-settings:
gomnd:
ignore-functions:
- 'strconv.*'
ignored-files:
- "pkg/ocsp/ocsp.go"
goimports:
local-prefixes: git.cacert.org/cacert-goocsp
misspell:
locale: en-AU
locale: "US"
ignore-words:
- CAcert

@ -140,6 +140,7 @@ func configureIssuers(ctx context.Context, issuerConfigs []*koanf.Koanf, opts []
continue
}
issuer := ocspsource.NewIssuer(
caCertificate,
responderCertificate,

@ -30,7 +30,7 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha1"
_ "crypto/sha1" //nolint:gosec
_ "crypto/sha256"
_ "crypto/sha512"
"crypto/x509"
@ -45,19 +45,19 @@ import (
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
// ResponseStatus contains the result of an OCSP request. See
// https://tools.ietf.org/html/rfc6960#section-2.3
// ResponseStatus contains the result of an OCSP request. See https://tools.ietf.org/html/rfc6960#section-2.3
type ResponseStatus int
const (
Success ResponseStatus = 0
Malformed ResponseStatus = 1
InternalError ResponseStatus = 2
TryLater ResponseStatus = 3
// Status code four is unused in OCSP. See
// https://tools.ietf.org/html/rfc6960#section-4.2.1
SignatureRequired ResponseStatus = 5
Unauthorized ResponseStatus = 6
Success ResponseStatus = 0 // response has valid confirmations
Malformed ResponseStatus = 1 // illegal confirmation request
InternalError ResponseStatus = 2 // internal error in issuer
TryLater ResponseStatus = 3 // try again later
// Status code four is unused in OCSP. See https://tools.ietf.org/html/rfc6960#section-4.2.1
SignatureRequired ResponseStatus = 5 // must sign the request
Unauthorized ResponseStatus = 6 // request unauthorized
)
func (r ResponseStatus) String() string {
@ -115,7 +115,7 @@ type request struct {
Cert certID
}
type responseASN1 struct {
type ocspResponseASN1 struct {
Status asn1.Enumerated
Response responseBytes `asn1:"explicit,tag:0,optional"`
}
@ -200,8 +200,17 @@ var signatureAlgorithmDetails = []struct {
}
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
var pubType x509.PublicKeyAlgorithm
//nolint:cyclop
func signingParamsForPublicKey(
pub interface{},
requestedSigAlgo x509.SignatureAlgorithm,
) (crypto.Hash, pkix.AlgorithmIdentifier, error) {
var (
pubType x509.PublicKeyAlgorithm
hashFunc crypto.Hash
sigAlgo pkix.AlgorithmIdentifier
err error
)
switch pub := pub.(type) {
case *rsa.PublicKey:
@ -234,35 +243,41 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureA
}
if err != nil {
return
return 0, pkix.AlgorithmIdentifier{}, err
}
if requestedSigAlgo == 0 {
return
return hashFunc, sigAlgo, nil
}
found := false
for _, details := range signatureAlgorithmDetails {
if details.algo == requestedSigAlgo {
if details.pubKeyAlgo != pubType {
err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
return
return 0, pkix.AlgorithmIdentifier{}, errors.New(
"x509: requested SignatureAlgorithm does not match private key type",
)
}
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
if hashFunc == 0 {
err = errors.New("x509: cannot sign with hash function requested")
return
return 0, pkix.AlgorithmIdentifier{}, errors.New(
"x509: cannot sign with hash function requested",
)
}
found = true
break
}
}
if !found {
err = errors.New("x509: unknown SignatureAlgorithm")
return 0, pkix.AlgorithmIdentifier{}, errors.New("x509: unknown SignatureAlgorithm")
}
return
return hashFunc, sigAlgo, nil
}
// TODO(agl): this is taken from crypto/x509 and so should probably be exported
@ -273,6 +288,7 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgor
return details.algo
}
}
return x509.UnknownSignatureAlgorithm
}
@ -283,6 +299,7 @@ func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
return hash
}
}
return crypto.Hash(0)
}
@ -292,6 +309,7 @@ func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier {
return oid
}
}
return nil
}
@ -308,7 +326,7 @@ const (
// ServerFailed is unused and was never used (see
// https://go-review.googlesource.com/#/c/18944). ParseResponse will
// return a ResponseError when an error response is parsed.
ServerFailed
// ServerFailed
)
// The enumerated reasons for revoking a certificate. See RFC 5280.
@ -340,7 +358,8 @@ func (req *Request) Marshal() ([]byte, error) {
if hashAlg == nil {
return nil, errors.New("Unknown hash algorithm")
}
return asn1.Marshal(ocspRequest{
request, err := asn1.Marshal(ocspRequest{
tbsRequest{
Version: 0,
RequestList: []request{
@ -358,6 +377,11 @@ func (req *Request) Marshal() ([]byte, error) {
},
},
})
if err != nil {
return nil, fmt.Errorf("could not marshal OCSP request ASN.1: %w", err)
}
return request, nil
}
// Response represents an OCSP response containing a single SingleResponse. See
@ -408,13 +432,13 @@ type Response struct {
// defined by OCSP. The Unauthorized code in particular can be used by an OCSP
// responder that supports only pre-signed responses as a response to requests
// for certificates with unknown status. See RFC 5019.
var (
MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
)
// var (
// MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
// InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
// TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
// SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
// UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
// )
// CheckSignatureFrom checks that the signature in resp is a valid signature
// from issuer. This should only be used if resp.Certificate is nil. Otherwise,
@ -422,7 +446,11 @@ var (
// signature. That signature is checked by ParseResponse and only
// resp.Certificate remains to be validated.
func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
if err := issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature); err != nil {
return fmt.Errorf("could not check signature: %w", err)
}
return nil
}
// ParseError results from an invalid OCSP response.
@ -437,10 +465,12 @@ func (p ParseError) Error() string {
// If a request includes a signature, it will result in a ParseError.
func ParseRequest(bytes []byte) (*Request, error) {
var req ocspRequest
rest, err := asn1.Unmarshal(bytes, &req)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not unmarshal OCSP request ASN.1: %w", err)
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP request")
}
@ -448,6 +478,7 @@ func ParseRequest(bytes []byte) (*Request, error) {
if len(req.TBSRequest.RequestList) == 0 {
return nil, ParseError("OCSP request contains no request body")
}
innerRequest := req.TBSRequest.RequestList[0]
hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
@ -487,12 +518,17 @@ func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
// multiple statuses and cert is not nil, then ParseResponseForCert will return
// the first status which contains a matching serial, otherwise it will return an
// error. If cert is nil, then the first status in the response will be returned.
//
//nolint:gocognit, cyclop
func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) {
var resp responseASN1
var resp ocspResponseASN1
rest, err := asn1.Unmarshal(bytes, &resp)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not unmarshal OCSP response ASN.1: %w", err)
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP response")
}
@ -506,10 +542,12 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
}
var basicResp basicResponse
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not unmarshal basic response ASN.1: %w", err)
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP response")
}
@ -519,6 +557,7 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
}
var singleResp singleResponse
if cert == nil {
singleResp = basicResp.TBSResponseData.Responses[0]
} else {
@ -527,6 +566,7 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
if cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 {
singleResp = resp
match = true
break
}
}
@ -553,9 +593,11 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
switch rawResponderID.Tag {
case 1: // Name
var rdn pkix.RDNSequence
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &rdn); err != nil || len(rest) != 0 {
return nil, ParseError("invalid responder name")
}
ret.RawResponderName = rawResponderID.Bytes
case 2: // KeyHash
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &ret.ResponderKeyHash); err != nil || len(rest) != 0 {
@ -565,7 +607,7 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
return nil, ParseError("invalid responder id tag")
}
if len(basicResp.Certificates) > 0 {
if len(basicResp.Certificates) > 0 { //nolint:nestif
// Responders should only send a single certificate (if they
// send any) that connects the responder's certificate to the
// original issuer. We accept responses with multiple
@ -575,7 +617,7 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
// [1] https://github.com/golang/go/issues/21527
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not parse signer certificate: %w", err)
}
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
@ -583,7 +625,11 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
}
if issuer != nil {
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
if err := issuer.CheckSignature(
ret.Certificate.SignatureAlgorithm,
ret.Certificate.RawTBSCertificate,
ret.Certificate.Signature,
); err != nil {
return nil, ParseError("bad OCSP signature: " + err.Error())
}
}
@ -602,9 +648,11 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
for h, oid := range hashOIDs {
if singleResp.CertID.HashAlgorithm.Algorithm.Equal(oid) {
ret.IssuerHash = h
break
}
}
if ret.IssuerHash == 0 {
return nil, ParseError("unsupported issuer hash algorithm")
}
@ -635,6 +683,7 @@ func (opts *RequestOptions) hash() crypto.Hash {
// SHA-1 is nearly universally used in OCSP.
return crypto.SHA1
}
return opts.Hash
}
@ -654,14 +703,16 @@ func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte
if !hashFunc.Available() {
return nil, x509.ErrUnsupportedAlgorithm
}
h := opts.hash().New()
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
return nil, err
return nil, fmt.Errorf("could not unmarshal public key info ASN.1: %w", err)
}
h.Write(publicKeyInfo.PublicKey.RightAlign())
@ -677,6 +728,7 @@ func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte
IssuerKeyHash: issuerKeyHash,
SerialNumber: cert.SerialNumber,
}
return req.Marshal()
}
@ -694,18 +746,27 @@ func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte
// If template.IssuerHash is not set, SHA1 will be used.
//
// The ProducedAt date is automatically set to the current date, to the nearest minute.
func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer, extensions []pkix.Extension) ([]byte, error) {
//
//nolint:cyclop
func CreateResponse(
issuer, responderCert *x509.Certificate,
template Response,
priv crypto.Signer,
extensions []pkix.Extension,
) ([]byte, error) {
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
return nil, err
return nil, fmt.Errorf("could not unmarshal public key info ASN.1: %w", err)
}
if template.IssuerHash == 0 {
template.IssuerHash = crypto.SHA1
}
hashOID := getOIDFromHashAlgorithm(template.IssuerHash)
if hashOID == nil {
return nil, errors.New("unsupported issuer hash algorithm")
@ -714,12 +775,15 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
if !template.IssuerHash.Available() {
return nil, fmt.Errorf("issuer hash algorithm %v not linked into binary", template.IssuerHash)
}
h := template.IssuerHash.New()
h.Write(publicKeyInfo.PublicKey.RightAlign())
issuerKeyHash := h.Sum(nil)
h.Reset()
h.Write(issuer.RawSubject)
issuerNameHash := h.Sum(nil)
innerResponse := singleResponse{
@ -765,7 +829,7 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not marshal response data: %w", err)
}
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
@ -775,9 +839,10 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
responseHash := hashFunc.New()
responseHash.Write(tbsResponseDataDER)
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
if err != nil {
return nil, err
return nil, fmt.Errorf("signing failed: %w", err)
}
response := basicResponse{
@ -793,16 +858,22 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response,
{FullBytes: template.Certificate.Raw},
}
}
responseDER, err := asn1.Marshal(response)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not marshal OCSP data ASN.1: %w", err)
}
return asn1.Marshal(responseASN1{
responseStructureDER, err := asn1.Marshal(ocspResponseASN1{
Status: asn1.Enumerated(Success),
Response: responseBytes{
ResponseType: idPKIXOCSPBasic,
Response: responseDER,
},
})
if err != nil {
return nil, fmt.Errorf("could not marshal OSCP response ASN.1: %w", err)
}
return responseStructureDER, nil
}

@ -34,7 +34,6 @@ import (
"io/ioutil"
"net/http"
"net/url"
"regexp"
"time"
"github.com/jmhodges/clock"
@ -44,9 +43,9 @@ import (
var (
malformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
internalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
tryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
sigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
unauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
// tryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
// sigRequiredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
unauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
// ErrNotFound indicates the request OCSP response was not found. It is used to
// indicate that the responder should reply with unauthorizedErrorResponse.
@ -79,44 +78,8 @@ func (src InMemorySource) Response(request *Request) ([]byte, http.Header, error
if !present {
return nil, nil, ErrNotFound
}
return response, nil, nil
}
// NewSourceFromFile reads the named file into an InMemorySource.
// The file read by this function must contain whitespace-separated OCSP
// responses. Each OCSP response must be in base64-encoded DER form (i.e.,
// PEM without headers or whitespace). Invalid responses are ignored.
// This function pulls the entire file into an InMemorySource.
func NewSourceFromFile(responseFile string) (Source, error) {
fileContents, err := ioutil.ReadFile(responseFile)
if err != nil {
return nil, err
}
responsesB64 := regexp.MustCompile("\\s").Split(string(fileContents), -1)
src := InMemorySource{}
for _, b64 := range responsesB64 {
// if the line/space is empty just skip
if b64 == "" {
continue
}
der, tmpErr := base64.StdEncoding.DecodeString(b64)
if tmpErr != nil {
logrus.Errorf("Base64 decode error %s on: %s", tmpErr, b64)
continue
}
response, tmpErr := ParseResponse(der, nil)
if tmpErr != nil {
logrus.Errorf("OCSP decode error %s on: %s", tmpErr, b64)
continue
}
src[response.SerialNumber.String()] = der
}
logrus.Infof("Read %d OCSP responses", len(src))
return src, nil
return response, nil, nil
}
// Stats is a basic interface that allows users to record information
@ -190,6 +153,8 @@ var hashToString = map[crypto.Hash]string{
// default handler will try to canonicalize path components by changing any
// strings of repeated '/' into a single '/', which will break the base64
// encoding.
//
//nolint:gocognit, cyclop
func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Request) {
le := logEvent{
IP: request.RemoteAddr,
@ -201,13 +166,16 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
defer func() {
le.Headers = response.Header()
le.Took = time.Since(le.Received)
jb, err := json.Marshal(le)
if err != nil {
// we log this error at the debug level as if we aren't at that level anyway
// we shouldn't really care about marshalling the log event object
// we shouldn't really care about marshaling the log event object
logrus.Debugf("failed to marshal log event object: %s", err)
return
}
logrus.Debugf("Received request: %s", string(jb))
}()
// By default we set a 'max-age=0, no-cache' Cache-Control header, this
@ -215,15 +183,20 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
// is not found or an error is returned. If a response if found the header
// will be altered to contain the proper max-age and modifiers.
response.Header().Add("Cache-Control", "max-age=0, no-cache")
// Read response from request
var requestBody []byte
var err error
var (
// Read response from request
requestBody []byte
err error
)
switch request.Method {
case "GET":
base64Request, err := url.QueryUnescape(request.URL.Path)
if err != nil {
logrus.Debugf("Error decoding URL: %s", request.URL.Path)
response.WriteHeader(http.StatusBadRequest)
return
}
// url.QueryUnescape not only unescapes %2B escaping, but it additionally
@ -243,10 +216,12 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
if len(base64RequestBytes) > 0 && base64RequestBytes[0] == '/' {
base64RequestBytes = base64RequestBytes[1:]
}
requestBody, err = base64.StdEncoding.DecodeString(string(base64RequestBytes))
if err != nil {
logrus.Debugf("Error decoding base64 from URL: %s", string(base64RequestBytes))
response.WriteHeader(http.StatusBadRequest)
return
}
case "POST":
@ -254,14 +229,19 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
if err != nil {
logrus.Errorf("Problem reading body of POST: %s", err)
response.WriteHeader(http.StatusBadRequest)
return
}
default:
response.WriteHeader(http.StatusMethodNotAllowed)
return
}
b64Body := base64.StdEncoding.EncodeToString(requestBody)
logrus.Debugf("Received OCSP request: %s", b64Body)
if request.Method == http.MethodPost {
le.Body = b64Body
}
@ -279,12 +259,15 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
if err != nil {
logrus.Debugf("Error decoding request body: %s", b64Body)
response.WriteHeader(http.StatusBadRequest)
response.Write(malformedRequestErrorResponse)
_, _ = response.Write(malformedRequestErrorResponse)
if rs.stats != nil {
rs.stats.ResponseStatus(Malformed)
}
return
}
le.Serial = fmt.Sprintf("%x", ocspRequest.SerialNumber.Bytes())
le.IssuerKeyHash = fmt.Sprintf("%x", ocspRequest.IssuerKeyHash)
le.IssuerNameHash = fmt.Sprintf("%x", ocspRequest.IssuerNameHash)
@ -293,22 +276,28 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
// Look up OCSP response from source
ocspResponse, headers, err := rs.Source.Response(ocspRequest)
if err != nil {
if err == ErrNotFound {
if errors.Is(err, ErrNotFound) {
logrus.Infof("No response found for request: serial %x, request body %s",
ocspRequest.SerialNumber, b64Body)
response.Write(unauthorizedErrorResponse)
_, _ = response.Write(unauthorizedErrorResponse)
if rs.stats != nil {
rs.stats.ResponseStatus(Unauthorized)
}
return
}
logrus.Infof("Error retrieving response for request: serial %x, request body %s, error: %s",
ocspRequest.SerialNumber, b64Body, err)
response.WriteHeader(http.StatusInternalServerError)
response.Write(internalErrorErrorResponse)
_, _ = response.Write(internalErrorErrorResponse)
if rs.stats != nil {
rs.stats.ResponseStatus(InternalError)
}
return
}
@ -316,18 +305,23 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
if err != nil {
logrus.Errorf("Error parsing response for serial %x: %s",
ocspRequest.SerialNumber, err)
response.Write(internalErrorErrorResponse)
_, _ = response.Write(internalErrorErrorResponse)
if rs.stats != nil {
rs.stats.ResponseStatus(InternalError)
}
return
}
// Write OCSP response to response
response.Header().Add("Last-Modified", parsedResponse.ThisUpdate.Format(time.RFC1123))
response.Header().Add("Expires", parsedResponse.NextUpdate.Format(time.RFC1123))
now := rs.clk.Now()
maxAge := 0
if now.Before(parsedResponse.NextUpdate) {
maxAge = int(parsedResponse.NextUpdate.Sub(now) / time.Second)
} else {
@ -335,6 +329,7 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
// (despite being stale) and 5019 forbids attaching no-cache
maxAge = 0
}
response.Header().Set(
"Cache-Control",
fmt.Sprintf(
@ -342,6 +337,7 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
maxAge,
),
)
responseHash := sha256.Sum256(ocspResponse)
response.Header().Add("ETag", fmt.Sprintf("\"%X\"", responseHash))
@ -355,11 +351,14 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
if etag := request.Header.Get("If-None-Match"); etag != "" {
if etag == fmt.Sprintf("\"%X\"", responseHash) {
response.WriteHeader(http.StatusNotModified)
return
}
}
response.WriteHeader(http.StatusOK)
response.Write(ocspResponse)
_, _ = response.Write(ocspResponse)
if rs.stats != nil {
rs.stats.ResponseStatus(Success)
}

@ -24,21 +24,13 @@ import (
"net/url"
"reflect"
"testing"
"time"
"github.com/jmhodges/clock"
)
const (
responseFile = "testdata/resp64.pem"
binResponseFile = "testdata/response.der"
brokenResponseFile = "testdata/response_broken.pem"
mixResponseFile = "testdata/response_mix.pem"
)
type testSource struct{}
func (ts testSource) Response(r *Request) ([]byte, http.Header, error) {
func (ts testSource) Response(_ *Request) ([]byte, http.Header, error) {
return []byte("hi"), nil, nil
}
@ -52,22 +44,22 @@ func TestOCSP(t *testing.T) {
{"OPTIONS", "/", http.StatusMethodNotAllowed},
{"GET", "/", http.StatusBadRequest},
// Bad URL encoding
// nolint:lll
//nolint:lll
{"GET", "%ZZFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Bad URL encoding
// nolint:lll
//nolint:lll
{"GET", "%%FQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Bad base64 encoding
// nolint:lll
//nolint:lll
{"GET", "==MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Bad OCSP DER encoding
// nolint:lll
//nolint:lll
{"GET", "AAAMFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Good encoding all around, including a double slash
// nolint:lll
//nolint:lll
{"GET", "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK},
// Good request, leading slash
// nolint:lll
//nolint:lll
{"GET", "/MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK},
}
@ -85,21 +77,23 @@ func TestOCSP(t *testing.T) {
Path: tc.path,
},
})
if rw.Code != tc.expected {
t.Errorf("Incorrect response code: got %d, wanted %d", rw.Code, tc.expected)
}
}
}
// nolint:lll
//nolint:lll
var testResp = `308204f90a0100a08204f2308204ee06092b0601050507300101048204df308204db3081a7a003020100a121301f311d301b06035504030c146861707079206861636b65722066616b65204341180f32303135303932333231303630305a306c306a3042300906052b0e03021a0500041439e45eb0e3a861c7fa3a3973876be61f7b7d98860414fb784f12f96015832c9f177f3419b32e36ea41890209009cf1912ea8d509088000180f32303135303932333030303030305aa011180f32303330303832363030303030305a300d06092a864886f70d01010b05000382010100c17ed5f12c408d214092c86cb2d6ba9881637a9d5cafb8ddc05aed85806a554c37abdd83c2e00a4bb25b2d0dda1e1c0be65144377471bca53f14616f379ee0c0b436c697b400b7eba9513c5be6d92fbc817586d568156293cfa0099d64585146def907dee36eb650c424a00207b01813aa7ae90e65045339482eeef12b6fa8656315da8f8bb1375caa29ac3858f891adb85066c35b5176e154726ae746016e42e0d6016668ff10a8aa9637417d29be387a1bdba9268b13558034ab5f3e498a47fb096f2e1b39236b22956545884fbbed1884f1bc9686b834d8def4802bac8f79924a36867af87412f808977abaf6457f3cda9e7eccbd0731bcd04865b899ee41a08203193082031530820311308201f9a0030201020209009cf1912ea8d50908300d06092a864886f70d01010b0500301f311d301b06035504030c146861707079206861636b65722066616b65204341301e170d3135303430373233353033385a170d3235303430343233353033385a301f311d301b06035504030c146861707079206861636b65722066616b6520434130820122300d06092a864886f70d01010105000382010f003082010a0282010100c20a47799a05c512b27717633413d770f936bf99de62f130c8774d476deac0029aa6c9d1bb519605df32d34b336394d48e9adc9bbeb48652767dafdb5241c2fc54ce9650e33cb672298888c403642407270cc2f46667f07696d3dd62cfd1f41a8dc0ed60d7c18366b1d2cd462d34a35e148e8695a9a3ec62b656bd129a211a9a534847992d005b0412bcdffdde23085eeca2c32c2693029b5a79f1090fe0b1cb4a154b5c36bc04c7d5a08fa2a58700d3c88d5059205bc5560dc9480f1732b1ad29b030ed3235f7fb868f904fdc79f98ffb5c4e7d4b831ce195f171729ec3f81294df54e66bd3f83d81843b640aea5d7ec64d0905a9dbb03e6ff0e6ac523d36ab0203010001a350304e301d0603551d0e04160414fb784f12f96015832c9f177f3419b32e36ea4189301f0603551d23041830168014fb784f12f96015832c9f177f3419b32e36ea4189300c0603551d13040530030101ff300d06092a864886f70d01010b050003820101001df436be66ff938ccbfb353026962aa758763a777531119377845109e7c2105476c165565d5bbce1464b41bd1d392b079a7341c978af754ca9b3bd7976d485cbbe1d2070d2d4feec1e0f79e8fec9df741e0ea05a26a658d3866825cc1aa2a96a0a04942b2c203cc39501f917a899161dfc461717fe9301fce6ea1afffd7b7998f8941cf76f62def994c028bd1c4b49b17c4d243a6fb058c484968cf80501234da89347108b56b2640cb408e3c336fd72cd355c7f690a15405a7f4ba1e30a6be4a51d262b586f77f8472b207fdd194efab8d3a2683cc148abda7a11b9de1db9307b8ed5a9cd20226f668bd6ac5a3852fd449e42899b7bc915ee747891a110a971`
type testHeaderSource struct {
headers http.Header
}
func (ts testHeaderSource) Response(r *Request) ([]byte, http.Header, error) {
func (ts testHeaderSource) Response(_ *Request) ([]byte, http.Header, error) {
resp, _ := hex.DecodeString(testResp)
return resp, ts.headers, nil
}
@ -120,104 +114,11 @@ func TestOverrideHeaders(t *testing.T) {
rw := httptest.NewRecorder()
responder.ServeHTTP(rw, &http.Request{
Method: "GET",
URL: &url.URL{Path: "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D"},
//nolint:lll
URL: &url.URL{Path: "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D"},
})
if !reflect.DeepEqual(rw.Header(), headers) {
t.Fatalf("Unexpected Headers returned: wanted %s, got %s", headers, rw.Header())
}
}
func TestCacheHeaders(t *testing.T) {
source, err := NewSourceFromFile(responseFile)
if err != nil {
t.Fatalf("Error constructing source: %s", err)
}
fc := clock.NewFake()
fc.Set(time.Date(2015, 11, 12, 0, 0, 0, 0, time.UTC))
responder := Responder{
Source: source,
clk: fc,
}
rw := httptest.NewRecorder()
responder.ServeHTTP(rw, &http.Request{
Method: "GET",
URL: &url.URL{
Path: "MEMwQTA/MD0wOzAJBgUrDgMCGgUABBSwLsMRhyg1dJUwnXWk++D57lvgagQU6aQ/7p6l5vLV13lgPJOmLiSOl6oCAhJN",
},
})
if rw.Code != http.StatusOK {
t.Errorf("Unexpected HTTP status code %d", rw.Code)
}
testCases := []struct {
header string
value string
}{
{"Last-Modified", "Tue, 20 Oct 2015 00:00:00 UTC"},
{"Expires", "Sun, 20 Oct 2030 00:00:00 UTC"},
{"Cache-Control", "max-age=471398400, public, no-transform, must-revalidate"},
{"Etag", "\"8169FB0843B081A76E9F6F13FD70C8411597BEACF8B182136FFDD19FBD26140A\""},
}
for _, tc := range testCases {
headers, ok := rw.Header()[tc.header]
if !ok {
t.Errorf("Header %s missing from HTTP response", tc.header)
continue
}
if len(headers) != 1 {
t.Errorf("Wrong number of headers in HTTP response. Wanted 1, got %d", len(headers))
continue
}
actual := headers[0]
if actual != tc.value {
t.Errorf("Got header %s: %s. Expected %s", tc.header, actual, tc.value)
}
}
rw = httptest.NewRecorder()
headers := http.Header{}
headers.Add("If-None-Match", "\"8169FB0843B081A76E9F6F13FD70C8411597BEACF8B182136FFDD19FBD26140A\"")
responder.ServeHTTP(rw, &http.Request{
Method: "GET",
URL: &url.URL{
Path: "MEMwQTA/MD0wOzAJBgUrDgMCGgUABBSwLsMRhyg1dJUwnXWk++D57lvgagQU6aQ/7p6l5vLV13lgPJOmLiSOl6oCAhJN",
},
Header: headers,
})
if rw.Code != http.StatusNotModified {
t.Fatalf("Got wrong status code: expected %d, got %d", http.StatusNotModified, rw.Code)
}
}
func TestNewSourceFromFile(t *testing.T) {
_, err := NewSourceFromFile("")
if err == nil {
t.Fatal("Didn't fail on non-file input")
}
// expected case
_, err = NewSourceFromFile(responseFile)
if err != nil {
t.Fatal(err)
}
// binary-formatted file
_, err = NewSourceFromFile(binResponseFile)
if err != nil {
t.Fatal(err)
}
// the response file from before, with stuff deleted
_, err = NewSourceFromFile(brokenResponseFile)
if err != nil {
t.Fatal(err)
}
// mix of a correct and malformed responses
_, err = NewSourceFromFile(mixResponseFile)
if err != nil {
t.Fatal(err)
}
}

@ -288,7 +288,7 @@ func (suite *OcspSourceTestSuite) newRandomSerial() *big.Int {
func (suite *OcspSourceTestSuite) deserializeKey(index int) crypto.Signer {
var (
// nolint:lll
//nolint:lll
pkcs8Keys = []string{
`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`,
`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`,
@ -394,7 +394,7 @@ func (suite *OcspSourceTestSuite) TestCertificateInIndexTxt() {
suite.FailNow("could not retrieve response: %v", err)
}
defer response.Body.Close()
defer func() { _ = response.Body.Close() }()
assert.Equal(suite.T(), 200, response.StatusCode)

Loading…
Cancel
Save