github.com/letsencrypt/boulder@v0.20251208.0/cmd/ceremony/crl_test.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/ecdsa"
     6  	"crypto/elliptic"
     7  	"crypto/rand"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/asn1"
    11  	"encoding/pem"
    12  	"io"
    13  	"math/big"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/letsencrypt/boulder/test"
    18  )
    19  
    20  func TestGenerateCRLTimeBounds(t *testing.T) {
    21  	_, err := generateCRL(nil, nil, time.Now().Add(time.Hour), time.Now(), 1, nil, []string{})
    22  	test.AssertError(t, err, "generateCRL did not fail")
    23  	test.AssertEquals(t, err.Error(), "thisUpdate must be before nextUpdate")
    24  
    25  	_, err = generateCRL(nil, &x509.Certificate{
    26  		NotBefore: time.Now().Add(time.Hour),
    27  		NotAfter:  time.Now(),
    28  	}, time.Now(), time.Now(), 1, nil, []string{})
    29  	test.AssertError(t, err, "generateCRL did not fail")
    30  	test.AssertEquals(t, err.Error(), "thisUpdate is before issuing certificate's notBefore")
    31  
    32  	_, err = generateCRL(nil, &x509.Certificate{
    33  		NotBefore: time.Now(),
    34  		NotAfter:  time.Now().Add(time.Hour * 2),
    35  	}, time.Now().Add(time.Hour), time.Now().Add(time.Hour*3), 1, nil, []string{})
    36  	test.AssertError(t, err, "generateCRL did not fail")
    37  	test.AssertEquals(t, err.Error(), "nextUpdate is after issuing certificate's notAfter")
    38  
    39  	_, err = generateCRL(nil, &x509.Certificate{
    40  		NotBefore: time.Now(),
    41  		NotAfter:  time.Now().Add(time.Hour * 24 * 370),
    42  	}, time.Now(), time.Now().Add(time.Hour*24*366), 1, nil, []string{})
    43  	test.AssertError(t, err, "generateCRL did not fail")
    44  	test.AssertEquals(t, err.Error(), "nextUpdate must be less than 12 months after thisUpdate")
    45  }
    46  
    47  // wrappedSigner wraps a crypto.Signer. In order to use a crypto.Signer in tests
    48  // we need to wrap it as we pass a purposefully broken io.Reader to Sign in order
    49  // to verify that go isn't using it as a source of randomness (we expect this
    50  // randomness to come from the HSM). If we directly call Sign on the crypto.Signer
    51  // it would fail, so we wrap it so that we can use a shim rand.Reader in the Sign
    52  // call.
    53  type wrappedSigner struct{ k crypto.Signer }
    54  
    55  func (p wrappedSigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
    56  	return p.k.Sign(rand.Reader, digest, opts)
    57  }
    58  
    59  func (p wrappedSigner) Public() crypto.PublicKey {
    60  	return p.k.Public()
    61  }
    62  
    63  func TestGenerateCRLLints(t *testing.T) {
    64  	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    65  	test.AssertNotError(t, err, "failed to generate test key")
    66  
    67  	cert := &x509.Certificate{
    68  		Subject:      pkix.Name{CommonName: "asd"},
    69  		SerialNumber: big.NewInt(7),
    70  		NotBefore:    time.Now(),
    71  		NotAfter:     time.Now().Add(365 * 24 * time.Hour),
    72  		IsCA:         true,
    73  		KeyUsage:     x509.KeyUsageCRLSign,
    74  		SubjectKeyId: []byte{1, 2, 3},
    75  	}
    76  
    77  	certBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, k.Public(), k)
    78  	test.AssertNotError(t, err, "failed to generate test cert")
    79  	cert, err = x509.ParseCertificate(certBytes)
    80  	test.AssertNotError(t, err, "failed to parse test cert")
    81  
    82  	// This CRL should fail the "e_crl_next_update_invalid" lint because the
    83  	// validity interval is more than 10 days, and this lint can't tell the
    84  	// difference between end-entity and CA CRLs.
    85  	_, err = generateCRL(&wrappedSigner{k}, cert, time.Now().Add(time.Hour), time.Now().Add(100*24*time.Hour), 1, []x509.RevocationListEntry{
    86  		{
    87  			SerialNumber:   big.NewInt(12345),
    88  			RevocationTime: time.Now().Add(time.Hour),
    89  		},
    90  	}, []string{})
    91  	test.AssertError(t, err, "generateCRL did not fail")
    92  	test.AssertContains(t, err.Error(), "e_crl_next_update_invalid")
    93  
    94  	// But we can tell it to ignore that lint, too.
    95  	_, err = generateCRL(&wrappedSigner{k}, cert, time.Now().Add(time.Hour), time.Now().Add(100*24*time.Hour), 1, []x509.RevocationListEntry{
    96  		{
    97  			SerialNumber:   big.NewInt(12345),
    98  			RevocationTime: time.Now().Add(time.Hour),
    99  		},
   100  	}, []string{"e_crl_next_update_invalid"})
   101  	test.AssertNotError(t, err, "generateCRL should have ignored the failing lint")
   102  }
   103  
   104  func TestGenerateCRL(t *testing.T) {
   105  	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   106  	test.AssertNotError(t, err, "failed to generate test key")
   107  
   108  	template := &x509.Certificate{
   109  		Subject:               pkix.Name{CommonName: "asd"},
   110  		SerialNumber:          big.NewInt(7),
   111  		NotBefore:             time.Now(),
   112  		NotAfter:              time.Now().Add(365 * 24 * time.Hour),
   113  		IsCA:                  true,
   114  		BasicConstraintsValid: true,
   115  		KeyUsage:              x509.KeyUsageCRLSign,
   116  		SubjectKeyId:          []byte{1, 2, 3},
   117  	}
   118  
   119  	certBytes, err := x509.CreateCertificate(rand.Reader, template, template, k.Public(), k)
   120  	test.AssertNotError(t, err, "failed to generate test cert")
   121  	cert, err := x509.ParseCertificate(certBytes)
   122  	test.AssertNotError(t, err, "failed to parse test cert")
   123  
   124  	crlPEM, err := generateCRL(&wrappedSigner{k}, cert, time.Now().Add(time.Hour), time.Now().Add(time.Hour*2), 1, nil, []string{})
   125  	test.AssertNotError(t, err, "generateCRL failed with valid profile")
   126  
   127  	pemBlock, _ := pem.Decode(crlPEM)
   128  	crlDER := pemBlock.Bytes
   129  
   130  	// use crypto/x509 to check signature is valid and list is empty
   131  	goCRL, err := x509.ParseRevocationList(crlDER)
   132  	test.AssertNotError(t, err, "failed to parse CRL")
   133  	err = goCRL.CheckSignatureFrom(cert)
   134  	test.AssertNotError(t, err, "CRL signature check failed")
   135  	test.AssertEquals(t, len(goCRL.RevokedCertificateEntries), 0)
   136  
   137  	// fully parse the CRL to check that the version is correct, and that
   138  	// it contains the CRL number extension containing the number we expect
   139  	var crl asn1CRL
   140  	_, err = asn1.Unmarshal(crlDER, &crl)
   141  	test.AssertNotError(t, err, "failed to parse CRL")
   142  	test.AssertEquals(t, crl.TBS.Version, 1)         // x509v2 == 1
   143  	test.AssertEquals(t, len(crl.TBS.Extensions), 3) // AKID, CRL number, IssuingDistributionPoint
   144  	test.Assert(t, crl.TBS.Extensions[1].Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 20}), "unexpected OID in extension")
   145  	test.Assert(t, crl.TBS.Extensions[2].Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 28}), "unexpected OID in extension")
   146  	var number int
   147  	_, err = asn1.Unmarshal(crl.TBS.Extensions[1].Value, &number)
   148  	test.AssertNotError(t, err, "failed to parse CRL number extension")
   149  	test.AssertEquals(t, number, 1)
   150  }
   151  
   152  type asn1CRL struct {
   153  	TBS struct {
   154  		Version int `asn1:"optional"`
   155  		SigAlg  pkix.AlgorithmIdentifier
   156  		Issuer  struct {
   157  			Raw asn1.RawContent
   158  		}
   159  		ThisUpdate          time.Time
   160  		NextUpdate          time.Time `asn1:"optional"`
   161  		RevokedCertificates []struct {
   162  			Serial     *big.Int
   163  			RevokedAt  time.Time
   164  			Extensions []pkix.Extension `asn1:"optional"`
   165  		} `asn1:"optional"`
   166  		Extensions []pkix.Extension `asn1:"optional,explicit,tag:0"`
   167  	}
   168  	SigAlg pkix.AlgorithmIdentifier
   169  	Sig    asn1.BitString
   170  }