1 package in_toto
2
3 import (
4 "crypto/x509"
5 "fmt"
6 "net/url"
7 )
8
9 const (
10 AllowAllConstraint = "*"
11 )
12
13
14
15
16
17 type CertificateConstraint struct {
18 CommonName string `json:"common_name"`
19 DNSNames []string `json:"dns_names"`
20 Emails []string `json:"emails"`
21 Organizations []string `json:"organizations"`
22 Roots []string `json:"roots"`
23 URIs []string `json:"uris"`
24 }
25
26
27
28 type checkResult struct {
29 errors []error
30 }
31
32
33 func newCheckResult() *checkResult {
34 return &checkResult{
35 errors: make([]error, 0),
36 }
37 }
38
39
40 func (cr *checkResult) evaluate(cert *x509.Certificate, constraintCheck func(*x509.Certificate) error) *checkResult {
41 err := constraintCheck(cert)
42 if err != nil {
43 cr.errors = append(cr.errors, err)
44 }
45 return cr
46 }
47
48
49
50
51 func (cr *checkResult) error() error {
52 if len(cr.errors) == 0 {
53 return nil
54 }
55 return fmt.Errorf("cert failed constraints check: %+q", cr.errors)
56 }
57
58
59
60 func (cc CertificateConstraint) Check(cert *x509.Certificate, rootCAIDs []string, rootCertPool, intermediateCertPool *x509.CertPool) error {
61 return newCheckResult().
62 evaluate(cert, cc.checkCommonName).
63 evaluate(cert, cc.checkDNSNames).
64 evaluate(cert, cc.checkEmails).
65 evaluate(cert, cc.checkOrganizations).
66 evaluate(cert, cc.checkRoots(rootCAIDs, rootCertPool, intermediateCertPool)).
67 evaluate(cert, cc.checkURIs).
68 error()
69 }
70
71
72 func (cc CertificateConstraint) checkCommonName(cert *x509.Certificate) error {
73 return checkCertConstraint("common name", []string{cc.CommonName}, []string{cert.Subject.CommonName})
74 }
75
76
77 func (cc CertificateConstraint) checkDNSNames(cert *x509.Certificate) error {
78 return checkCertConstraint("dns name", cc.DNSNames, cert.DNSNames)
79 }
80
81
82 func (cc CertificateConstraint) checkEmails(cert *x509.Certificate) error {
83 return checkCertConstraint("email", cc.Emails, cert.EmailAddresses)
84 }
85
86
87 func (cc CertificateConstraint) checkOrganizations(cert *x509.Certificate) error {
88 return checkCertConstraint("organization", cc.Organizations, cert.Subject.Organization)
89 }
90
91
92
93 func (cc CertificateConstraint) checkRoots(rootCAIDs []string, rootCertPool, intermediateCertPool *x509.CertPool) func(*x509.Certificate) error {
94 return func(cert *x509.Certificate) error {
95 _, err := VerifyCertificateTrust(cert, rootCertPool, intermediateCertPool)
96 if err != nil {
97 return fmt.Errorf("failed to verify roots: %w", err)
98 }
99 return checkCertConstraint("root", cc.Roots, rootCAIDs)
100 }
101 }
102
103
104 func (cc CertificateConstraint) checkURIs(cert *x509.Certificate) error {
105 return checkCertConstraint("uri", cc.URIs, urisToStrings(cert.URIs))
106 }
107
108
109 func urisToStrings(uris []*url.URL) []string {
110 res := make([]string, 0, len(uris))
111 for _, uri := range uris {
112 res = append(res, uri.String())
113 }
114
115 return res
116 }
117
118
119
120 func checkCertConstraint(attributeName string, constraints, values []string) error {
121
122 if len(constraints) == 1 && constraints[0] == AllowAllConstraint {
123 return nil
124 }
125
126 if len(constraints) == 1 && constraints[0] == "" {
127 constraints = []string{}
128 }
129
130 if len(values) == 1 && values[0] == "" {
131 values = []string{}
132 }
133
134
135 if len(constraints) == 0 && len(values) > 0 {
136 return fmt.Errorf("not expecting any %s(s), but cert has %d %s(s)", attributeName, len(values), attributeName)
137 }
138
139 unmet := NewSet(constraints...)
140 for _, v := range values {
141
142 if !unmet.Has(v) {
143 return fmt.Errorf("cert has an unexpected %s %s given constraints %+q", attributeName, v, constraints)
144 }
145
146
147 unmet.Remove(v)
148 }
149
150
151 if len(unmet) > 0 {
152 return fmt.Errorf("cert with %s(s) %+q did not pass all constraints %+q", attributeName, values, constraints)
153 }
154
155 return nil
156 }
157
View as plain text