Fix golangci-lint warnings

This commit is contained in:
Jan Dittberner 2022-03-29 18:20:28 +02:00
parent a525cd0416
commit 8dbfc208b9
6 changed files with 186 additions and 212 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -24,21 +24,13 @@ import (
"net/url" "net/url"
"reflect" "reflect"
"testing" "testing"
"time"
"github.com/jmhodges/clock" "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{} 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 return []byte("hi"), nil, nil
} }
@ -52,22 +44,22 @@ func TestOCSP(t *testing.T) {
{"OPTIONS", "/", http.StatusMethodNotAllowed}, {"OPTIONS", "/", http.StatusMethodNotAllowed},
{"GET", "/", http.StatusBadRequest}, {"GET", "/", http.StatusBadRequest},
// Bad URL encoding // Bad URL encoding
// nolint:lll //nolint:lll
{"GET", "%ZZFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest}, {"GET", "%ZZFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Bad URL encoding // Bad URL encoding
// nolint:lll //nolint:lll
{"GET", "%%FQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest}, {"GET", "%%FQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Bad base64 encoding // Bad base64 encoding
// nolint:lll //nolint:lll
{"GET", "==MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest}, {"GET", "==MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Bad OCSP DER encoding // Bad OCSP DER encoding
// nolint:lll //nolint:lll
{"GET", "AAAMFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest}, {"GET", "AAAMFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusBadRequest},
// Good encoding all around, including a double slash // Good encoding all around, including a double slash
// nolint:lll //nolint:lll
{"GET", "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK}, {"GET", "MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK},
// Good request, leading slash // Good request, leading slash
// nolint:lll //nolint:lll
{"GET", "/MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK}, {"GET", "/MFQwUjBQME4wTDAJBgUrDgMCGgUABBQ55F6w46hhx%2Fo6OXOHa%2BYfe32YhgQU%2B3hPEvlgFYMsnxd%2FNBmzLjbqQYkCEwD6Wh0MaVKu9gJ3By9DI%2F%2Fxsd4%3D", http.StatusOK},
} }
@ -85,21 +77,23 @@ func TestOCSP(t *testing.T) {
Path: tc.path, Path: tc.path,
}, },
}) })
if rw.Code != tc.expected { if rw.Code != tc.expected {
t.Errorf("Incorrect response code: got %d, wanted %d", rw.Code, tc.expected) t.Errorf("Incorrect response code: got %d, wanted %d", rw.Code, tc.expected)
} }
} }
} }
// nolint:lll //nolint:lll
var testResp = `308204f90a0100a08204f2308204ee06092b0601050507300101048204df308204db3081a7a003020100a121301f311d301b06035504030c146861707079206861636b65722066616b65204341180f32303135303932333231303630305a306c306a3042300906052b0e03021a0500041439e45eb0e3a861c7fa3a3973876be61f7b7d98860414fb784f12f96015832c9f177f3419b32e36ea41890209009cf1912ea8d509088000180f32303135303932333030303030305aa011180f32303330303832363030303030305a300d06092a864886f70d01010b05000382010100c17ed5f12c408d214092c86cb2d6ba9881637a9d5cafb8ddc05aed85806a554c37abdd83c2e00a4bb25b2d0dda1e1c0be65144377471bca53f14616f379ee0c0b436c697b400b7eba9513c5be6d92fbc817586d568156293cfa0099d64585146def907dee36eb650c424a00207b01813aa7ae90e65045339482eeef12b6fa8656315da8f8bb1375caa29ac3858f891adb85066c35b5176e154726ae746016e42e0d6016668ff10a8aa9637417d29be387a1bdba9268b13558034ab5f3e498a47fb096f2e1b39236b22956545884fbbed1884f1bc9686b834d8def4802bac8f79924a36867af87412f808977abaf6457f3cda9e7eccbd0731bcd04865b899ee41a08203193082031530820311308201f9a0030201020209009cf1912ea8d50908300d06092a864886f70d01010b0500301f311d301b06035504030c146861707079206861636b65722066616b65204341301e170d3135303430373233353033385a170d3235303430343233353033385a301f311d301b06035504030c146861707079206861636b65722066616b6520434130820122300d06092a864886f70d01010105000382010f003082010a0282010100c20a47799a05c512b27717633413d770f936bf99de62f130c8774d476deac0029aa6c9d1bb519605df32d34b336394d48e9adc9bbeb48652767dafdb5241c2fc54ce9650e33cb672298888c403642407270cc2f46667f07696d3dd62cfd1f41a8dc0ed60d7c18366b1d2cd462d34a35e148e8695a9a3ec62b656bd129a211a9a534847992d005b0412bcdffdde23085eeca2c32c2693029b5a79f1090fe0b1cb4a154b5c36bc04c7d5a08fa2a58700d3c88d5059205bc5560dc9480f1732b1ad29b030ed3235f7fb868f904fdc79f98ffb5c4e7d4b831ce195f171729ec3f81294df54e66bd3f83d81843b640aea5d7ec64d0905a9dbb03e6ff0e6ac523d36ab0203010001a350304e301d0603551d0e04160414fb784f12f96015832c9f177f3419b32e36ea4189301f0603551d23041830168014fb784f12f96015832c9f177f3419b32e36ea4189300c0603551d13040530030101ff300d06092a864886f70d01010b050003820101001df436be66ff938ccbfb353026962aa758763a777531119377845109e7c2105476c165565d5bbce1464b41bd1d392b079a7341c978af754ca9b3bd7976d485cbbe1d2070d2d4feec1e0f79e8fec9df741e0ea05a26a658d3866825cc1aa2a96a0a04942b2c203cc39501f917a899161dfc461717fe9301fce6ea1afffd7b7998f8941cf76f62def994c028bd1c4b49b17c4d243a6fb058c484968cf80501234da89347108b56b2640cb408e3c336fd72cd355c7f690a15405a7f4ba1e30a6be4a51d262b586f77f8472b207fdd194efab8d3a2683cc148abda7a11b9de1db9307b8ed5a9cd20226f668bd6ac5a3852fd449e42899b7bc915ee747891a110a971` var testResp = `308204f90a0100a08204f2308204ee06092b0601050507300101048204df308204db3081a7a003020100a121301f311d301b06035504030c146861707079206861636b65722066616b65204341180f32303135303932333231303630305a306c306a3042300906052b0e03021a0500041439e45eb0e3a861c7fa3a3973876be61f7b7d98860414fb784f12f96015832c9f177f3419b32e36ea41890209009cf1912ea8d509088000180f32303135303932333030303030305aa011180f32303330303832363030303030305a300d06092a864886f70d01010b05000382010100c17ed5f12c408d214092c86cb2d6ba9881637a9d5cafb8ddc05aed85806a554c37abdd83c2e00a4bb25b2d0dda1e1c0be65144377471bca53f14616f379ee0c0b436c697b400b7eba9513c5be6d92fbc817586d568156293cfa0099d64585146def907dee36eb650c424a00207b01813aa7ae90e65045339482eeef12b6fa8656315da8f8bb1375caa29ac3858f891adb85066c35b5176e154726ae746016e42e0d6016668ff10a8aa9637417d29be387a1bdba9268b13558034ab5f3e498a47fb096f2e1b39236b22956545884fbbed1884f1bc9686b834d8def4802bac8f79924a36867af87412f808977abaf6457f3cda9e7eccbd0731bcd04865b899ee41a08203193082031530820311308201f9a0030201020209009cf1912ea8d50908300d06092a864886f70d01010b0500301f311d301b06035504030c146861707079206861636b65722066616b65204341301e170d3135303430373233353033385a170d3235303430343233353033385a301f311d301b06035504030c146861707079206861636b65722066616b6520434130820122300d06092a864886f70d01010105000382010f003082010a0282010100c20a47799a05c512b27717633413d770f936bf99de62f130c8774d476deac0029aa6c9d1bb519605df32d34b336394d48e9adc9bbeb48652767dafdb5241c2fc54ce9650e33cb672298888c403642407270cc2f46667f07696d3dd62cfd1f41a8dc0ed60d7c18366b1d2cd462d34a35e148e8695a9a3ec62b656bd129a211a9a534847992d005b0412bcdffdde23085eeca2c32c2693029b5a79f1090fe0b1cb4a154b5c36bc04c7d5a08fa2a58700d3c88d5059205bc5560dc9480f1732b1ad29b030ed3235f7fb868f904fdc79f98ffb5c4e7d4b831ce195f171729ec3f81294df54e66bd3f83d81843b640aea5d7ec64d0905a9dbb03e6ff0e6ac523d36ab0203010001a350304e301d0603551d0e04160414fb784f12f96015832c9f177f3419b32e36ea4189301f0603551d23041830168014fb784f12f96015832c9f177f3419b32e36ea4189300c0603551d13040530030101ff300d06092a864886f70d01010b050003820101001df436be66ff938ccbfb353026962aa758763a777531119377845109e7c2105476c165565d5bbce1464b41bd1d392b079a7341c978af754ca9b3bd7976d485cbbe1d2070d2d4feec1e0f79e8fec9df741e0ea05a26a658d3866825cc1aa2a96a0a04942b2c203cc39501f917a899161dfc461717fe9301fce6ea1afffd7b7998f8941cf76f62def994c028bd1c4b49b17c4d243a6fb058c484968cf80501234da89347108b56b2640cb408e3c336fd72cd355c7f690a15405a7f4ba1e30a6be4a51d262b586f77f8472b207fdd194efab8d3a2683cc148abda7a11b9de1db9307b8ed5a9cd20226f668bd6ac5a3852fd449e42899b7bc915ee747891a110a971`
type testHeaderSource struct { type testHeaderSource struct {
headers http.Header 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) resp, _ := hex.DecodeString(testResp)
return resp, ts.headers, nil return resp, ts.headers, nil
} }
@ -120,104 +114,11 @@ func TestOverrideHeaders(t *testing.T) {
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
responder.ServeHTTP(rw, &http.Request{ responder.ServeHTTP(rw, &http.Request{
Method: "GET", 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) { if !reflect.DeepEqual(rw.Header(), headers) {
t.Fatalf("Unexpected Headers returned: wanted %s, got %s", headers, rw.Header()) 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)
}
}

View file

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