summaryrefslogtreecommitdiffstats
path: root/src/crypto/x509/oid.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto/x509/oid.go')
-rw-r--r--src/crypto/x509/oid.go273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/crypto/x509/oid.go b/src/crypto/x509/oid.go
new file mode 100644
index 0000000..5359af6
--- /dev/null
+++ b/src/crypto/x509/oid.go
@@ -0,0 +1,273 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package x509
+
+import (
+ "bytes"
+ "encoding/asn1"
+ "errors"
+ "math"
+ "math/big"
+ "math/bits"
+ "strconv"
+ "strings"
+)
+
+var (
+ errInvalidOID = errors.New("invalid oid")
+)
+
+// An OID represents an ASN.1 OBJECT IDENTIFIER.
+type OID struct {
+ der []byte
+}
+
+func newOIDFromDER(der []byte) (OID, bool) {
+ if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
+ return OID{}, false
+ }
+
+ start := 0
+ for i, v := range der {
+ // ITU-T X.690, section 8.19.2:
+ // The subidentifier shall be encoded in the fewest possible octets,
+ // that is, the leading octet of the subidentifier shall not have the value 0x80.
+ if i == start && v == 0x80 {
+ return OID{}, false
+ }
+ if v&0x80 == 0 {
+ start = i + 1
+ }
+ }
+
+ return OID{der}, true
+}
+
+// OIDFromInts creates a new OID using ints, each integer is a separate component.
+func OIDFromInts(oid []uint64) (OID, error) {
+ if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
+ return OID{}, errInvalidOID
+ }
+
+ length := base128IntLength(oid[0]*40 + oid[1])
+ for _, v := range oid[2:] {
+ length += base128IntLength(v)
+ }
+
+ der := make([]byte, 0, length)
+ der = appendBase128Int(der, oid[0]*40+oid[1])
+ for _, v := range oid[2:] {
+ der = appendBase128Int(der, v)
+ }
+ return OID{der}, nil
+}
+
+func base128IntLength(n uint64) int {
+ if n == 0 {
+ return 1
+ }
+ return (bits.Len64(n) + 6) / 7
+}
+
+func appendBase128Int(dst []byte, n uint64) []byte {
+ for i := base128IntLength(n) - 1; i >= 0; i-- {
+ o := byte(n >> uint(i*7))
+ o &= 0x7f
+ if i != 0 {
+ o |= 0x80
+ }
+ dst = append(dst, o)
+ }
+ return dst
+}
+
+// Equal returns true when oid and other represents the same Object Identifier.
+func (oid OID) Equal(other OID) bool {
+ // There is only one possible DER encoding of
+ // each unique Object Identifier.
+ return bytes.Equal(oid.der, other.der)
+}
+
+func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, failed bool) {
+ offset = initOffset
+ var ret64 int64
+ for shifted := 0; offset < len(bytes); shifted++ {
+ // 5 * 7 bits per byte == 35 bits of data
+ // Thus the representation is either non-minimal or too large for an int32
+ if shifted == 5 {
+ failed = true
+ return
+ }
+ ret64 <<= 7
+ b := bytes[offset]
+ // integers should be minimally encoded, so the leading octet should
+ // never be 0x80
+ if shifted == 0 && b == 0x80 {
+ failed = true
+ return
+ }
+ ret64 |= int64(b & 0x7f)
+ offset++
+ if b&0x80 == 0 {
+ ret = int(ret64)
+ // Ensure that the returned value fits in an int on all platforms
+ if ret64 > math.MaxInt32 {
+ failed = true
+ }
+ return
+ }
+ }
+ failed = true
+ return
+}
+
+// EqualASN1OID returns whether an OID equals an asn1.ObjectIdentifier. If
+// asn1.ObjectIdentifier cannot represent the OID specified by oid, because
+// a component of OID requires more than 31 bits, it returns false.
+func (oid OID) EqualASN1OID(other asn1.ObjectIdentifier) bool {
+ if len(other) < 2 {
+ return false
+ }
+ v, offset, failed := parseBase128Int(oid.der, 0)
+ if failed {
+ // This should never happen, since we've already parsed the OID,
+ // but just in case.
+ return false
+ }
+ if v < 80 {
+ a, b := v/40, v%40
+ if other[0] != a || other[1] != b {
+ return false
+ }
+ } else {
+ a, b := 2, v-80
+ if other[0] != a || other[1] != b {
+ return false
+ }
+ }
+
+ i := 2
+ for ; offset < len(oid.der); i++ {
+ v, offset, failed = parseBase128Int(oid.der, offset)
+ if failed {
+ // Again, shouldn't happen, since we've already parsed
+ // the OID, but better safe than sorry.
+ return false
+ }
+ if v != other[i] {
+ return false
+ }
+ }
+
+ return i == len(other)
+}
+
+// Strings returns the string representation of the Object Identifier.
+func (oid OID) String() string {
+ var b strings.Builder
+ b.Grow(32)
+ const (
+ valSize = 64 // size in bits of val.
+ bitsPerByte = 7
+ maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
+ )
+ var (
+ start = 0
+ val = uint64(0)
+ numBuf = make([]byte, 0, 21)
+ bigVal *big.Int
+ overflow bool
+ )
+ for i, v := range oid.der {
+ curVal := v & 0x7F
+ valEnd := v&0x80 == 0
+ if valEnd {
+ if start != 0 {
+ b.WriteByte('.')
+ }
+ }
+ if !overflow && val > maxValSafeShift {
+ if bigVal == nil {
+ bigVal = new(big.Int)
+ }
+ bigVal = bigVal.SetUint64(val)
+ overflow = true
+ }
+ if overflow {
+ bigVal = bigVal.Lsh(bigVal, bitsPerByte).Or(bigVal, big.NewInt(int64(curVal)))
+ if valEnd {
+ if start == 0 {
+ b.WriteString("2.")
+ bigVal = bigVal.Sub(bigVal, big.NewInt(80))
+ }
+ numBuf = bigVal.Append(numBuf, 10)
+ b.Write(numBuf)
+ numBuf = numBuf[:0]
+ val = 0
+ start = i + 1
+ overflow = false
+ }
+ continue
+ }
+ val <<= bitsPerByte
+ val |= uint64(curVal)
+ if valEnd {
+ if start == 0 {
+ if val < 80 {
+ b.Write(strconv.AppendUint(numBuf, val/40, 10))
+ b.WriteByte('.')
+ b.Write(strconv.AppendUint(numBuf, val%40, 10))
+ } else {
+ b.WriteString("2.")
+ b.Write(strconv.AppendUint(numBuf, val-80, 10))
+ }
+ } else {
+ b.Write(strconv.AppendUint(numBuf, val, 10))
+ }
+ val = 0
+ start = i + 1
+ }
+ }
+ return b.String()
+}
+
+func (oid OID) toASN1OID() (asn1.ObjectIdentifier, bool) {
+ out := make([]int, 0, len(oid.der)+1)
+
+ const (
+ valSize = 31 // amount of usable bits of val for OIDs.
+ bitsPerByte = 7
+ maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
+ )
+
+ val := 0
+
+ for _, v := range oid.der {
+ if val > maxValSafeShift {
+ return nil, false
+ }
+
+ val <<= bitsPerByte
+ val |= int(v & 0x7F)
+
+ if v&0x80 == 0 {
+ if len(out) == 0 {
+ if val < 80 {
+ out = append(out, val/40)
+ out = append(out, val%40)
+ } else {
+ out = append(out, 2)
+ out = append(out, val-80)
+ }
+ val = 0
+ continue
+ }
+ out = append(out, val)
+ val = 0
+ }
+ }
+
+ return out, true
+}