...

Source file src/github.com/letsencrypt/boulder/ca/ocsp_test.go

Documentation: github.com/letsencrypt/boulder/ca

     1  package ca
     2  
     3  import (
     4  	"context"
     5  	"crypto/x509"
     6  	"encoding/hex"
     7  	"testing"
     8  	"time"
     9  
    10  	capb "github.com/letsencrypt/boulder/ca/proto"
    11  	"github.com/letsencrypt/boulder/core"
    12  	blog "github.com/letsencrypt/boulder/log"
    13  	"github.com/letsencrypt/boulder/metrics"
    14  	"github.com/letsencrypt/boulder/test"
    15  	"golang.org/x/crypto/ocsp"
    16  )
    17  
    18  func TestImplementationOCSP(t *testing.T) {
    19  	test.AssertImplementsGRPCServer(t, &ocspImpl{}, capb.UnimplementedOCSPGeneratorServer{})
    20  }
    21  
    22  func serial(t *testing.T) []byte {
    23  	serial, err := hex.DecodeString("aabbccddeeffaabbccddeeff000102030405")
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	return serial
    28  
    29  }
    30  
    31  func TestOCSP(t *testing.T) {
    32  	testCtx := setup(t)
    33  	ca, err := NewCertificateAuthorityImpl(
    34  		&mockSA{},
    35  		testCtx.pa,
    36  		testCtx.boulderIssuers,
    37  		nil,
    38  		testCtx.certExpiry,
    39  		testCtx.certBackdate,
    40  		testCtx.serialPrefix,
    41  		testCtx.maxNames,
    42  		testCtx.keyPolicy,
    43  		testCtx.logger,
    44  		testCtx.stats,
    45  		testCtx.signatureCount,
    46  		testCtx.signErrorCount,
    47  		testCtx.fc)
    48  	test.AssertNotError(t, err, "Failed to create CA")
    49  	ocspi := testCtx.ocsp
    50  
    51  	// Issue a certificate from the RSA issuer caCert, then check OCSP comes from the same issuer.
    52  	rsaIssuerID := ca.issuers.byAlg[x509.RSA].ID()
    53  	rsaCertPB, err := ca.IssuePrecertificate(ctx, &capb.IssueCertificateRequest{Csr: CNandSANCSR, RegistrationID: arbitraryRegID})
    54  	test.AssertNotError(t, err, "Failed to issue certificate")
    55  	rsaCert, err := x509.ParseCertificate(rsaCertPB.DER)
    56  	test.AssertNotError(t, err, "Failed to parse rsaCert")
    57  	rsaOCSPPB, err := ocspi.GenerateOCSP(ctx, &capb.GenerateOCSPRequest{
    58  		Serial:   core.SerialToString(rsaCert.SerialNumber),
    59  		IssuerID: int64(rsaIssuerID),
    60  		Status:   string(core.OCSPStatusGood),
    61  	})
    62  	test.AssertNotError(t, err, "Failed to generate OCSP")
    63  	rsaOCSP, err := ocsp.ParseResponse(rsaOCSPPB.Response, caCert.Certificate)
    64  	test.AssertNotError(t, err, "Failed to parse / validate OCSP for rsaCert")
    65  	test.AssertEquals(t, rsaOCSP.Status, 0)
    66  	test.AssertEquals(t, rsaOCSP.RevocationReason, 0)
    67  	test.AssertEquals(t, rsaOCSP.SerialNumber.Cmp(rsaCert.SerialNumber), 0)
    68  
    69  	// Issue a certificate from the ECDSA issuer caCert2, then check OCSP comes from the same issuer.
    70  	ecdsaIssuerID := ca.issuers.byAlg[x509.ECDSA].ID()
    71  	ecdsaCertPB, err := ca.IssuePrecertificate(ctx, &capb.IssueCertificateRequest{Csr: ECDSACSR, RegistrationID: arbitraryRegID})
    72  	test.AssertNotError(t, err, "Failed to issue certificate")
    73  	ecdsaCert, err := x509.ParseCertificate(ecdsaCertPB.DER)
    74  	test.AssertNotError(t, err, "Failed to parse ecdsaCert")
    75  	ecdsaOCSPPB, err := ocspi.GenerateOCSP(ctx, &capb.GenerateOCSPRequest{
    76  		Serial:   core.SerialToString(ecdsaCert.SerialNumber),
    77  		IssuerID: int64(ecdsaIssuerID),
    78  		Status:   string(core.OCSPStatusGood),
    79  	})
    80  	test.AssertNotError(t, err, "Failed to generate OCSP")
    81  	ecdsaOCSP, err := ocsp.ParseResponse(ecdsaOCSPPB.Response, caCert2.Certificate)
    82  	test.AssertNotError(t, err, "Failed to parse / validate OCSP for ecdsaCert")
    83  	test.AssertEquals(t, ecdsaOCSP.Status, 0)
    84  	test.AssertEquals(t, ecdsaOCSP.RevocationReason, 0)
    85  	test.AssertEquals(t, ecdsaOCSP.SerialNumber.Cmp(ecdsaCert.SerialNumber), 0)
    86  
    87  	// GenerateOCSP with a bad IssuerID should fail.
    88  	_, err = ocspi.GenerateOCSP(context.Background(), &capb.GenerateOCSPRequest{
    89  		Serial:   core.SerialToString(rsaCert.SerialNumber),
    90  		IssuerID: int64(666),
    91  		Status:   string(core.OCSPStatusGood),
    92  	})
    93  	test.AssertError(t, err, "GenerateOCSP didn't fail with invalid IssuerID")
    94  
    95  	// GenerateOCSP with a bad Serial should fail.
    96  	_, err = ocspi.GenerateOCSP(context.Background(), &capb.GenerateOCSPRequest{
    97  		Serial:   "BADDECAF",
    98  		IssuerID: int64(rsaIssuerID),
    99  		Status:   string(core.OCSPStatusGood),
   100  	})
   101  	test.AssertError(t, err, "GenerateOCSP didn't fail with invalid Serial")
   102  
   103  	// GenerateOCSP with a valid-but-nonexistent Serial should *not* fail.
   104  	_, err = ocspi.GenerateOCSP(context.Background(), &capb.GenerateOCSPRequest{
   105  		Serial:   "03DEADBEEFBADDECAFFADEFACECAFE30",
   106  		IssuerID: int64(rsaIssuerID),
   107  		Status:   string(core.OCSPStatusGood),
   108  	})
   109  	test.AssertNotError(t, err, "GenerateOCSP failed with fake-but-valid Serial")
   110  }
   111  
   112  // Set up an ocspLogQueue with a very long period and a large maxLen,
   113  // to ensure any buffered entries get flushed on `.stop()`.
   114  func TestOcspLogFlushOnExit(t *testing.T) {
   115  	t.Parallel()
   116  	log := blog.NewMock()
   117  	stats := metrics.NoopRegisterer
   118  	queue := newOCSPLogQueue(4000, 10000*time.Millisecond, stats, log)
   119  	go queue.loop()
   120  	queue.enqueue(serial(t), time.Now(), ocsp.Good, ocsp.Unspecified)
   121  	queue.stop()
   122  
   123  	expected := []string{
   124  		"INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:_,",
   125  	}
   126  	test.AssertDeepEquals(t, log.GetAll(), expected)
   127  }
   128  
   129  // Ensure log lines are sent when they exceed maxLen.
   130  func TestOcspFlushOnLength(t *testing.T) {
   131  	t.Parallel()
   132  	log := blog.NewMock()
   133  	stats := metrics.NoopRegisterer
   134  	queue := newOCSPLogQueue(100, 100*time.Millisecond, stats, log)
   135  	go queue.loop()
   136  	for i := 0; i < 5; i++ {
   137  		queue.enqueue(serial(t), time.Now(), ocsp.Good, ocsp.Unspecified)
   138  	}
   139  	queue.stop()
   140  
   141  	expected := []string{
   142  		"INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:_,aabbccddeeffaabbccddeeff000102030405:_,",
   143  		"INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:_,aabbccddeeffaabbccddeeff000102030405:_,",
   144  		"INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:_,",
   145  	}
   146  	test.AssertDeepEquals(t, log.GetAll(), expected)
   147  }
   148  
   149  // Ensure log lines are sent after a timeout.
   150  func TestOcspFlushOnTimeout(t *testing.T) {
   151  	t.Parallel()
   152  	log := blog.NewWaitingMock()
   153  	stats := metrics.NoopRegisterer
   154  	queue := newOCSPLogQueue(90000, 10*time.Millisecond, stats, log)
   155  
   156  	go queue.loop()
   157  	queue.enqueue(serial(t), time.Now(), ocsp.Good, ocsp.Unspecified)
   158  
   159  	expected := "INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:_,"
   160  	logLines, err := log.WaitForMatch("OCSP signed", 50*time.Millisecond)
   161  	test.AssertNotError(t, err, "error in mock log")
   162  	test.AssertDeepEquals(t, logLines, expected)
   163  	queue.stop()
   164  }
   165  
   166  // If the deadline passes and nothing has been logged, we should not log a blank line.
   167  func TestOcspNoEmptyLines(t *testing.T) {
   168  	t.Parallel()
   169  	log := blog.NewMock()
   170  	stats := metrics.NoopRegisterer
   171  	queue := newOCSPLogQueue(90000, 10*time.Millisecond, stats, log)
   172  
   173  	go queue.loop()
   174  	time.Sleep(50 * time.Millisecond)
   175  	queue.stop()
   176  
   177  	test.AssertDeepEquals(t, log.GetAll(), []string{})
   178  }
   179  
   180  // If the maxLogLen is shorter than one entry, log everything immediately.
   181  func TestOcspLogWhenMaxLogLenIsShort(t *testing.T) {
   182  	t.Parallel()
   183  	log := blog.NewMock()
   184  	stats := metrics.NoopRegisterer
   185  	queue := newOCSPLogQueue(3, 10000*time.Millisecond, stats, log)
   186  	go queue.loop()
   187  	queue.enqueue(serial(t), time.Now(), ocsp.Good, ocsp.Unspecified)
   188  	queue.stop()
   189  
   190  	expected := []string{
   191  		"INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:_,",
   192  	}
   193  	test.AssertDeepEquals(t, log.GetAll(), expected)
   194  }
   195  
   196  // Enqueueing entries after stop causes panic.
   197  func TestOcspLogPanicsOnEnqueueAfterStop(t *testing.T) {
   198  	t.Parallel()
   199  
   200  	log := blog.NewMock()
   201  	stats := metrics.NoopRegisterer
   202  	queue := newOCSPLogQueue(4000, 10000*time.Millisecond, stats, log)
   203  	go queue.loop()
   204  	queue.stop()
   205  
   206  	defer func() {
   207  		if r := recover(); r == nil {
   208  			t.Errorf("The code did not panic")
   209  		}
   210  	}()
   211  
   212  	queue.enqueue(serial(t), time.Now(), ocsp.Good, ocsp.Unspecified)
   213  }
   214  
   215  // Ensure revoke reason gets set.
   216  func TestOcspRevokeReasonIsSet(t *testing.T) {
   217  	t.Parallel()
   218  	log := blog.NewMock()
   219  	stats := metrics.NoopRegisterer
   220  	queue := newOCSPLogQueue(100, 100*time.Millisecond, stats, log)
   221  	go queue.loop()
   222  
   223  	queue.enqueue(serial(t), time.Now(), ocsp.Revoked, ocsp.KeyCompromise)
   224  	queue.enqueue(serial(t), time.Now(), ocsp.Revoked, ocsp.CACompromise)
   225  	queue.stop()
   226  
   227  	expected := []string{
   228  		"INFO: [AUDIT] OCSP signed: aabbccddeeffaabbccddeeff000102030405:1,aabbccddeeffaabbccddeeff000102030405:2,",
   229  	}
   230  	test.AssertDeepEquals(t, log.GetAll(), expected)
   231  }
   232  

View as plain text