Skip to content

Commit dc3a92e

Browse files
committed
crypto/x509: matching any requested EKU should be sufficient.
The documentation was unclear here and I misremembered the behaviour and changed it in 1.10: it used to be that matching any EKU was enough but 1.10 requires that all EKUs match. Restore 1.9 behaviour and clarify the documentation to make it official. Fixes #24162. Change-Id: Ic9466cd0799cb27ec3a3a7e6c96f10c2aacc7020 Reviewed-on: https://go-review.googlesource.com/97720 Run-TryBot: Adam Langley <agl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org>
1 parent 2d8181e commit dc3a92e

File tree

2 files changed

+59
-7
lines changed

2 files changed

+59
-7
lines changed

src/crypto/x509/name_constraints_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,23 @@ var nameConstraintsTests = []nameConstraintsTest{
15411541
},
15421542
expectedError: "cannot parse rfc822Name",
15431543
},
1544+
1545+
// #80: if several EKUs are requested, satisfying any of them is sufficient.
1546+
nameConstraintsTest{
1547+
roots: []constraintsSpec{
1548+
constraintsSpec{},
1549+
},
1550+
intermediates: [][]constraintsSpec{
1551+
[]constraintsSpec{
1552+
constraintsSpec{},
1553+
},
1554+
},
1555+
leaf: leafSpec{
1556+
sans: []string{"dns:example.com"},
1557+
ekus: []string{"email"},
1558+
},
1559+
requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection},
1560+
},
15441561
}
15451562

15461563
func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) {

src/crypto/x509/verify.go

+42-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ package x509
66

77
import (
88
"bytes"
9+
"encoding/asn1"
910
"errors"
1011
"fmt"
1112
"net"
1213
"net/url"
1314
"reflect"
1415
"runtime"
16+
"strconv"
1517
"strings"
1618
"time"
1719
"unicode/utf8"
@@ -178,10 +180,14 @@ type VerifyOptions struct {
178180
Intermediates *CertPool
179181
Roots *CertPool // if nil, the system roots are used
180182
CurrentTime time.Time // if zero, the current time is used
181-
// KeyUsage specifies which Extended Key Usage values are acceptable.
182-
// An empty list means ExtKeyUsageServerAuth. Key usage is considered a
183-
// constraint down the chain which mirrors Windows CryptoAPI behavior,
184-
// but not the spec. To accept any key usage, include ExtKeyUsageAny.
183+
// KeyUsage specifies which Extended Key Usage values are acceptable. A leaf
184+
// certificate is accepted if it contains any of the listed values. An empty
185+
// list means ExtKeyUsageServerAuth. To accept any key usage, include
186+
// ExtKeyUsageAny.
187+
//
188+
// Certificate chains are required to nest extended key usage values,
189+
// irrespective of this value. This matches the Windows CryptoAPI behavior,
190+
// but not the spec.
185191
KeyUsages []ExtKeyUsage
186192
// MaxConstraintComparisions is the maximum number of comparisons to
187193
// perform when checking a given certificate's name constraints. If
@@ -786,6 +792,18 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
786792
return nil
787793
}
788794

795+
// formatOID formats an ASN.1 OBJECT IDENTIFER in the common, dotted style.
796+
func formatOID(oid asn1.ObjectIdentifier) string {
797+
ret := ""
798+
for i, v := range oid {
799+
if i > 0 {
800+
ret += "."
801+
}
802+
ret += strconv.Itoa(v)
803+
}
804+
return ret
805+
}
806+
789807
// Verify attempts to verify c by building one or more chains from c to a
790808
// certificate in opts.Roots, using certificates in opts.Intermediates if
791809
// needed. If successful, it returns one or more chains where the first
@@ -860,16 +878,33 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
860878
}
861879

862880
if checkEKU {
881+
foundMatch := false
863882
NextUsage:
864883
for _, eku := range requestedKeyUsages {
865884
for _, leafEKU := range c.ExtKeyUsage {
866885
if ekuPermittedBy(eku, leafEKU, checkingAgainstLeafCert) {
867-
continue NextUsage
886+
foundMatch = true
887+
break NextUsage
868888
}
869889
}
890+
}
870891

871-
oid, _ := oidFromExtKeyUsage(eku)
872-
return nil, CertificateInvalidError{c, IncompatibleUsage, fmt.Sprintf("%#v", oid)}
892+
if !foundMatch {
893+
msg := "leaf contains the following, recognized EKUs: "
894+
895+
for i, leafEKU := range c.ExtKeyUsage {
896+
oid, ok := oidFromExtKeyUsage(leafEKU)
897+
if !ok {
898+
continue
899+
}
900+
901+
if i > 0 {
902+
msg += ", "
903+
}
904+
msg += formatOID(oid)
905+
}
906+
907+
return nil, CertificateInvalidError{c, IncompatibleUsage, msg}
873908
}
874909
}
875910

0 commit comments

Comments
 (0)