1
2
3
4
5
6
7
8
9
10 package nss
11
12 import (
13 "bufio"
14 "bytes"
15 "crypto/sha1"
16 "crypto/x509"
17 "errors"
18 "fmt"
19 "io"
20 "strconv"
21 "strings"
22 "time"
23 )
24
25
26
27 type Constraint interface {
28 Kind() Kind
29 }
30
31
32 type Kind int
33
34 const (
35 CKA_NSS_SERVER_DISTRUST_AFTER Kind = iota
36 )
37
38
39
40
41
42 type DistrustAfter time.Time
43
44 func (DistrustAfter) Kind() Kind {
45 return CKA_NSS_SERVER_DISTRUST_AFTER
46 }
47
48
49
50
51 type Certificate struct {
52
53 X509 *x509.Certificate
54
55
56
57
58 Constraints []Constraint
59 }
60
61 func parseMulitLineOctal(s *bufio.Scanner) ([]byte, error) {
62 buf := bytes.NewBuffer(nil)
63 for s.Scan() {
64 if s.Text() == "END" {
65 break
66 }
67 b, err := strconv.Unquote(fmt.Sprintf("\"%s\"", s.Text()))
68 if err != nil {
69 return nil, err
70 }
71 buf.Write([]byte(b))
72 }
73 return buf.Bytes(), nil
74 }
75
76 type certObj struct {
77 c *x509.Certificate
78 DistrustAfter *time.Time
79 }
80
81 func parseCertClass(s *bufio.Scanner) ([sha1.Size]byte, *certObj, error) {
82 var h [sha1.Size]byte
83 co := &certObj{}
84 for s.Scan() {
85 l := s.Text()
86 if l == "" {
87
88 break
89 }
90 if strings.HasPrefix(l, "CKA_VALUE") {
91 b, err := parseMulitLineOctal(s)
92 if err != nil {
93 return h, nil, err
94 }
95 co.c, err = x509.ParseCertificate(b)
96 if err != nil {
97 return h, nil, err
98 }
99 h = sha1.Sum(b)
100 } else if strings.HasPrefix(l, "CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_FALSE") {
101
102 return h, nil, nil
103 } else if l == "CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL" {
104 dateStr, err := parseMulitLineOctal(s)
105 if err != nil {
106 return h, nil, err
107 }
108 t, err := time.Parse("060102150405Z0700", string(dateStr))
109 if err != nil {
110 return h, nil, err
111 }
112 co.DistrustAfter = &t
113 }
114 }
115 if co.c == nil {
116 return h, nil, errors.New("malformed CKO_CERTIFICATE object")
117 }
118 return h, co, nil
119 }
120
121 type trustObj struct {
122 trusted bool
123 }
124
125 func parseTrustClass(s *bufio.Scanner) ([sha1.Size]byte, *trustObj, error) {
126 var h [sha1.Size]byte
127 to := &trustObj{trusted: false}
128
129 for s.Scan() {
130 l := s.Text()
131 if l == "" {
132
133 break
134 }
135 if l == "CKA_CERT_SHA1_HASH MULTILINE_OCTAL" {
136 hash, err := parseMulitLineOctal(s)
137 if err != nil {
138 return h, nil, err
139 }
140 copy(h[:], hash)
141 } else if l == "CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR" {
142
143 to.trusted = true
144 }
145 }
146
147 return h, to, nil
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 func Parse(r io.Reader) ([]*Certificate, error) {
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 scanner := bufio.NewScanner(r)
186
187 type nssEntry struct {
188 cert *certObj
189 trust *trustObj
190 }
191 entries := map[[sha1.Size]byte]*nssEntry{}
192
193 for scanner.Scan() {
194
195 if !strings.HasPrefix(scanner.Text(), "CKA_CLASS") {
196 continue
197 }
198
199 f := strings.Fields(scanner.Text())
200 if len(f) != 3 {
201 return nil, errors.New("malformed CKA_CLASS")
202 }
203 switch f[2] {
204 case "CKO_CERTIFICATE":
205 h, co, err := parseCertClass(scanner)
206 if err != nil {
207 return nil, err
208 }
209 if co != nil {
210 e, ok := entries[h]
211 if !ok {
212 e = &nssEntry{}
213 entries[h] = e
214 }
215 e.cert = co
216 }
217
218 case "CKO_NSS_TRUST":
219 h, to, err := parseTrustClass(scanner)
220 if err != nil {
221 return nil, err
222 }
223 if to != nil {
224 e, ok := entries[h]
225 if !ok {
226 e = &nssEntry{}
227 entries[h] = e
228 }
229 e.trust = to
230 }
231 }
232 }
233 if err := scanner.Err(); err != nil {
234 return nil, err
235 }
236
237 var certs []*Certificate
238 for h, e := range entries {
239 if e.cert == nil && e.trust != nil {
240
241
242
243
244 continue
245 } else if e.cert != nil && e.trust == nil {
246 return nil, fmt.Errorf("missing trust object for certificate with SHA1 hash: %x", h)
247 }
248 if !e.trust.trusted {
249 continue
250 }
251 nssCert := &Certificate{X509: e.cert.c}
252 if e.cert.DistrustAfter != nil {
253 nssCert.Constraints = append(nssCert.Constraints, DistrustAfter(*e.cert.DistrustAfter))
254 }
255 certs = append(certs, nssCert)
256 }
257
258 return certs, nil
259 }
260
View as plain text