1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ctfe
16
17 import (
18 "bytes"
19 "errors"
20 "fmt"
21 "time"
22
23 "github.com/google/certificate-transparency-go/asn1"
24 "github.com/google/certificate-transparency-go/x509"
25 "github.com/google/certificate-transparency-go/x509util"
26 )
27
28
29
30
31 func IsPrecertificate(cert *x509.Certificate) (bool, error) {
32 for _, ext := range cert.Extensions {
33 if x509.OIDExtensionCTPoison.Equal(ext.Id) {
34 if !ext.Critical || !bytes.Equal(asn1.NullBytes, ext.Value) {
35 return false, fmt.Errorf("CT poison ext is not critical or invalid: %v", ext)
36 }
37
38 return true, nil
39 }
40 }
41
42 return false, nil
43 }
44
45
46
47
48
49
50 func ValidateChain(rawChain [][]byte, validationOpts CertValidationOpts) ([]*x509.Certificate, error) {
51
52 chain := make([]*x509.Certificate, 0, len(rawChain))
53 intermediatePool := x509util.NewPEMCertPool()
54
55 for i, certBytes := range rawChain {
56 cert, err := x509.ParseCertificate(certBytes)
57 if x509.IsFatal(err) {
58 return nil, err
59 }
60
61 chain = append(chain, cert)
62
63
64 if i > 0 {
65 intermediatePool.AddCert(cert)
66 }
67 }
68
69 naStart := validationOpts.notAfterStart
70 naLimit := validationOpts.notAfterLimit
71 cert := chain[0]
72
73
74 if naStart != nil && cert.NotAfter.Before(*naStart) {
75 return nil, fmt.Errorf("certificate NotAfter (%v) < %v", cert.NotAfter, *naStart)
76 }
77 if naLimit != nil && !cert.NotAfter.Before(*naLimit) {
78 return nil, fmt.Errorf("certificate NotAfter (%v) >= %v", cert.NotAfter, *naLimit)
79 }
80
81 if validationOpts.acceptOnlyCA && !cert.IsCA {
82 return nil, errors.New("only certificates with CA bit set are accepted")
83 }
84
85 now := validationOpts.currentTime
86 if now.IsZero() {
87 now = time.Now()
88 }
89 expired := now.After(cert.NotAfter)
90 if validationOpts.rejectExpired && expired {
91 return nil, errors.New("rejecting expired certificate")
92 }
93 if validationOpts.rejectUnexpired && !expired {
94 return nil, errors.New("rejecting unexpired certificate")
95 }
96
97
98
99
100 if len(validationOpts.rejectExtIds) != 0 {
101 badIDs := make(map[string]bool)
102 for _, id := range validationOpts.rejectExtIds {
103 badIDs[id.String()] = true
104 }
105 for idx, ext := range cert.Extensions {
106 extOid := ext.Id.String()
107 if _, ok := badIDs[extOid]; ok {
108 return nil, fmt.Errorf("rejecting certificate containing extension %v at index %d", extOid, idx)
109 }
110 }
111 }
112
113
114
115 if len(validationOpts.extKeyUsages) > 0 {
116 acceptEKUs := make(map[x509.ExtKeyUsage]bool)
117 for _, eku := range validationOpts.extKeyUsages {
118 acceptEKUs[eku] = true
119 }
120 good := false
121 for _, certEKU := range cert.ExtKeyUsage {
122 if _, ok := acceptEKUs[certEKU]; ok {
123 good = true
124 break
125 }
126 }
127 if !good {
128 return nil, fmt.Errorf("rejecting certificate without EKU in %v", validationOpts.extKeyUsages)
129 }
130 }
131
132
133
134 verifyOpts := x509.VerifyOptions{
135 Roots: validationOpts.trustedRoots.CertPool(),
136 CurrentTime: now,
137 Intermediates: intermediatePool.CertPool(),
138 DisableTimeChecks: true,
139
140
141
142 DisableCriticalExtensionChecks: true,
143
144
145
146
147
148 DisableEKUChecks: true,
149
150
151 DisablePathLenChecks: true,
152 DisableNameConstraintChecks: true,
153 DisableNameChecks: false,
154 KeyUsages: validationOpts.extKeyUsages,
155 }
156
157 verifiedChains, err := cert.Verify(verifyOpts)
158 if err != nil {
159 return nil, err
160 }
161
162 if len(verifiedChains) == 0 {
163 return nil, errors.New("no path to root found when trying to validate chains")
164 }
165
166
167
168
169 for _, verifiedChain := range verifiedChains {
170 if chainsEquivalent(chain, verifiedChain) {
171 return verifiedChain, nil
172 }
173 }
174
175 return nil, errors.New("no RFC compliant path to root found when trying to validate chain")
176 }
177
178 func chainsEquivalent(inChain []*x509.Certificate, verifiedChain []*x509.Certificate) bool {
179
180
181
182 if len(inChain) != len(verifiedChain) && len(inChain) != (len(verifiedChain)-1) {
183 return false
184 }
185
186 for i, certInChain := range inChain {
187 if !certInChain.Equal(verifiedChain[i]) {
188 return false
189 }
190 }
191 return true
192 }
193
View as plain text