Skip to content

Commit bb7cbe0

Browse files
authored
Fix PreIssuer detection (#554)
* Fix PreIssuer detection y * Use OIDs from rfc6962 * add IsCA check, and more thorough tests. o * better names * add comment
1 parent caeded8 commit bb7cbe0

File tree

3 files changed

+389
-47
lines changed

3 files changed

+389
-47
lines changed

internal/types/rfc6962/rfc6962.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ const (
4343
TreeNodePrefix = byte(0x01)
4444
)
4545

46-
// Defined in RFC 6962 s3.1.
46+
// Defined or referenced in RFC 6962 s3.1.
4747
var (
48+
OIDExtAuthorityKeyId = asn1.ObjectIdentifier{2, 5, 29, 35}
49+
OIDExtKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37}
4850
OIDExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
4951
OIDExtKeyUsageCertificateTransparency = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 4}
5052
)

internal/x509util/ct.go

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,9 @@ import (
2525

2626
"github.com/transparency-dev/tessera/ctonly"
2727
"github.com/transparency-dev/tesseract/internal/types/rfc6962"
28-
)
2928

30-
var (
31-
oidExtensionAuthorityKeyId = asn1.ObjectIdentifier{2, 5, 29, 35}
32-
// These extensions are defined in RFC 6962 s3.1.
33-
oidExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
34-
oidExtensionKeyUsageCertificateTransparency = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 4}
29+
"golang.org/x/crypto/cryptobyte"
30+
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
3531
)
3632

3733
type tbsCertificate struct {
@@ -110,7 +106,7 @@ func removeExtension(tbsData []byte, oid asn1.ObjectIdentifier) ([]byte, error)
110106
// - The precert's AuthorityKeyId is changed to the AuthorityKeyId of the
111107
// intermediate.
112108
func BuildPrecertTBS(tbsData []byte, preIssuer *x509.Certificate) ([]byte, error) {
113-
data, err := removeExtension(tbsData, oidExtensionCTPoison)
109+
data, err := removeExtension(tbsData, rfc6962.OIDExtensionCTPoison)
114110
if err != nil {
115111
return nil, err
116112
}
@@ -133,28 +129,20 @@ func BuildPrecertTBS(tbsData []byte, preIssuer *x509.Certificate) ([]byte, error
133129
// to that of the preIssuer.
134130
var issuerKeyID []byte
135131
for _, ext := range preIssuer.Extensions {
136-
if ext.Id.Equal(oidExtensionAuthorityKeyId) {
132+
if ext.Id.Equal(rfc6962.OIDExtAuthorityKeyId) {
137133
issuerKeyID = ext.Value
138134
break
139135
}
140136
}
141137

142-
// The x509 package does not parse CT EKU, so look for it in
143-
// extensions directly.
144-
seenCTEKU := false
145-
for _, ext := range preIssuer.Extensions {
146-
if ext.Id.Equal(oidExtensionKeyUsageCertificateTransparency) {
147-
seenCTEKU = true
148-
break
149-
}
150-
}
151-
if !seenCTEKU {
138+
// TODO(phbnf): is this check really necessary?
139+
if !isPreIssuer(preIssuer) {
152140
return nil, fmt.Errorf("issuer does not have CertificateTransparency extended key usage")
153141
}
154142

155143
keyAt := -1
156144
for i, ext := range tbs.Extensions {
157-
if ext.Id.Equal(oidExtensionAuthorityKeyId) {
145+
if ext.Id.Equal(rfc6962.OIDExtAuthorityKeyId) {
158146
keyAt = i
159147
break
160148
}
@@ -169,7 +157,7 @@ func BuildPrecertTBS(tbsData []byte, preIssuer *x509.Certificate) ([]byte, error
169157
} else if issuerKeyID != nil {
170158
// PreCert did not have an auth-key-id, but the preIssuer does, so add it at the end.
171159
authKeyIDExt := pkix.Extension{
172-
Id: oidExtensionAuthorityKeyId,
160+
Id: rfc6962.OIDExtAuthorityKeyId,
173161
Critical: false,
174162
Value: issuerKeyID,
175163
}
@@ -198,7 +186,6 @@ func RemoveCTPoison(tbsData []byte) ([]byte, error) {
198186

199187
// EntryFromChain generates an Entry from a chain and timestamp.
200188
// copied from certificate-transparency-go/serialization.go
201-
// TODO(phboneff): add tests
202189
func EntryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64) (*ctonly.Entry, error) {
203190
leaf := ctonly.Entry{
204191
IsPrecert: isPrecert,
@@ -256,14 +243,35 @@ func EntryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64)
256243
return &leaf, nil
257244
}
258245

259-
// isPreIssuer indicates whether a certificate is a pre-cert issuer with the specific
260-
// certificate transparency extended key usage.
246+
// isPreIssuer indicates if a certificate is a precertificate signing cert.
247+
//
248+
// From RFC6962 s3.1, these certs should contain:
249+
// (CA:true, Extended Key Usage: Certificate Transparency, OID 1.3.6.1.4.1.11129.2.4.4)
261250
func isPreIssuer(cert *x509.Certificate) bool {
262-
// Look for the extension in the Extensions field and not ExtKeyUsage
263-
// since crypto/x509 does not recognize this extension as an ExtKeyUsage.
251+
if !cert.IsCA {
252+
return false
253+
}
254+
// Look for the extension in the Extensions field and not in ExtKeyUsage
255+
// since crypto/x509 does not recognize this extension as such.
256+
// We cannot reliably check in UnknownExtKeyUsage either, since it might
257+
// one day disappear from UnknownExtKeyUsage and make it to ExtKeyUsage.
258+
// Given that ExtKeyUsage does not contain OIDs, we would not be able to
259+
// detect such a move.
264260
for _, ext := range cert.Extensions {
265-
if rfc6962.OIDExtKeyUsageCertificateTransparency.Equal(ext.Id) {
266-
return true
261+
if rfc6962.OIDExtKeyUsage.Equal(ext.Id) {
262+
der := cryptobyte.String(ext.Value)
263+
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
264+
continue
265+
}
266+
for !der.Empty() {
267+
var eku asn1.ObjectIdentifier
268+
if !der.ReadASN1ObjectIdentifier(&eku) {
269+
continue
270+
}
271+
if rfc6962.OIDExtKeyUsageCertificateTransparency.Equal(eku) {
272+
return true
273+
}
274+
}
267275
}
268276
}
269277
return false

0 commit comments

Comments
 (0)