oid.mx raw

   1  // Copyright 2023 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package x509
   6  
   7  import (
   8  	"bytes"
   9  	"encoding/asn1"
  10  	"errors"
  11  	"math"
  12  	"math/big"
  13  	"math/bits"
  14  	"strconv"
  15  )
  16  
  17  var (
  18  	errInvalidOID = errors.New("invalid oid")
  19  )
  20  
  21  // An OID represents an ASN.1 OBJECT IDENTIFIER.
  22  type OID struct {
  23  	der []byte
  24  }
  25  
  26  // ParseOID parses a Object Identifier string, represented by ASCII numbers separated by dots.
  27  func ParseOID(oid string) (OID, error) {
  28  	var o OID
  29  	return o, o.unmarshalOIDText(oid)
  30  }
  31  
  32  func newOIDFromDER(der []byte) (OID, bool) {
  33  	if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
  34  		return OID{}, false
  35  	}
  36  
  37  	start := 0
  38  	for i, v := range der {
  39  		// ITU-T X.690, section 8.19.2:
  40  		// The subidentifier shall be encoded in the fewest possible octets,
  41  		// that is, the leading octet of the subidentifier shall not have the value 0x80.
  42  		if i == start && v == 0x80 {
  43  			return OID{}, false
  44  		}
  45  		if v&0x80 == 0 {
  46  			start = i + 1
  47  		}
  48  	}
  49  
  50  	return OID{der}, true
  51  }
  52  
  53  // OIDFromInts creates a new OID using ints, each integer is a separate component.
  54  func OIDFromInts(oid []uint64) (OID, error) {
  55  	if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
  56  		return OID{}, errInvalidOID
  57  	}
  58  
  59  	length := base128IntLength(oid[0]*40 + oid[1])
  60  	for _, v := range oid[2:] {
  61  		length += base128IntLength(v)
  62  	}
  63  
  64  	der := []byte{:0:length}
  65  	der = appendBase128Int(der, oid[0]*40+oid[1])
  66  	for _, v := range oid[2:] {
  67  		der = appendBase128Int(der, v)
  68  	}
  69  	return OID{der}, nil
  70  }
  71  
  72  func base128IntLength(n uint64) int {
  73  	if n == 0 {
  74  		return 1
  75  	}
  76  	return (bits.Len64(n) + 6) / 7
  77  }
  78  
  79  func appendBase128Int(dst []byte, n uint64) []byte {
  80  	for i := base128IntLength(n) - 1; i >= 0; i-- {
  81  		o := byte(n >> uint(i*7))
  82  		o &= 0x7f
  83  		if i != 0 {
  84  			o |= 0x80
  85  		}
  86  		dst = append(dst, o)
  87  	}
  88  	return dst
  89  }
  90  
  91  func base128BigIntLength(n *big.Int) int {
  92  	if n.Cmp(big.NewInt(0)) == 0 {
  93  		return 1
  94  	}
  95  	return (n.BitLen() + 6) / 7
  96  }
  97  
  98  func appendBase128BigInt(dst []byte, n *big.Int) []byte {
  99  	if n.Cmp(big.NewInt(0)) == 0 {
 100  		return append(dst, 0)
 101  	}
 102  
 103  	for i := base128BigIntLength(n) - 1; i >= 0; i-- {
 104  		o := byte(big.NewInt(0).Rsh(n, uint(i)*7).Bits()[0])
 105  		o &= 0x7f
 106  		if i != 0 {
 107  			o |= 0x80
 108  		}
 109  		dst = append(dst, o)
 110  	}
 111  	return dst
 112  }
 113  
 114  // AppendText implements [encoding.TextAppender]
 115  func (o OID) AppendText(b []byte) ([]byte, error) {
 116  	return append(b, o.String()...), nil
 117  }
 118  
 119  // MarshalText implements [encoding.TextMarshaler]
 120  func (o OID) MarshalText() ([]byte, error) {
 121  	return o.AppendText(nil)
 122  }
 123  
 124  // UnmarshalText implements [encoding.TextUnmarshaler]
 125  func (o *OID) UnmarshalText(text []byte) error {
 126  	return o.unmarshalOIDText(string(text))
 127  }
 128  
 129  func (o *OID) unmarshalOIDText(oid string) error {
 130  	// (*big.Int).SetString allows +/- signs, but we don't want
 131  	// to allow them in the string representation of Object Identifier, so
 132  	// reject such encodings.
 133  	for _, c := range oid {
 134  		isDigit := c >= '0' && c <= '9'
 135  		if !isDigit && c != '.' {
 136  			return errInvalidOID
 137  		}
 138  	}
 139  
 140  	var (
 141  		firstNum  string
 142  		secondNum string
 143  	)
 144  
 145  	var nextComponentExists bool
 146  	firstNum, oid, nextComponentExists = bytes.Cut(oid, ".")
 147  	if !nextComponentExists {
 148  		return errInvalidOID
 149  	}
 150  	secondNum, oid, nextComponentExists = bytes.Cut(oid, ".")
 151  
 152  	var (
 153  		first  = big.NewInt(0)
 154  		second = big.NewInt(0)
 155  	)
 156  
 157  	if _, ok := first.SetString(firstNum, 10); !ok {
 158  		return errInvalidOID
 159  	}
 160  	if _, ok := second.SetString(secondNum, 10); !ok {
 161  		return errInvalidOID
 162  	}
 163  
 164  	if first.Cmp(big.NewInt(2)) > 0 || (first.Cmp(big.NewInt(2)) < 0 && second.Cmp(big.NewInt(40)) >= 0) {
 165  		return errInvalidOID
 166  	}
 167  
 168  	firstComponent := first.Mul(first, big.NewInt(40))
 169  	firstComponent.Add(firstComponent, second)
 170  
 171  	der := appendBase128BigInt([]byte{:0:32}, firstComponent)
 172  
 173  	for nextComponentExists {
 174  		var strNum string
 175  		strNum, oid, nextComponentExists = bytes.Cut(oid, ".")
 176  		b, ok := big.NewInt(0).SetString(strNum, 10)
 177  		if !ok {
 178  			return errInvalidOID
 179  		}
 180  		der = appendBase128BigInt(der, b)
 181  	}
 182  
 183  	o.der = der
 184  	return nil
 185  }
 186  
 187  // AppendBinary implements [encoding.BinaryAppender]
 188  func (o OID) AppendBinary(b []byte) ([]byte, error) {
 189  	return append(b, o.der...), nil
 190  }
 191  
 192  // MarshalBinary implements [encoding.BinaryMarshaler]
 193  func (o OID) MarshalBinary() ([]byte, error) {
 194  	return o.AppendBinary(nil)
 195  }
 196  
 197  // UnmarshalBinary implements [encoding.BinaryUnmarshaler]
 198  func (o *OID) UnmarshalBinary(b []byte) error {
 199  	oid, ok := newOIDFromDER(bytes.Clone(b))
 200  	if !ok {
 201  		return errInvalidOID
 202  	}
 203  	*o = oid
 204  	return nil
 205  }
 206  
 207  // Equal returns true when oid and other represents the same Object Identifier.
 208  func (oid OID) Equal(other OID) bool {
 209  	// There is only one possible DER encoding of
 210  	// each unique Object Identifier.
 211  	return bytes.Equal(oid.der, other.der)
 212  }
 213  
 214  func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, failed bool) {
 215  	offset = initOffset
 216  	var ret64 int64
 217  	for shifted := 0; offset < len(bytes); shifted++ {
 218  		// 5 * 7 bits per byte == 35 bits of data
 219  		// Thus the representation is either non-minimal or too large for an int32
 220  		if shifted == 5 {
 221  			failed = true
 222  			return
 223  		}
 224  		ret64 <<= 7
 225  		b := bytes[offset]
 226  		// integers should be minimally encoded, so the leading octet should
 227  		// never be 0x80
 228  		if shifted == 0 && b == 0x80 {
 229  			failed = true
 230  			return
 231  		}
 232  		ret64 |= int64(b & 0x7f)
 233  		offset++
 234  		if b&0x80 == 0 {
 235  			ret = int(ret64)
 236  			// Ensure that the returned value fits in an int on all platforms
 237  			if ret64 > math.MaxInt32 {
 238  				failed = true
 239  			}
 240  			return
 241  		}
 242  	}
 243  	failed = true
 244  	return
 245  }
 246  
 247  // EqualASN1OID returns whether an OID equals an asn1.ObjectIdentifier. If
 248  // asn1.ObjectIdentifier cannot represent the OID specified by oid, because
 249  // a component of OID requires more than 31 bits, it returns false.
 250  func (oid OID) EqualASN1OID(other asn1.ObjectIdentifier) bool {
 251  	if len(other) < 2 {
 252  		return false
 253  	}
 254  	v, offset, failed := parseBase128Int(oid.der, 0)
 255  	if failed {
 256  		// This should never happen, since we've already parsed the OID,
 257  		// but just in case.
 258  		return false
 259  	}
 260  	if v < 80 {
 261  		a, b := v/40, v%40
 262  		if other[0] != a || other[1] != b {
 263  			return false
 264  		}
 265  	} else {
 266  		a, b := 2, v-80
 267  		if other[0] != a || other[1] != b {
 268  			return false
 269  		}
 270  	}
 271  
 272  	i := 2
 273  	for ; offset < len(oid.der); i++ {
 274  		v, offset, failed = parseBase128Int(oid.der, offset)
 275  		if failed {
 276  			// Again, shouldn't happen, since we've already parsed
 277  			// the OID, but better safe than sorry.
 278  			return false
 279  		}
 280  		if i >= len(other) || v != other[i] {
 281  			return false
 282  		}
 283  	}
 284  
 285  	return i == len(other)
 286  }
 287  
 288  // Strings returns the string representation of the Object Identifier.
 289  func (oid OID) String() string {
 290  	var b bytes.Buffer
 291  	b.Grow(32)
 292  	const (
 293  		valSize         = 64 // size in bits of val.
 294  		bitsPerByte     = 7
 295  		maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
 296  	)
 297  	var (
 298  		start    = 0
 299  		val      = uint64(0)
 300  		numBuf   = []byte{:0:21}
 301  		bigVal   *big.Int
 302  		overflow bool
 303  	)
 304  	for i, v := range oid.der {
 305  		curVal := v & 0x7F
 306  		valEnd := v&0x80 == 0
 307  		if valEnd {
 308  			if start != 0 {
 309  				b.WriteByte('.')
 310  			}
 311  		}
 312  		if !overflow && val > maxValSafeShift {
 313  			if bigVal == nil {
 314  				bigVal = &big.Int{}
 315  			}
 316  			bigVal = bigVal.SetUint64(val)
 317  			overflow = true
 318  		}
 319  		if overflow {
 320  			bigVal = bigVal.Lsh(bigVal, bitsPerByte).Or(bigVal, big.NewInt(int64(curVal)))
 321  			if valEnd {
 322  				if start == 0 {
 323  					b.WriteString("2.")
 324  					bigVal = bigVal.Sub(bigVal, big.NewInt(80))
 325  				}
 326  				numBuf = bigVal.Append(numBuf, 10)
 327  				b.Write(numBuf)
 328  				numBuf = numBuf[:0]
 329  				val = 0
 330  				start = i + 1
 331  				overflow = false
 332  			}
 333  			continue
 334  		}
 335  		val <<= bitsPerByte
 336  		val |= uint64(curVal)
 337  		if valEnd {
 338  			if start == 0 {
 339  				if val < 80 {
 340  					b.Write(strconv.AppendUint(numBuf, val/40, 10))
 341  					b.WriteByte('.')
 342  					b.Write(strconv.AppendUint(numBuf, val%40, 10))
 343  				} else {
 344  					b.WriteString("2.")
 345  					b.Write(strconv.AppendUint(numBuf, val-80, 10))
 346  				}
 347  			} else {
 348  				b.Write(strconv.AppendUint(numBuf, val, 10))
 349  			}
 350  			val = 0
 351  			start = i + 1
 352  		}
 353  	}
 354  	return b.String()
 355  }
 356  
 357  func (oid OID) toASN1OID() (asn1.ObjectIdentifier, bool) {
 358  	out := []int{:0:len(oid.der)+1}
 359  
 360  	const (
 361  		valSize         = 31 // amount of usable bits of val for OIDs.
 362  		bitsPerByte     = 7
 363  		maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
 364  	)
 365  
 366  	val := 0
 367  
 368  	for _, v := range oid.der {
 369  		if val > maxValSafeShift {
 370  			return nil, false
 371  		}
 372  
 373  		val <<= bitsPerByte
 374  		val |= int(v & 0x7F)
 375  
 376  		if v&0x80 == 0 {
 377  			if len(out) == 0 {
 378  				if val < 80 {
 379  					out = append(out, val/40)
 380  					out = append(out, val%40)
 381  				} else {
 382  					out = append(out, 2)
 383  					out = append(out, val-80)
 384  				}
 385  				val = 0
 386  				continue
 387  			}
 388  			out = append(out, val)
 389  			val = 0
 390  		}
 391  	}
 392  
 393  	return out, true
 394  }
 395