| // 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 |
| } |