Fix golangci-lint warnings
This commit is contained in:
parent
a525cd0416
commit
8dbfc208b9
6 changed files with 186 additions and 212 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
163
pkg/ocsp/ocsp.go
163
pkg/ocsp/ocsp.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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{
|
||||||
`MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC0sI1pZMtN8MyKE2uO8Jx7nythIzQ6QtG07Tj+CwjdsTlrLYOjtmXl5NHaElAFcCdknX5TT/P+z+uBwbFUQbTsp0Z9Dd/SX4M5VXHFyPDFHLfBdAcRO/ECy7JxbntDBdS5skpDZzsFFDUXrKtAZoAUtRn8DsrHWoSDKpoMvkT5dRyJEv6AJGhV8853PGBWH59hcPqXYts/mYOPpfGcQ9JvePKTc2+Xp+OOYm0pG8D+tpDwLu5qy0NXjgRRUS1Gs2chpV4M+cvYEU0bfZ6j5ZzaaiqWHdKlRJxL9dHArI4Lx0A4r6lTbTbkheDqndYa4i+Nxcp7FxiJEsgahCfHmdRinY+9YpFaxT3v2925y+UWCW3CYD93iumbD1XwAwrVt5ipUpW00P5Dm0A623S0WPw2sN9dCKruQtj1rDHudi8lYw3hwpQu/mEJ5rvLDnq/yINFRnQTbRGvyjiVgppIwgNzC393nPXxy7MVkKvlxLqoXtkEtZHf37rT8mjsQFLV9o4U+rKl9Bw3MiuufEu3aL7UMZ7Y1VwRBEgpQVW1sN2ycpm6mR9UMRNUMQZlF5PcODKN1rSn0G49xiUTTHfzdql1xzmh37iuFXMju071ZLAOweHNZp6E61p6PyiPhlmoWToX/UpMsCjEqLmGv8odAZnvAn+dWsg+B0pkJn4qYGwOWQIDAQABAoICAQCFYwBBmYLr2qNkGsoAD2e1at8fKlnX2JPuuGCmSYcWAUqd8E+Jf5DhkXXJQlOIcC7ke89RuWgp51u3wkEiLg9d1G6YyrE8H/5DSOxgUeJL4NNWIE1HT7Svl6f3TNP5ukg7fX6NG5vaN/ypqXISbJiIsNip1lGjsRK5sa1fUuagMPAL0NXHyxiquvzj0NJdQaLqz/ht4TBxVuZkGOCvtvGWEPciGsl6bxnxdn+XJUHnxuZgKIUgmUTxUYmmbgK6ep3bnLQ9Z/ovWzZM4QNHgq20H0Oo5gMmteubt7BMSBXkZAbo0eRyDeHD+YD8MeBooCjyw9yh2X7nUdIeoQistMavdoyqCpp+3ppDTE4aXEDXEGvjlDrRyoxMu+IYTrONDICT1H+0UDyxHBMpMXslC5WiqqPaXaiE3APxQJlTViOSYfi1AIfn6XTMDDX7xrIFryAn3/YRU3BvGEQRPdAp/PioCl1pwYrGUPzN9dpngCTlJMaGNq689paQ7cqft/+WTi/0d2znrZvfjTD8ebtMi3JRvN6gWNGdipUZylcxk+3xFZwijDf4aiFQTDEWB9/ztDILU8umVdx3Pg+95/7H7/dGb27hmgGPdKhcL7LdHc0R/l1AVSI+ktkyVW4WuFgDtdxvfXaG9ueYIumHVETgGyezwMjhWr4jXanmdHti2ckoAQKCAQEA8KUNYVMxCFvHFqlu39tS8uhUuW/QlmZJntalbqJSDFMcfjVYIxWH/t0lmoNcql4gp+PizG9yPZEr1/hM6xfVBONdZRZkLYfgfpL4eIxeR3yKog/THyYY8ePpl9kMO985dV/iPGGttXPAy4KLF4/PAgR+yXTbQS6IGZK+m0ND4OfkNb1jL6FhyiyQQMoOzqaiixziVCctD28IFAj8X9/G1Lei/LkrZsTh2nUxrPQijuwoYjhd/mZkRiUXGRrqydniRRU0u5SEgTciriwaNp1KEMCJH2+tqddEU1OCd6I9fzs7d0M+6I3WVypZXq1o/zLQszuUSjTeHvDO+HPMwQC6QQKCAQEAwDghREsLkh9hvCi42DXE8aKClXwfEErk26ptGM0IKp4Vs/PyV8JUvb4sfYRz0XEz5gY5cFkwGJ8na9Jn6WlZhkJFPgjDrP1eUOM1XNvWcFaCFisjneV65wue2QgZuLFKGdSM2l08V2vkAYupe/ndWqbfuOiHX9uFtlwmTrq0csmr5Nj88xFsBCee3RZQn1mBWAXnZTGjvQqMuSMBr9Erqq6YVtsueQVOWS82dZPKO7YySaFWniN3ENe6YGEehlXsfwC21fAr6q0m383ZoaB5aIOhhZZSZIAy5tslXTSygkioyMt9t8fkYOq1FbKInpCb7Qg1qbvf8L78wKVjKXZeGQKCAQEAgJq0m96pxZ3GuAW0i03a9pXTMYbgIoBnt8tefdGhp5SDFa/MenN3Sm1FO14Yl7PWH+NZwYeNtflvROwdr0X9Qa+AqhVdVDfZcct5nnLlr66PKCzs1yi1DBCRNeV49EZHnsKvVTWeb+p8jkYlr2Hbb2iXmXIp+puzgHc8Bh3cmMVU4KNl7n6AlFtcfEN/xlCnbVDyY6bMHgNofcyPk200BVpecFFFMJbnXKhk89lE3ry2mEcndF2kGRiZT4FjGJyon+LFW7nzVlrw3cQT/jvw6SL8JSC53pp8mUt33heGZghxrzrw07d5ZPvs5vwlsLynbCHbDg1S55YqBf1c34hUAQKCAQAYvKB7W3UHvwYi4tK28GSB/Dn15t1QiXLeWhZkJK6yXVnJqNE9PgMN0BWFqbAF+h2FsrNdo/yujnzSQWeiAC7HOmwSyan07z5eBn9tjdUQ6/EdzTtuLIQei75Li+FThW1V2aauy2Tikz3qfQC4tbUo32TXNQWG7odMqps7coeaZh26+7nBxjIybq7hIOgLOceNfmUC/tplj6AAi2LCL5cBBisQsnBOnsIwdAT8KKLUAGSEWUD+ZO4Dut9/W8BD5Xbj+UGOUrQ0JUhlcOZbCH3XV2lH+cpsqJrkXrt2PTvjzFn8TUPGjPD1D/9x5WHzlgSbAn11dHojT1DTsfUK9vKxAoIBAEraanpXIJqzjt5wj8z8/OjbXFXOdH3njKe2ntwPCrzU9qN4yv2/a9GEqhnBHZp4s1kznM5hCoEG3TROOKmgp4iDyq/hFKx61wZQAOdHOjCuxvOr+TXSUYJ5lW41BkjZUhm+gWO5WK1nsstaGbOUBQkbOF41zAzUvyt3klaXRaoSJL2XmcVm0VHkpO9IDFIgJNXQpEWV+uiQcghp1K1KPm9sII18g0i0KeI8LllbcmrSvC5kZcv2r9XIKz7h6qvSWYS3wzBKZxeZgCMlMAbg5RNTn72qFvPtXdJBymRvRebxuwrsMhkwvtyxk8T8ym/fKHSJuHaLt46c+r4QjO4alY0`,
|
`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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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue