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 }