github.com/letsencrypt/boulder@v0.20251208.0/linter/lints/chrome/e_scts_from_same_operator.go (about)

     1  package chrome
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/zmap/zcrypto/x509"
     7  	"github.com/zmap/zcrypto/x509/ct"
     8  	"github.com/zmap/zlint/v3/lint"
     9  	"github.com/zmap/zlint/v3/util"
    10  
    11  	"github.com/letsencrypt/boulder/ctpolicy/loglist"
    12  	"github.com/letsencrypt/boulder/linter/lints"
    13  )
    14  
    15  type sctsFromSameOperator struct {
    16  	logList loglist.List
    17  }
    18  
    19  func init() {
    20  	lint.RegisterCertificateLint(&lint.CertificateLint{
    21  		LintMetadata: lint.LintMetadata{
    22  			Name:          "e_scts_from_same_operator",
    23  			Description:   "Let's Encrypt Subscriber Certificates have two SCTs from logs run by different operators",
    24  			Citation:      "Chrome CT Policy",
    25  			Source:        lints.ChromeCTPolicy,
    26  			EffectiveDate: time.Date(2022, time.April, 15, 0, 0, 0, 0, time.UTC),
    27  		},
    28  		Lint: NewSCTsFromSameOperator,
    29  	})
    30  }
    31  
    32  func NewSCTsFromSameOperator() lint.CertificateLintInterface {
    33  	return &sctsFromSameOperator{logList: loglist.GetLintList()}
    34  }
    35  
    36  func (l *sctsFromSameOperator) CheckApplies(c *x509.Certificate) bool {
    37  	return util.IsSubscriberCert(c) && !util.IsExtInCert(c, util.CtPoisonOID)
    38  }
    39  
    40  func (l *sctsFromSameOperator) Execute(c *x509.Certificate) *lint.LintResult {
    41  	if len(l.logList) == 0 {
    42  		return &lint.LintResult{
    43  			Status:  lint.NE,
    44  			Details: "Failed to load log list, unable to check Certificate SCTs.",
    45  		}
    46  	}
    47  
    48  	if len(c.SignedCertificateTimestampList) < 2 {
    49  		return &lint.LintResult{
    50  			Status:  lint.Error,
    51  			Details: "Certificate had too few embedded SCTs; browser policy requires 2.",
    52  		}
    53  	}
    54  
    55  	logIDs := make(map[ct.SHA256Hash]struct{})
    56  	for _, sct := range c.SignedCertificateTimestampList {
    57  		logIDs[sct.LogID] = struct{}{}
    58  	}
    59  
    60  	if len(logIDs) < 2 {
    61  		return &lint.LintResult{
    62  			Status:  lint.Error,
    63  			Details: "Certificate SCTs from too few distinct logs; browser policy requires 2.",
    64  		}
    65  	}
    66  
    67  	rfc6962Compliant := false
    68  	operatorNames := make(map[string]struct{})
    69  	for logID := range logIDs {
    70  		log, err := l.logList.GetByID(logID.Base64String())
    71  		if err != nil {
    72  			// This certificate *may* have more than 2 SCTs, so missing one now isn't
    73  			// a problem.
    74  			continue
    75  		}
    76  		if !log.Tiled {
    77  			rfc6962Compliant = true
    78  		}
    79  		operatorNames[log.Operator] = struct{}{}
    80  	}
    81  
    82  	if !rfc6962Compliant {
    83  		return &lint.LintResult{
    84  			Status:  lint.Error,
    85  			Details: "At least one certificate SCT must be from an RFC6962-compliant log.",
    86  		}
    87  	}
    88  
    89  	if len(operatorNames) < 2 {
    90  		return &lint.LintResult{
    91  			Status:  lint.Error,
    92  			Details: "Certificate SCTs from too few distinct log operators; browser policy requires 2.",
    93  		}
    94  	}
    95  
    96  	return &lint.LintResult{
    97  		Status: lint.Pass,
    98  	}
    99  }