1 package precert
2
3 import (
4 "bytes"
5 encoding_asn1 "encoding/asn1"
6 "errors"
7 "fmt"
8
9 "golang.org/x/crypto/cryptobyte"
10 "golang.org/x/crypto/cryptobyte/asn1"
11 )
12
13
14
15
16
17 func Correspond(precertDER, finalDER []byte) error {
18 preTBS, err := tbsDERFromCertDER(precertDER)
19 if err != nil {
20 return fmt.Errorf("parsing precert: %w", err)
21 }
22
23 finalTBS, err := tbsDERFromCertDER(finalDER)
24 if err != nil {
25 return fmt.Errorf("parsing final cert: %w", err)
26 }
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 for i := 0; i < 7; i++ {
48 if err := readIdenticalElement(&preTBS, &finalTBS); err != nil {
49 return fmt.Errorf("checking for identical field %d: %w", i, err)
50 }
51 }
52
53
54
55
56
57
58
59
60 precertExtensionBytes, err := unwrapExtensions(preTBS)
61 if err != nil {
62 return fmt.Errorf("parsing precert extensions: %w", err)
63 }
64
65 finalCertExtensionBytes, err := unwrapExtensions(finalTBS)
66 if err != nil {
67 return fmt.Errorf("parsing final cert extensions: %w", err)
68 }
69
70 precertParser := extensionParser{bytes: precertExtensionBytes, skippableOID: poisonOID}
71 finalCertParser := extensionParser{bytes: finalCertExtensionBytes, skippableOID: sctListOID}
72
73 for i := 0; ; i++ {
74 precertExtn, err := precertParser.Next()
75 if err != nil {
76 return err
77 }
78
79 finalCertExtn, err := finalCertParser.Next()
80 if err != nil {
81 return err
82 }
83
84 if !bytes.Equal(precertExtn, finalCertExtn) {
85 return fmt.Errorf("precert extension %d (%x) not equal to final cert extension %d (%x)",
86 i+precertParser.skipped, precertExtn, i+finalCertParser.skipped, finalCertExtn)
87 }
88
89 if precertExtn == nil && finalCertExtn == nil {
90 break
91 }
92 }
93
94 if precertParser.skipped == 0 {
95 return fmt.Errorf("no poison extension found in precert")
96 }
97 if precertParser.skipped > 1 {
98 return fmt.Errorf("multiple poison extensions found in precert")
99 }
100 if finalCertParser.skipped == 0 {
101 return fmt.Errorf("no SCTList extension found in final cert")
102 }
103 if finalCertParser.skipped > 1 {
104 return fmt.Errorf("multiple SCTList extensions found in final cert")
105 }
106 return nil
107 }
108
109 var poisonOID = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
110 var sctListOID = []int{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
111
112
113
114
115
116 type extensionParser struct {
117 skippableOID encoding_asn1.ObjectIdentifier
118 bytes cryptobyte.String
119 skipped int
120 }
121
122
123
124
125 func (e *extensionParser) Next() (cryptobyte.String, error) {
126 if e.bytes.Empty() {
127 return nil, nil
128 }
129
130 var next cryptobyte.String
131 if !e.bytes.ReadASN1(&next, asn1.SEQUENCE) {
132 return nil, fmt.Errorf("failed to parse extension")
133 }
134
135 var oid encoding_asn1.ObjectIdentifier
136 nextCopy := next
137 if !nextCopy.ReadASN1ObjectIdentifier(&oid) {
138 return nil, fmt.Errorf("failed to parse extension OID")
139 }
140
141 if oid.Equal(e.skippableOID) {
142 e.skipped++
143 return e.Next()
144 }
145
146 return next, nil
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160
161 func unwrapExtensions(field cryptobyte.String) (cryptobyte.String, error) {
162 var extensions cryptobyte.String
163 if !field.ReadASN1(&extensions, asn1.Tag(3).Constructed().ContextSpecific()) {
164 return nil, errors.New("error reading extensions")
165 }
166
167 var extensionsInner cryptobyte.String
168 if !extensions.ReadASN1(&extensionsInner, asn1.SEQUENCE) {
169 return nil, errors.New("error reading extensions inner")
170 }
171
172 return extensionsInner, nil
173 }
174
175
176
177 func readIdenticalElement(a, b *cryptobyte.String) error {
178 var aInner, bInner cryptobyte.String
179 var aTag, bTag asn1.Tag
180 if !a.ReadAnyASN1Element(&aInner, &aTag) {
181 return fmt.Errorf("failed to read element from first input")
182 }
183 if !b.ReadAnyASN1Element(&bInner, &bTag) {
184 return fmt.Errorf("failed to read element from first input")
185 }
186 if aTag != bTag {
187 return fmt.Errorf("tags differ: %d != %d", aTag, bTag)
188 }
189 if !bytes.Equal([]byte(aInner), []byte(bInner)) {
190 return fmt.Errorf("elements differ: %x != %x", aInner, bInner)
191 }
192 return nil
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 func tbsDERFromCertDER(certDER []byte) (cryptobyte.String, error) {
209 var inner cryptobyte.String
210 input := cryptobyte.String(certDER)
211
212 if !input.ReadASN1(&inner, asn1.SEQUENCE) {
213 return nil, fmt.Errorf("failed to read outer sequence")
214 }
215
216 var tbsCertificate cryptobyte.String
217 if !inner.ReadASN1(&tbsCertificate, asn1.SEQUENCE) {
218 return nil, fmt.Errorf("failed to read tbsCertificate")
219 }
220
221 return tbsCertificate, nil
222 }
223
View as plain text