...

Source file src/github.com/letsencrypt/boulder/cmd/ceremony/crl.go

Documentation: github.com/letsencrypt/boulder/cmd/ceremony

     1  package main
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/x509"
     6  	"crypto/x509/pkix"
     7  	"encoding/asn1"
     8  	"encoding/pem"
     9  	"errors"
    10  	"fmt"
    11  	"math/big"
    12  	"time"
    13  
    14  	"github.com/letsencrypt/boulder/linter"
    15  )
    16  
    17  func generateCRL(signer crypto.Signer, issuer *x509.Certificate, thisUpdate, nextUpdate time.Time, number int64, revokedCertificates []x509.RevocationListEntry) ([]byte, error) {
    18  	template := &x509.RevocationList{
    19  		RevokedCertificateEntries: revokedCertificates,
    20  		Number:                    big.NewInt(number),
    21  		ThisUpdate:                thisUpdate,
    22  		NextUpdate:                nextUpdate,
    23  	}
    24  
    25  	if nextUpdate.Before(thisUpdate) {
    26  		return nil, errors.New("thisUpdate must be before nextUpdate")
    27  	}
    28  	if thisUpdate.Before(issuer.NotBefore) {
    29  		return nil, errors.New("thisUpdate is before issuing certificate's notBefore")
    30  	} else if nextUpdate.After(issuer.NotAfter) {
    31  		return nil, errors.New("nextUpdate is after issuing certificate's notAfter")
    32  	}
    33  
    34  	// Verify that the CRL is not valid for more than 12 months as specified in
    35  	// CABF BRs Section 4.9.7
    36  	if nextUpdate.Sub(thisUpdate) > time.Hour*24*365 {
    37  		return nil, errors.New("nextUpdate must be less than 12 months after thisUpdate")
    38  	}
    39  	// Add the Issuing Distribution Point extension.
    40  	idp, err := makeIDPExt()
    41  	if err != nil {
    42  		return nil, fmt.Errorf("creating IDP extension: %w", err)
    43  	}
    44  	template.ExtraExtensions = append(template.ExtraExtensions, *idp)
    45  
    46  	err = linter.CheckCRL(template, issuer, signer, []string{})
    47  	if err != nil {
    48  		return nil, fmt.Errorf("crl failed pre-issuance lint: %w", err)
    49  	}
    50  
    51  	// x509.CreateRevocationList uses an io.Reader here for signing methods that require
    52  	// a source of randomness. Since PKCS#11 based signing generates needed randomness
    53  	// at the HSM we don't need to pass a real reader. Instead of passing a nil reader
    54  	// we use one that always returns errors in case the internal usage of this reader
    55  	// changes.
    56  	crlBytes, err := x509.CreateRevocationList(&failReader{}, template, issuer, signer)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	return pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: crlBytes}), nil
    62  }
    63  
    64  // issuingDistributionPoint represents the ASN.1 IssuingDistributionPoint
    65  // SEQUENCE as defined in RFC 5280 Section 5.2.5. We only use one of the fields,
    66  // all others are omitted.
    67  // https://datatracker.ietf.org/doc/html/rfc5280#page-66
    68  type issuingDistributionPoint struct {
    69  	OnlyContainsCACerts bool `asn1:"optional,tag:2"`
    70  }
    71  
    72  // makeIDPExt returns a critical IssuingDistributionPoint extension enabling the
    73  // OnlyContainsCACerts boolean.
    74  func makeIDPExt() (*pkix.Extension, error) {
    75  	val := issuingDistributionPoint{
    76  		OnlyContainsCACerts: true,
    77  	}
    78  
    79  	valBytes, err := asn1.Marshal(val)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return &pkix.Extension{
    85  		Id:       asn1.ObjectIdentifier{2, 5, 29, 28}, // id-ce-issuingDistributionPoint
    86  		Value:    valBytes,
    87  		Critical: true,
    88  	}, nil
    89  }
    90  

View as plain text