@@ -6,35 +6,87 @@ package x509
6
6
7
7
import (
8
8
"bytes"
9
+ "crypto/sha256"
9
10
"encoding/pem"
10
11
"errors"
11
12
"runtime"
12
13
)
13
14
15
+ type sum224 [sha256 .Size224 ]byte
16
+
14
17
// CertPool is a set of certificates.
15
18
type CertPool struct {
16
- byName map [string ][]int
17
- certs []* Certificate
19
+ byName map [string ][]int // cert.RawSubject => index into lazyCerts
20
+
21
+ // lazyCerts contains funcs that return a certificate,
22
+ // lazily parsing/decompressing it as needed.
23
+ lazyCerts []lazyCert
24
+
25
+ // haveSum maps from sum224(cert.Raw) to true. It's used only
26
+ // for AddCert duplicate detection, to avoid CertPool.contains
27
+ // calls in the AddCert path (because the contains method can
28
+ // call getCert and otherwise negate savings from lazy getCert
29
+ // funcs).
30
+ haveSum map [sum224 ]bool
31
+ }
32
+
33
+ // lazyCert is minimal metadata about a Cert and a func to retrieve it
34
+ // in its normal expanded *Certificate form.
35
+ type lazyCert struct {
36
+ // rawSubject is the Certificate.RawSubject value.
37
+ // It's the same as the CertPool.byName key, but in []byte
38
+ // form to make CertPool.Subjects (as used by crypto/tls) do
39
+ // fewer allocations.
40
+ rawSubject []byte
41
+
42
+ // getCert returns the certificate.
43
+ //
44
+ // It is not meant to do network operations or anything else
45
+ // where a failure is likely; the func is meant to lazily
46
+ // parse/decompress data that is already known to be good. The
47
+ // error in the signature primarily is meant for use in the
48
+ // case where a cert file existed on local disk when the program
49
+ // started up is deleted later before it's read.
50
+ getCert func () (* Certificate , error )
18
51
}
19
52
20
53
// NewCertPool returns a new, empty CertPool.
21
54
func NewCertPool () * CertPool {
22
55
return & CertPool {
23
- byName : make (map [string ][]int ),
56
+ byName : make (map [string ][]int ),
57
+ haveSum : make (map [sum224 ]bool ),
24
58
}
25
59
}
26
60
61
+ // len returns the number of certs in the set.
62
+ // A nil set is a valid empty set.
63
+ func (s * CertPool ) len () int {
64
+ if s == nil {
65
+ return 0
66
+ }
67
+ return len (s .lazyCerts )
68
+ }
69
+
70
+ // cert returns cert index n in s.
71
+ func (s * CertPool ) cert (n int ) (* Certificate , error ) {
72
+ return s .lazyCerts [n ].getCert ()
73
+ }
74
+
27
75
func (s * CertPool ) copy () * CertPool {
28
76
p := & CertPool {
29
- byName : make (map [string ][]int , len (s .byName )),
30
- certs : make ([]* Certificate , len (s .certs )),
77
+ byName : make (map [string ][]int , len (s .byName )),
78
+ lazyCerts : make ([]lazyCert , len (s .lazyCerts )),
79
+ haveSum : make (map [sum224 ]bool , len (s .haveSum )),
31
80
}
32
81
for k , v := range s .byName {
33
82
indexes := make ([]int , len (v ))
34
83
copy (indexes , v )
35
84
p .byName [k ] = indexes
36
85
}
37
- copy (p .certs , s .certs )
86
+ for k := range s .haveSum {
87
+ p .haveSum [k ] = true
88
+ }
89
+ copy (p .lazyCerts , s .lazyCerts )
38
90
return p
39
91
}
40
92
@@ -64,7 +116,7 @@ func SystemCertPool() (*CertPool, error) {
64
116
65
117
// findPotentialParents returns the indexes of certificates in s which might
66
118
// have signed cert.
67
- func (s * CertPool ) findPotentialParents (cert * Certificate ) []int {
119
+ func (s * CertPool ) findPotentialParents (cert * Certificate ) []* Certificate {
68
120
if s == nil {
69
121
return nil
70
122
}
@@ -75,41 +127,46 @@ func (s *CertPool) findPotentialParents(cert *Certificate) []int {
75
127
// AKID and SKID match
76
128
// AKID present, SKID missing / AKID missing, SKID present
77
129
// AKID and SKID don't match
78
- var matchingKeyID , oneKeyID , mismatchKeyID []int
130
+ var matchingKeyID , oneKeyID , mismatchKeyID []* Certificate
79
131
for _ , c := range s .byName [string (cert .RawIssuer )] {
80
- candidate := s .certs [c ]
132
+ candidate , err := s .cert (c )
133
+ if err != nil {
134
+ continue
135
+ }
81
136
kidMatch := bytes .Equal (candidate .SubjectKeyId , cert .AuthorityKeyId )
82
137
switch {
83
138
case kidMatch :
84
- matchingKeyID = append (matchingKeyID , c )
139
+ matchingKeyID = append (matchingKeyID , candidate )
85
140
case (len (candidate .SubjectKeyId ) == 0 && len (cert .AuthorityKeyId ) > 0 ) ||
86
141
(len (candidate .SubjectKeyId ) > 0 && len (cert .AuthorityKeyId ) == 0 ):
87
- oneKeyID = append (oneKeyID , c )
142
+ oneKeyID = append (oneKeyID , candidate )
88
143
default :
89
- mismatchKeyID = append (mismatchKeyID , c )
144
+ mismatchKeyID = append (mismatchKeyID , candidate )
90
145
}
91
146
}
92
147
93
148
found := len (matchingKeyID ) + len (oneKeyID ) + len (mismatchKeyID )
94
149
if found == 0 {
95
150
return nil
96
151
}
97
- candidates := make ([]int , 0 , found )
152
+ candidates := make ([]* Certificate , 0 , found )
98
153
candidates = append (candidates , matchingKeyID ... )
99
154
candidates = append (candidates , oneKeyID ... )
100
155
candidates = append (candidates , mismatchKeyID ... )
101
-
102
156
return candidates
103
157
}
104
158
105
159
func (s * CertPool ) contains (cert * Certificate ) bool {
106
160
if s == nil {
107
161
return false
108
162
}
109
-
110
163
candidates := s .byName [string (cert .RawSubject )]
111
- for _ , c := range candidates {
112
- if s .certs [c ].Equal (cert ) {
164
+ for _ , i := range candidates {
165
+ c , err := s .cert (i )
166
+ if err != nil {
167
+ return false
168
+ }
169
+ if c .Equal (cert ) {
113
170
return true
114
171
}
115
172
}
@@ -122,17 +179,32 @@ func (s *CertPool) AddCert(cert *Certificate) {
122
179
if cert == nil {
123
180
panic ("adding nil Certificate to CertPool" )
124
181
}
182
+ s .addCertFunc (sha256 .Sum224 (cert .Raw ), string (cert .RawSubject ), func () (* Certificate , error ) {
183
+ return cert , nil
184
+ })
185
+ }
186
+
187
+ // addCertFunc adds metadata about a certificate to a pool, along with
188
+ // a func to fetch that certificate later when needed.
189
+ //
190
+ // The rawSubject is Certificate.RawSubject and must be non-empty.
191
+ // The getCert func may be called 0 or more times.
192
+ func (s * CertPool ) addCertFunc (rawSum224 sum224 , rawSubject string , getCert func () (* Certificate , error )) {
193
+ if getCert == nil {
194
+ panic ("getCert can't be nil" )
195
+ }
125
196
126
197
// Check that the certificate isn't being added twice.
127
- if s .contains ( cert ) {
198
+ if s .haveSum [ rawSum224 ] {
128
199
return
129
200
}
130
201
131
- n := len (s .certs )
132
- s .certs = append (s .certs , cert )
133
-
134
- name := string (cert .RawSubject )
135
- s .byName [name ] = append (s .byName [name ], n )
202
+ s .haveSum [rawSum224 ] = true
203
+ s .lazyCerts = append (s .lazyCerts , lazyCert {
204
+ rawSubject : []byte (rawSubject ),
205
+ getCert : getCert ,
206
+ })
207
+ s .byName [rawSubject ] = append (s .byName [rawSubject ], len (s .lazyCerts )- 1 )
136
208
}
137
209
138
210
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
@@ -167,9 +239,9 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
167
239
// Subjects returns a list of the DER-encoded subjects of
168
240
// all of the certificates in the pool.
169
241
func (s * CertPool ) Subjects () [][]byte {
170
- res := make ([][]byte , len ( s . certs ))
171
- for i , c := range s .certs {
172
- res [i ] = c . RawSubject
242
+ res := make ([][]byte , s . len ( ))
243
+ for i , lc := range s .lazyCerts {
244
+ res [i ] = lc . rawSubject
173
245
}
174
246
return res
175
247
}
0 commit comments