for _, e := range in.TBSCertificate.Extensions {
out.Extensions = append(out.Extensions, e)
+ failIfCritical := false
if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 {
switch e.Id[3] {
case 15:
// RFC 5280, 4.2.1.3
var usageBits asn1.BitString
- _, err := asn1.Unmarshal(e.Value, &usageBits)
+ if _, err := asn1.Unmarshal(e.Value, &usageBits); err != nil {
+ return nil, err
+ }
- if err == nil {
- var usage int
- for i := 0; i < 9; i++ {
- if usageBits.At(i) != 0 {
- usage |= 1 << uint(i)
- }
+ var usage int
+ for i := 0; i < 9; i++ {
+ if usageBits.At(i) != 0 {
+ usage |= 1 << uint(i)
}
- out.KeyUsage = KeyUsage(usage)
- continue
}
+ out.KeyUsage = KeyUsage(usage)
+
case 19:
// RFC 5280, 4.2.1.9
var constraints basicConstraints
- _, err := asn1.Unmarshal(e.Value, &constraints)
-
- if err == nil {
- out.BasicConstraintsValid = true
- out.IsCA = constraints.IsCA
- out.MaxPathLen = constraints.MaxPathLen
- out.MaxPathLenZero = out.MaxPathLen == 0
- continue
+ if _, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
+ return nil, err
}
+
+ out.BasicConstraintsValid = true
+ out.IsCA = constraints.IsCA
+ out.MaxPathLen = constraints.MaxPathLen
+ out.MaxPathLenZero = out.MaxPathLen == 0
+
case 17:
out.DNSNames, out.EmailAddresses, out.IPAddresses, err = parseSANExtension(e.Value)
if err != nil {
return nil, err
}
- if len(out.DNSNames) > 0 || len(out.EmailAddresses) > 0 || len(out.IPAddresses) > 0 {
- continue
+ if len(out.DNSNames) == 0 && len(out.EmailAddresses) == 0 && len(out.IPAddresses) == 0 {
+ // If we didn't parse anything then we do the critical check, below.
+ failIfCritical = true
}
- // If we didn't parse any of the names then we
- // fall through to the critical check below.
case 30:
// RFC 5280, 4.2.1.10
// BaseDistance ::= INTEGER (0..MAX)
var constraints nameConstraints
- _, err := asn1.Unmarshal(e.Value, &constraints)
- if err != nil {
+ if _, err := asn1.Unmarshal(e.Value, &constraints); err != nil {
return nil, err
}
}
out.PermittedDNSDomains = append(out.PermittedDNSDomains, subtree.Name)
}
- continue
case 31:
// RFC 5280, 4.2.1.14
// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
var cdp []distributionPoint
- _, err := asn1.Unmarshal(e.Value, &cdp)
- if err != nil {
+ if _, err := asn1.Unmarshal(e.Value, &cdp); err != nil {
return nil, err
}
for _, dp := range cdp {
var n asn1.RawValue
- _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n)
- if err != nil {
+ if _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n); err != nil {
return nil, err
}
out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes))
}
}
- continue
case 35:
// RFC 5280, 4.2.1.1
var a authKeyId
- _, err = asn1.Unmarshal(e.Value, &a)
- if err != nil {
+ if _, err = asn1.Unmarshal(e.Value, &a); err != nil {
return nil, err
}
out.AuthorityKeyId = a.Id
- continue
case 37:
// RFC 5280, 4.2.1.12. Extended Key Usage
// KeyPurposeId ::= OBJECT IDENTIFIER
var keyUsage []asn1.ObjectIdentifier
- _, err = asn1.Unmarshal(e.Value, &keyUsage)
- if err != nil {
+ if _, err = asn1.Unmarshal(e.Value, &keyUsage); err != nil {
return nil, err
}
}
}
- continue
-
case 14:
// RFC 5280, 4.2.1.2
var keyid []byte
- _, err = asn1.Unmarshal(e.Value, &keyid)
- if err != nil {
+ if _, err = asn1.Unmarshal(e.Value, &keyid); err != nil {
return nil, err
}
out.SubjectKeyId = keyid
- continue
case 32:
// RFC 5280 4.2.1.4: Certificate Policies
for i, policy := range policies {
out.PolicyIdentifiers[i] = policy.Policy
}
+
+ default:
+ // Unknown extensions cause an error if marked as critical.
+ failIfCritical = true
}
} else if e.Id.Equal(oidExtensionAuthorityInfoAccess) {
// RFC 5280 4.2.2.1: Authority Information Access
out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes))
}
}
+ } else {
+ // Unknown extensions cause an error if marked as critical.
+ failIfCritical = true
}
- if e.Critical {
+ if e.Critical && failIfCritical {
return out, UnhandledCriticalExtension{}
}
}
}
}
+func TestUnknownCriticalExtension(t *testing.T) {
+ priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("Failed to generate ECDSA key: %s", err)
+ }
+
+ oids := []asn1.ObjectIdentifier{
+ // This OID is in the PKIX arc, but unknown.
+ asn1.ObjectIdentifier{2, 5, 29, 999999},
+ // This is a nonsense, unassigned OID.
+ asn1.ObjectIdentifier{1, 2, 3, 4},
+ }
+
+ for _, oid := range oids {
+ template := Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: "foo",
+ },
+ NotBefore: time.Unix(1000, 0),
+ NotAfter: time.Unix(100000, 0),
+
+ ExtraExtensions: []pkix.Extension{
+ {
+ Id: oid,
+ Critical: true,
+ Value: nil,
+ },
+ },
+ }
+
+ derBytes, err := CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
+ if err != nil {
+ t.Fatalf("failed to create certificate: %s", err)
+ }
+
+ _, err = ParseCertificate(derBytes)
+ if err == nil {
+ t.Fatalf("Certificate with critical extension was parsed without error.")
+ }
+ if _, ok := err.(UnhandledCriticalExtension); !ok {
+ t.Fatalf("Error was %#v, but wanted one of type UnhandledCriticalExtension", err)
+ }
+ }
+}
+
// Self-signed certificate using ECDSA with SHA1 & secp256r1
var ecdsaSHA1CertPem = `
-----BEGIN CERTIFICATE-----