5
5
package x509
6
6
7
7
import (
8
+ "crypto/sha256"
8
9
"encoding/pem"
9
10
"errors"
10
11
"runtime"
12
+ "sync"
11
13
)
12
14
15
+ type sum224 [sha256 .Size224 ]byte
16
+
13
17
// CertPool is a set of certificates.
14
18
type CertPool struct {
15
- bySubjectKeyId map [string ][]int
16
- byName map [string ][]int
17
- certs []* Certificate
19
+ bySubjectKeyId map [string ][]int // cert.SubjectKeyId => getCert index(es)
20
+ byName map [string ][]int // cert.RawSubject => getCert index(es)
21
+
22
+ // haveSum maps from sum224(cert.Raw) to true. It's used only
23
+ // for AddCert duplicate detection, to avoid CertPool.contains
24
+ // calls in the AddCert path (because the contains method can
25
+ // call getCert and otherwise negate savings from lazy getCert
26
+ // funcs).
27
+ haveSum map [sum224 ]bool
28
+
29
+ // getCert contains funcs that return the certificates.
30
+ getCert []func () (* Certificate , error )
31
+
32
+ // rawSubjects is each cert's RawSubject field.
33
+ // Its indexes correspond to the getCert indexes.
34
+ rawSubjects [][]byte
18
35
}
19
36
20
37
// NewCertPool returns a new, empty CertPool.
21
38
func NewCertPool () * CertPool {
22
39
return & CertPool {
23
40
bySubjectKeyId : make (map [string ][]int ),
24
41
byName : make (map [string ][]int ),
42
+ haveSum : make (map [sum224 ]bool ),
43
+ }
44
+ }
45
+
46
+ // len returns the number of certs in the set.
47
+ // A nil set is a valid empty set.
48
+ func (s * CertPool ) len () int {
49
+ if s == nil {
50
+ return 0
25
51
}
52
+ return len (s .getCert )
53
+ }
54
+
55
+ // cert returns cert index n in s.
56
+ func (s * CertPool ) cert (n int ) (* Certificate , error ) {
57
+ return s .getCert [n ]()
26
58
}
27
59
28
60
func (s * CertPool ) copy () * CertPool {
29
61
p := & CertPool {
30
62
bySubjectKeyId : make (map [string ][]int , len (s .bySubjectKeyId )),
31
63
byName : make (map [string ][]int , len (s .byName )),
32
- certs : make ([]* Certificate , len (s .certs )),
64
+ haveSum : make (map [sum224 ]bool , len (s .haveSum )),
65
+ getCert : make ([]func () (* Certificate , error ), len (s .getCert )),
66
+ rawSubjects : make ([][]byte , len (s .rawSubjects )),
33
67
}
34
68
for k , v := range s .bySubjectKeyId {
35
69
indexes := make ([]int , len (v ))
@@ -41,7 +75,11 @@ func (s *CertPool) copy() *CertPool {
41
75
copy (indexes , v )
42
76
p .byName [k ] = indexes
43
77
}
44
- copy (p .certs , s .certs )
78
+ for k := range s .haveSum {
79
+ p .haveSum [k ] = true
80
+ }
81
+ copy (p .getCert , s .getCert )
82
+ copy (p .rawSubjects , s .rawSubjects )
45
83
return p
46
84
}
47
85
@@ -82,41 +120,61 @@ func (s *CertPool) findPotentialParents(cert *Certificate) []int {
82
120
return candidates
83
121
}
84
122
85
- func (s * CertPool ) contains (cert * Certificate ) bool {
123
+ func (s * CertPool ) contains (cert * Certificate ) ( bool , error ) {
86
124
if s == nil {
87
- return false
125
+ return false , nil
88
126
}
89
-
90
127
candidates := s .byName [string (cert .RawSubject )]
91
- for _ , c := range candidates {
92
- if s .certs [c ].Equal (cert ) {
93
- return true
128
+ for _ , i := range candidates {
129
+ c , err := s .cert (i )
130
+ if err != nil {
131
+ return false , err
132
+ }
133
+ if c .Equal (cert ) {
134
+ return true , nil
94
135
}
95
136
}
96
137
97
- return false
138
+ return false , nil
98
139
}
99
140
100
141
// AddCert adds a certificate to a pool.
101
142
func (s * CertPool ) AddCert (cert * Certificate ) {
102
143
if cert == nil {
103
144
panic ("adding nil Certificate to CertPool" )
104
145
}
146
+ s .addCertFunc (sha256 .Sum224 (cert .Raw ), string (cert .RawSubject ), string (cert .SubjectKeyId ), func () (* Certificate , error ) {
147
+ return cert , nil
148
+ })
149
+ }
105
150
151
+ // addCertFunc adds metadata about a certificate to a pool, along with
152
+ // a func to fetch that certificate later when needed.
153
+ //
154
+ // The rawSubject is Certificate.RawSubject and must be non-empty.
155
+ // The subjectKeyID is Certificate.SubjectKeyId and may be empty.
156
+ // The getCert func may be called 0 or more times.
157
+ func (s * CertPool ) addCertFunc (rawSum224 sum224 , rawSubject , subjectKeyID string , getCert func () (* Certificate , error )) {
106
158
// Check that the certificate isn't being added twice.
107
- if s .contains ( cert ) {
159
+ if s .haveSum [ rawSum224 ] {
108
160
return
109
161
}
162
+ s .haveSum [rawSum224 ] = true
163
+ s .addCertFuncNotDup (rawSubject , subjectKeyID , getCert )
164
+ }
110
165
111
- n := len (s .certs )
112
- s .certs = append (s .certs , cert )
166
+ func (s * CertPool ) addCertFuncNotDup (rawSubject , subjectKeyID string , getCert func () (* Certificate , error )) {
167
+ if getCert == nil {
168
+ panic ("getCert can't be nil" )
169
+ }
170
+ n := len (s .getCert )
171
+ s .getCert = append (s .getCert , getCert )
113
172
114
- if len (cert .SubjectKeyId ) > 0 {
115
- keyId := string (cert .SubjectKeyId )
116
- s .bySubjectKeyId [keyId ] = append (s .bySubjectKeyId [keyId ], n )
173
+ if subjectKeyID != "" {
174
+ s .bySubjectKeyId [subjectKeyID ] = append (s .bySubjectKeyId [subjectKeyID ], n )
117
175
}
118
- name := string ( cert . RawSubject )
119
- s .byName [ name ] = append (s .byName [ name ], n )
176
+ s . byName [ rawSubject ] = append ( s . byName [ rawSubject ], n )
177
+ s .rawSubjects = append (s .rawSubjects , [] byte ( rawSubject ) )
120
178
}
121
179
122
180
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
@@ -136,24 +194,34 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
136
194
continue
137
195
}
138
196
139
- cert , err := ParseCertificate (block .Bytes )
197
+ certBytes := block .Bytes
198
+ cert , err := ParseCertificate (certBytes )
140
199
if err != nil {
141
200
continue
142
201
}
143
-
144
- s .AddCert (cert )
202
+ var lazyCert struct {
203
+ sync.Once
204
+ v * Certificate
205
+ }
206
+ s .addCertFunc (sha256 .Sum224 (cert .Raw ), string (cert .RawSubject ), string (cert .SubjectKeyId ), func () (* Certificate , error ) {
207
+ lazyCert .Do (func () {
208
+ // This can't fail, as the same bytes already parsed above.
209
+ lazyCert .v , _ = ParseCertificate (certBytes )
210
+ certBytes = nil
211
+ })
212
+ return lazyCert .v , nil
213
+ })
145
214
ok = true
146
215
}
147
-
148
- return
216
+ return ok
149
217
}
150
218
151
219
// Subjects returns a list of the DER-encoded subjects of
152
220
// all of the certificates in the pool.
153
221
func (s * CertPool ) Subjects () [][]byte {
154
- res := make ([][]byte , len ( s . certs ))
155
- for i , c := range s .certs {
156
- res [i ] = c . RawSubject
222
+ res := make ([][]byte , s . len ( ))
223
+ for i , s := range s .rawSubjects {
224
+ res [i ] = s
157
225
}
158
226
return res
159
227
}
0 commit comments