Skip to content

Commit 4da111c

Browse files
committed
ocsp: better validate OCSP response's certificates
We make three changes here: 1. Allow iterating over all given certificates to find the one that signed this OCSP response, as RFC 6960 does not guarantee an order and some CAs send multiple certificates, and 2. Allow the passed issuer to match the certificate that directly signed this response, and 3. Lastly, we document the unsafe behavior of calling these functions with issuer=nil, indicating that it performs no trust verification. Previously, when a CA returned the intermediate CA that signed a leaf cert as an additional cert in the response field (without using a delegated OCSP certificate), Go would err with a bad signature, as it expected the intermediate CA to have signed the wire copy (even though it was the exact same certificate). Also includes a code comment around the "bad signature on embedded certificate" error message, indicating that this isn't strictly the correct preposition choice. See also: https://github.com/crtsh/test_websites_monitor/blob/1bd8226b5f963e91d7889ea432a36e3173be8eec/test_websites_monitor.go#L267 See also: golang/go#59641 Fixes golang/go#59641
1 parent 1faeef9 commit 4da111c

File tree

1 file changed

+52
-13
lines changed

1 file changed

+52
-13
lines changed

ocsp/ocsp.go

+52-13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
_ "crypto/sha1"
1717
_ "crypto/sha256"
1818
_ "crypto/sha512"
19+
"crypto/subtle"
1920
"crypto/x509"
2021
"crypto/x509/pkix"
2122
"encoding/asn1"
@@ -458,7 +459,9 @@ func ParseRequest(bytes []byte) (*Request, error) {
458459
// the signature on the embedded certificate.
459460
//
460461
// If the response does not contain an embedded certificate and issuer is not
461-
// nil, then issuer will be used to verify the response signature.
462+
// nil, then issuer will be used to verify the response signature. This is unsafe
463+
// unless the OCSP request and response was transmitted over a secure channel,
464+
// such as HTTPS signed by an external CA.
462465
//
463466
// Invalid responses and parse failures will result in a ParseError.
464467
// Error responses will result in a ResponseError.
@@ -551,31 +554,67 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
551554
}
552555

553556
if len(basicResp.Certificates) > 0 {
554-
// Responders should only send a single certificate (if they
557+
// Responders SHOULD only send a single certificate (if they
555558
// send any) that connects the responder's certificate to the
556559
// original issuer. We accept responses with multiple
557-
// certificates due to a number responders sending them[1], but
558-
// ignore all but the first.
560+
// certificates due to a number responders sending them [1, 2].
561+
// However, we only retain the one that signed this request:
562+
// notably, RFC 6960 does not guarantee an order, but states
563+
// that any delegated OCSP responder certificate MUST be
564+
// directly issued by the CA that issued this certificate [2],
565+
// so any remaining certificates presumably overlap with the
566+
// existing certs known by the caller.
559567
//
560568
// [1] https://github.com/golang/go/issues/21527
561-
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
562-
if err != nil {
563-
return nil, err
569+
// [2] https://github.com/golang/go/issues/59641
570+
var lastErr error
571+
for _, candidateBytes := range basicResp.Certificates {
572+
candidate, err := x509.ParseCertificate(candidateBytes.FullBytes)
573+
if err != nil {
574+
return nil, ParseError("bad embedded certificate: " + err.Error())
575+
}
576+
577+
if err := ret.CheckSignatureFrom(candidate); err != nil {
578+
lastErr = err
579+
continue
580+
}
581+
582+
// We found a certificate which directly issued this OCSP
583+
// request; set it as our certificate object and clear any
584+
// errors.
585+
lastErr = nil
586+
ret.Certificate = candidate
587+
break
564588
}
565589

566-
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
590+
if lastErr != nil {
591+
// n.b.: this error message string is checked by callers but is
592+
// incorrectly worded: the signature on the _response_ is bad,
593+
// from this selected certificate, not the signature on this
594+
// embedded certificate from some parent issuer.
567595
return nil, ParseError("bad signature on embedded certificate: " + err.Error())
568596
}
597+
}
569598

570-
if issuer != nil {
599+
// When given a trusted issuer CA, use it to validate that this
600+
// response is valid.
601+
if issuer != nil {
602+
if ret.Certificate == nil {
603+
// We have no other information about this request, so require
604+
// it to be signed by this trusted issuer.
605+
if err := ret.CheckSignatureFrom(issuer); err != nil {
606+
return nil, ParseError("bad OCSP signature: " + err.Error())
607+
}
608+
} else if subtle.ConstantTimeCompare(ret.Certificate.Raw, issuer.Raw) == 0 {
609+
// Since we had a certificate, we had two scenarios: (1) either the
610+
// responder unnecessarily sent us the issuer again (guarded by
611+
// the above if), or (2), we have a different certificate here in
612+
// this branch and thus we need to check the signature from
613+
// issuer->ret.Certificate.
571614
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
572615
return nil, ParseError("bad OCSP signature: " + err.Error())
573616
}
574617
}
575-
} else if issuer != nil {
576-
if err := ret.CheckSignatureFrom(issuer); err != nil {
577-
return nil, ParseError("bad OCSP signature: " + err.Error())
578-
}
579618
}
580619

581620
for _, ext := range singleResp.SingleExtensions {

0 commit comments

Comments
 (0)