From e2cae9ecdf944a1cc5d8803ff8932180858b8ce6 Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Wed, 26 Nov 2025 09:21:13 -0800 Subject: [PATCH] crypto/x509: add ExtKeyUsage.OID method And OIDFromASN1OID for converting between asn1.ObjectIdentifier and OID. Fixes #75325 Change-Id: I3b84dce54346d88aab731ffe30d0fef07b014f04 Reviewed-on: https://go-review.googlesource.com/c/go/+/724761 Reviewed-by: Neal Patel Auto-Submit: Roland Shoemaker Commit-Queue: Neal Patel LUCI-TryBot-Result: Go LUCI --- api/next/75325.txt | 2 ++ .../6-stdlib/99-minor/crypto/x509/75325.md | 4 ++++ src/crypto/x509/oid.go | 12 ++++++++++ src/crypto/x509/oid_test.go | 24 +++++++++++++++++++ src/crypto/x509/x509.go | 13 ++++++++++ src/crypto/x509/x509_test.go | 9 +++++++ 6 files changed, 64 insertions(+) create mode 100644 api/next/75325.txt create mode 100644 doc/next/6-stdlib/99-minor/crypto/x509/75325.md diff --git a/api/next/75325.txt b/api/next/75325.txt new file mode 100644 index 0000000000..dc241efef2 --- /dev/null +++ b/api/next/75325.txt @@ -0,0 +1,2 @@ +pkg crypto/x509, func OIDFromASN1OID(asn1.ObjectIdentifier) (OID, error) #75325 +pkg crypto/x509, method (ExtKeyUsage) OID() OID #75325 diff --git a/doc/next/6-stdlib/99-minor/crypto/x509/75325.md b/doc/next/6-stdlib/99-minor/crypto/x509/75325.md new file mode 100644 index 0000000000..a133e66209 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/crypto/x509/75325.md @@ -0,0 +1,4 @@ +The [ExtKeyUsage] type now has an OID method that returns the corresponding OID for the EKU. + +The new [OIDFromASN1OID] function allows converting an [encoding/asn1.ObjectIdentifier] into +an [OID]. \ No newline at end of file diff --git a/src/crypto/x509/oid.go b/src/crypto/x509/oid.go index c60daa7540..8bf19a1433 100644 --- a/src/crypto/x509/oid.go +++ b/src/crypto/x509/oid.go @@ -393,3 +393,15 @@ func (oid OID) toASN1OID() (asn1.ObjectIdentifier, bool) { return out, true } + +// OIDFromASN1OID creates a new OID using asn1OID. +func OIDFromASN1OID(asn1OID asn1.ObjectIdentifier) (OID, error) { + uint64OID := make([]uint64, 0, len(asn1OID)) + for _, component := range asn1OID { + if component < 0 { + return OID{}, errors.New("x509: OID components must be non-negative") + } + uint64OID = append(uint64OID, uint64(component)) + } + return OIDFromInts(uint64OID) +} diff --git a/src/crypto/x509/oid_test.go b/src/crypto/x509/oid_test.go index ce3a0672a6..efc71fc2dc 100644 --- a/src/crypto/x509/oid_test.go +++ b/src/crypto/x509/oid_test.go @@ -343,3 +343,27 @@ func BenchmarkOIDMarshalUnmarshalText(b *testing.B) { } } } + +func TestOIDFromASN1OID(t *testing.T) { + negativeComponentOID := asn1.ObjectIdentifier{-1} + _, err := OIDFromASN1OID(negativeComponentOID) + if err == nil || err.Error() != "x509: OID components must be non-negative" { + t.Fatalf("OIDFromASN1OID() = %v; want = \"x509: OID components must be non-negative\"", err) + } + + shortOID := asn1.ObjectIdentifier{1} + _, err = OIDFromASN1OID(shortOID) + if err == nil || err != errInvalidOID { + t.Fatalf("OIDFromASN1OID() = %v; want = %q", err, errInvalidOID) + } + invalidOIDFirstComponent := asn1.ObjectIdentifier{255, 1} + _, err = OIDFromASN1OID(invalidOIDFirstComponent) + if err == nil || err != errInvalidOID { + t.Fatalf("OIDFromASN1OID() = %v; want = %q", err, errInvalidOID) + } + invalidOIDSecondComponent := asn1.ObjectIdentifier{1, 255} + _, err = OIDFromASN1OID(invalidOIDSecondComponent) + if err == nil || err != errInvalidOID { + t.Fatalf("OIDFromASN1OID() = %v; want = %q", err, errInvalidOID) + } +} diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 85e8fceedc..7953b615f5 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -687,6 +687,19 @@ func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) { return } +// OID returns the ASN.1 object identifier of the EKU. +func (eku ExtKeyUsage) OID() OID { + asn1OID, ok := oidFromExtKeyUsage(eku) + if !ok { + panic("x509: internal error: known ExtKeyUsage has no OID") + } + oid, err := OIDFromASN1OID(asn1OID) + if err != nil { + panic("x509: internal error: known ExtKeyUsage has invalid OID") + } + return oid +} + // A Certificate represents an X.509 certificate. type Certificate struct { Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 98f3f7941c..183ee303fa 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -4263,3 +4263,12 @@ func TestCreateCertificateNegativeMaxPathLength(t *testing.T) { t.Fatalf(`CreateCertificate() = %v; want = "x509: invalid MaxPathLen, must be greater or equal to -1"`, err) } } + +func TestEKUOIDS(t *testing.T) { + for _, eku := range extKeyUsageOIDs { + oid := eku.extKeyUsage.OID() + if !oid.EqualASN1OID(eku.oid) { + t.Errorf("extKeyUsage %v: expected OID %v, got %v", eku.extKeyUsage, eku.oid, oid) + } + } +} -- 2.52.0