github.com/letsencrypt/boulder@v0.20251208.0/crl/checker/checker.go (about) 1 package checker 2 3 import ( 4 "bytes" 5 "crypto/x509" 6 "fmt" 7 "math/big" 8 "sort" 9 "time" 10 11 zlint_x509 "github.com/zmap/zcrypto/x509" 12 "github.com/zmap/zlint/v3" 13 14 "github.com/letsencrypt/boulder/linter" 15 ) 16 17 // Validate runs the given CRL through our set of lints, ensures its signature 18 // validates (if supplied with a non-nil issuer), and checks that the CRL is 19 // less than ageLimit old. It returns an error if any of these conditions are 20 // not met. 21 func Validate(crl *x509.RevocationList, issuer *x509.Certificate, ageLimit time.Duration) error { 22 zcrl, err := zlint_x509.ParseRevocationList(crl.Raw) 23 if err != nil { 24 return fmt.Errorf("parsing CRL: %w", err) 25 } 26 27 err = linter.ProcessResultSet(zlint.LintRevocationList(zcrl)) 28 if err != nil { 29 return fmt.Errorf("linting CRL: %w", err) 30 } 31 32 if issuer != nil { 33 err = crl.CheckSignatureFrom(issuer) 34 if err != nil { 35 return fmt.Errorf("checking CRL signature: %w", err) 36 } 37 } 38 39 if time.Since(crl.ThisUpdate) >= ageLimit { 40 return fmt.Errorf("thisUpdate more than %s in the past: %v", ageLimit, crl.ThisUpdate) 41 } 42 43 return nil 44 } 45 46 type diffResult struct { 47 Added []*big.Int 48 Removed []*big.Int 49 // TODO: consider adding a "changed" field, for entries whose revocation time 50 // or revocation reason changes. 51 } 52 53 // Diff returns the sets of serials that were added and removed between two 54 // CRLs. In order to be comparable, the CRLs must come from the same issuer, and 55 // be given in the correct order (the "old" CRL's Number and ThisUpdate must 56 // both precede the "new" CRL's). 57 func Diff(old, new *x509.RevocationList) (*diffResult, error) { 58 if !bytes.Equal(old.AuthorityKeyId, new.AuthorityKeyId) { 59 return nil, fmt.Errorf("CRLs were not issued by same issuer") 60 } 61 62 if old.Number.Cmp(new.Number) >= 0 { 63 return nil, fmt.Errorf("old CRL does not precede new CRL") 64 } 65 66 if new.ThisUpdate.Before(old.ThisUpdate) { 67 return nil, fmt.Errorf("old CRL does not precede new CRL") 68 } 69 70 // Sort both sets of serials so we can march through them in order. 71 oldSerials := make([]*big.Int, len(old.RevokedCertificateEntries)) 72 for i, rc := range old.RevokedCertificateEntries { 73 oldSerials[i] = rc.SerialNumber 74 } 75 sort.Slice(oldSerials, func(i, j int) bool { 76 return oldSerials[i].Cmp(oldSerials[j]) < 0 77 }) 78 79 newSerials := make([]*big.Int, len(new.RevokedCertificateEntries)) 80 for j, rc := range new.RevokedCertificateEntries { 81 newSerials[j] = rc.SerialNumber 82 } 83 sort.Slice(newSerials, func(i, j int) bool { 84 return newSerials[i].Cmp(newSerials[j]) < 0 85 }) 86 87 // Work our way through both lists of sorted serials. If the old list skips 88 // past a serial seen in the new list, then that serial was added. If the new 89 // list skips past a serial seen in the old list, then it was removed. 90 i, j := 0, 0 91 added := make([]*big.Int, 0) 92 removed := make([]*big.Int, 0) 93 for { 94 if i >= len(oldSerials) { 95 added = append(added, newSerials[j:]...) 96 break 97 } 98 if j >= len(newSerials) { 99 removed = append(removed, oldSerials[i:]...) 100 break 101 } 102 cmp := oldSerials[i].Cmp(newSerials[j]) 103 if cmp < 0 { 104 removed = append(removed, oldSerials[i]) 105 i++ 106 } else if cmp > 0 { 107 added = append(added, newSerials[j]) 108 j++ 109 } else { 110 i++ 111 j++ 112 } 113 } 114 115 return &diffResult{added, removed}, nil 116 }