ocsp_helper: allow suppressing output. (#5103)

This adds a configurable output parameter for ocsp_helper, which
defaults to stdout. This allows suppressing the stdout output when using
ocsp_helper in integration tests. That output was making it hard to
see details about failing tests.
This commit is contained in:
Jacob Hoffman-Andrews 2020-09-25 14:03:50 -07:00 committed by GitHub
parent d3f2efcb7f
commit d90a2817c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 28 deletions

View File

@ -3,6 +3,7 @@
package integration
import (
"io/ioutil"
"os"
"strings"
"testing"
@ -39,7 +40,7 @@ func TestPrecertificateOCSP(t *testing.T) {
t.Fatalf("couldn't find rejected precert for %q", domain)
}
ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Good)
ocspConfig := ocsp_helper.DefaultConfig.WithExpectStatus(ocsp.Good).WithOutput(ioutil.Discard)
_, err = ocsp_helper.ReqDER(cert.Raw, ocspConfig)
if err != nil {
t.Errorf("requesting OCSP for rejected precertificate: %s", err)

View File

@ -8,9 +8,11 @@ import (
"encoding/pem"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"sync"
"time"
@ -37,6 +39,7 @@ type Config struct {
ignoreExpiredCerts bool
expectStatus int
expectReason int
output io.Writer
}
// DefaultConfig is a Config populated with the same defaults as if no
@ -49,6 +52,7 @@ var DefaultConfig = Config{
ignoreExpiredCerts: *ignoreExpiredCerts,
expectStatus: *expectStatus,
expectReason: *expectReason,
output: os.Stdout,
}
var parseFlagsOnce sync.Once
@ -78,6 +82,14 @@ func (template Config) WithExpectStatus(status int) Config {
return ret
}
// WithOutput returns a new Config with the given output,
// and all other fields the same as the receiver.
func (template Config) WithOutput(w io.Writer) Config {
ret := template
ret.output = w
return ret
}
func getIssuer(cert *x509.Certificate) (*x509.Certificate, error) {
if cert == nil {
return nil, fmt.Errorf("nil certificate")
@ -187,14 +199,14 @@ func ReqDER(der []byte, config Config) (*ocsp.Response, error) {
return nil, err
}
httpResp, err := sendHTTPRequest(req, ocspURL, config.method, config.hostOverride)
httpResp, err := sendHTTPRequest(req, ocspURL, config.method, config.hostOverride, config.output)
if err != nil {
return nil, err
}
fmt.Printf("HTTP %d\n", httpResp.StatusCode)
fmt.Fprintf(config.output, "HTTP %d\n", httpResp.StatusCode)
for k, v := range httpResp.Header {
for _, vv := range v {
fmt.Printf("%s: %s\n", k, vv)
fmt.Fprintf(config.output, "%s: %s\n", k, vv)
}
}
if httpResp.StatusCode != 200 {
@ -211,16 +223,22 @@ func ReqDER(der []byte, config Config) (*ocsp.Response, error) {
return parseAndPrint(respBytes, cert, issuer, config)
}
func sendHTTPRequest(req []byte, ocspURL *url.URL, method string, host string) (*http.Response, error) {
func sendHTTPRequest(
req []byte,
ocspURL *url.URL,
method string,
host string,
output io.Writer,
) (*http.Response, error) {
encodedReq := base64.StdEncoding.EncodeToString(req)
var httpRequest *http.Request
var err error
if method == "GET" {
ocspURL.Path = encodedReq
fmt.Printf("Fetching %s\n", ocspURL.String())
fmt.Fprintf(output, "Fetching %s\n", ocspURL.String())
httpRequest, err = http.NewRequest("GET", ocspURL.String(), http.NoBody)
} else if method == "POST" {
fmt.Printf("POSTing request, reproduce with: curl -i --data-binary @- %s < <(base64 -d <<<%s)\n",
fmt.Fprintf(output, "POSTing request, reproduce with: curl -i --data-binary @- %s < <(base64 -d <<<%s)\n",
ocspURL, encodedReq)
httpRequest, err = http.NewRequest("POST", ocspURL.String(), bytes.NewBuffer(req))
} else {
@ -259,12 +277,12 @@ func getOCSPURL(cert *x509.Certificate, urlOverride string) (*url.URL, error) {
// checkSignerTimes checks that the OCSP response is within the
// validity window of whichever certificate signed it, and that that
// certificate is currently valid.
func checkSignerTimes(resp *ocsp.Response, issuer *x509.Certificate) error {
func checkSignerTimes(resp *ocsp.Response, issuer *x509.Certificate, output io.Writer) error {
var ocspSigner = issuer
if delegatedSigner := resp.Certificate; delegatedSigner != nil {
ocspSigner = delegatedSigner
fmt.Printf("Using delegated OCSP signer from response: %s\n",
fmt.Fprintf(output, "Using delegated OCSP signer from response: %s\n",
base64.StdEncoding.EncodeToString(ocspSigner.Raw))
}
@ -287,7 +305,7 @@ func checkSignerTimes(resp *ocsp.Response, issuer *x509.Certificate) error {
}
func parseAndPrint(respBytes []byte, cert, issuer *x509.Certificate, config Config) (*ocsp.Response, error) {
fmt.Printf("\nDecoding body: %s\n", base64.StdEncoding.EncodeToString(respBytes))
fmt.Fprintf(config.output, "\nDecoding body: %s\n", base64.StdEncoding.EncodeToString(respBytes))
resp, err := ocsp.ParseResponseForCert(respBytes, cert, issuer)
if err != nil {
return nil, fmt.Errorf("parsing response: %s", err)
@ -306,30 +324,34 @@ func parseAndPrint(respBytes []byte, cert, issuer *x509.Certificate, config Conf
errs = append(errs, fmt.Errorf("NextUpdate is too soon: %s", timeTilExpiry))
}
err = checkSignerTimes(resp, issuer)
err = checkSignerTimes(resp, issuer, config.output)
if err != nil {
errs = append(errs, fmt.Errorf("checking signature on delegated signer: %s", err))
}
fmt.Print("\n")
fmt.Print("Response:\n")
fmt.Printf(" CertStatus %d\n", resp.Status)
fmt.Printf(" SerialNumber %036x\n", resp.SerialNumber)
fmt.Printf(" ProducedAt %s\n", resp.ProducedAt)
fmt.Printf(" ThisUpdate %s\n", resp.ThisUpdate)
fmt.Printf(" NextUpdate %s\n", resp.NextUpdate)
fmt.Printf(" RevokedAt %s\n", resp.RevokedAt)
fmt.Printf(" RevocationReason %d\n", resp.RevocationReason)
fmt.Printf(" SignatureAlgorithm %s\n", resp.SignatureAlgorithm)
fmt.Printf(" Extensions %#v\n", resp.Extensions)
pr := func(s string, v ...interface{}) {
fmt.Fprintf(config.output, s, v)
}
pr("\n")
pr("Response:\n")
pr(" CertStatus %d\n", resp.Status)
pr(" SerialNumber %036x\n", resp.SerialNumber)
pr(" ProducedAt %s\n", resp.ProducedAt)
pr(" ThisUpdate %s\n", resp.ThisUpdate)
pr(" NextUpdate %s\n", resp.NextUpdate)
pr(" RevokedAt %s\n", resp.RevokedAt)
pr(" RevocationReason %d\n", resp.RevocationReason)
pr(" SignatureAlgorithm %s\n", resp.SignatureAlgorithm)
pr(" Extensions %#v\n", resp.Extensions)
if resp.Certificate == nil {
fmt.Print(" Certificate: nil\n")
pr(" Certificate: nil\n")
} else {
fmt.Print(" Certificate:\n")
fmt.Printf(" Subject: %s\n", resp.Certificate.Subject)
fmt.Printf(" Issuer: %s\n", resp.Certificate.Issuer)
fmt.Printf(" NotBefore: %s\n", resp.Certificate.NotBefore)
fmt.Printf(" NotAfter: %s\n", resp.Certificate.NotAfter)
pr(" Certificate:\n")
pr(" Subject: %s\n", resp.Certificate.Subject)
pr(" Issuer: %s\n", resp.Certificate.Issuer)
pr(" NotBefore: %s\n", resp.Certificate.NotBefore)
pr(" NotAfter: %s\n", resp.Certificate.NotAfter)
}
if len(errs) > 0 {