// 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 (
	
	
	
	
	
	
	
	
)

var (
	errInvalidOID = errors.New("invalid oid")
)

// An OID represents an ASN.1 OBJECT IDENTIFIER.
type OID struct {
	der []byte
}

// ParseOID parses a Object Identifier string, represented by ASCII numbers separated by dots.
func ( string) (OID, error) {
	var  OID
	return , .unmarshalOIDText()
}

func newOIDFromDER( []byte) (OID, bool) {
	if len() == 0 || [len()-1]&0x80 != 0 {
		return OID{}, false
	}

	 := 0
	for ,  := range  {
		// 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  ==  &&  == 0x80 {
			return OID{}, false
		}
		if &0x80 == 0 {
			 =  + 1
		}
	}

	return OID{}, true
}

// OIDFromInts creates a new OID using ints, each integer is a separate component.
func ( []uint64) (OID, error) {
	if len() < 2 || [0] > 2 || ([0] < 2 && [1] >= 40) {
		return OID{}, errInvalidOID
	}

	 := base128IntLength([0]*40 + [1])
	for ,  := range [2:] {
		 += base128IntLength()
	}

	 := make([]byte, 0, )
	 = appendBase128Int(, [0]*40+[1])
	for ,  := range [2:] {
		 = appendBase128Int(, )
	}
	return OID{}, nil
}

func base128IntLength( uint64) int {
	if  == 0 {
		return 1
	}
	return (bits.Len64() + 6) / 7
}

func appendBase128Int( []byte,  uint64) []byte {
	for  := base128IntLength() - 1;  >= 0; -- {
		 := byte( >> uint(*7))
		 &= 0x7f
		if  != 0 {
			 |= 0x80
		}
		 = append(, )
	}
	return 
}

func base128BigIntLength( *big.Int) int {
	if .Cmp(big.NewInt(0)) == 0 {
		return 1
	}
	return (.BitLen() + 6) / 7
}

func appendBase128BigInt( []byte,  *big.Int) []byte {
	if .Cmp(big.NewInt(0)) == 0 {
		return append(, 0)
	}

	for  := base128BigIntLength() - 1;  >= 0; -- {
		 := byte(big.NewInt(0).Rsh(, uint()*7).Bits()[0])
		 &= 0x7f
		if  != 0 {
			 |= 0x80
		}
		 = append(, )
	}
	return 
}

// MarshalText implements [encoding.TextMarshaler]
func ( OID) () ([]byte, error) {
	return []byte(.String()), nil
}

// UnmarshalText implements [encoding.TextUnmarshaler]
func ( *OID) ( []byte) error {
	return .unmarshalOIDText(string())
}

func ( *OID) ( string) error {
	// (*big.Int).SetString allows +/- signs, but we don't want
	// to allow them in the string representation of Object Identifier, so
	// reject such encodings.
	for ,  := range  {
		 :=  >= '0' &&  <= '9'
		if ! &&  != '.' {
			return errInvalidOID
		}
	}

	var (
		  string
		 string
	)

	var  bool
	, ,  = strings.Cut(, ".")
	if ! {
		return errInvalidOID
	}
	, ,  = strings.Cut(, ".")

	var (
		  = big.NewInt(0)
		 = big.NewInt(0)
	)

	if ,  := .SetString(, 10); ! {
		return errInvalidOID
	}
	if ,  := .SetString(, 10); ! {
		return errInvalidOID
	}

	if .Cmp(big.NewInt(2)) > 0 || (.Cmp(big.NewInt(2)) < 0 && .Cmp(big.NewInt(40)) >= 0) {
		return errInvalidOID
	}

	 := .Mul(, big.NewInt(40))
	.Add(, )

	 := appendBase128BigInt(make([]byte, 0, 32), )

	for  {
		var  string
		, ,  = strings.Cut(, ".")
		,  := big.NewInt(0).SetString(, 10)
		if ! {
			return errInvalidOID
		}
		 = appendBase128BigInt(, )
	}

	.der = 
	return nil
}

// MarshalBinary implements [encoding.BinaryMarshaler]
func ( OID) () ([]byte, error) {
	return bytes.Clone(.der), nil
}

// UnmarshalBinary implements [encoding.BinaryUnmarshaler]
func ( *OID) ( []byte) error {
	,  := newOIDFromDER(bytes.Clone())
	if ! {
		return errInvalidOID
	}
	* = 
	return nil
}

// Equal returns true when oid and other represents the same Object Identifier.
func ( OID) ( OID) bool {
	// There is only one possible DER encoding of
	// each unique Object Identifier.
	return bytes.Equal(.der, .der)
}

func parseBase128Int( []byte,  int) (,  int,  bool) {
	 = 
	var  int64
	for  := 0;  < len(); ++ {
		// 5 * 7 bits per byte == 35 bits of data
		// Thus the representation is either non-minimal or too large for an int32
		if  == 5 {
			 = true
			return
		}
		 <<= 7
		 := []
		// integers should be minimally encoded, so the leading octet should
		// never be 0x80
		if  == 0 &&  == 0x80 {
			 = true
			return
		}
		 |= int64( & 0x7f)
		++
		if &0x80 == 0 {
			 = int()
			// Ensure that the returned value fits in an int on all platforms
			if  > math.MaxInt32 {
				 = true
			}
			return
		}
	}
	 = 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) ( asn1.ObjectIdentifier) bool {
	if len() < 2 {
		return false
	}
	, ,  := parseBase128Int(.der, 0)
	if  {
		// This should never happen, since we've already parsed the OID,
		// but just in case.
		return false
	}
	if  < 80 {
		,  := /40, %40
		if [0] !=  || [1] !=  {
			return false
		}
	} else {
		,  := 2, -80
		if [0] !=  || [1] !=  {
			return false
		}
	}

	 := 2
	for ;  < len(.der); ++ {
		, ,  = parseBase128Int(.der, )
		if  {
			// Again, shouldn't happen, since we've already parsed
			// the OID, but better safe than sorry.
			return false
		}
		if  >= len() ||  != [] {
			return false
		}
	}

	return  == len()
}

// Strings returns the string representation of the Object Identifier.
func ( OID) () string {
	var  strings.Builder
	.Grow(32)
	const (
		         = 64 // size in bits of val.
		     = 7
		 = (1 << ( - )) - 1
	)
	var (
		    = 0
		      = uint64(0)
		   = make([]byte, 0, 21)
		   *big.Int
		 bool
	)
	for ,  := range .der {
		 :=  & 0x7F
		 := &0x80 == 0
		if  {
			if  != 0 {
				.WriteByte('.')
			}
		}
		if ! &&  >  {
			if  == nil {
				 = new(big.Int)
			}
			 = .SetUint64()
			 = true
		}
		if  {
			 = .Lsh(, ).Or(, big.NewInt(int64()))
			if  {
				if  == 0 {
					.WriteString("2.")
					 = .Sub(, big.NewInt(80))
				}
				 = .Append(, 10)
				.Write()
				 = [:0]
				 = 0
				 =  + 1
				 = false
			}
			continue
		}
		 <<= 
		 |= uint64()
		if  {
			if  == 0 {
				if  < 80 {
					.Write(strconv.AppendUint(, /40, 10))
					.WriteByte('.')
					.Write(strconv.AppendUint(, %40, 10))
				} else {
					.WriteString("2.")
					.Write(strconv.AppendUint(, -80, 10))
				}
			} else {
				.Write(strconv.AppendUint(, , 10))
			}
			 = 0
			 =  + 1
		}
	}
	return .String()
}

func ( OID) () (asn1.ObjectIdentifier, bool) {
	 := make([]int, 0, len(.der)+1)

	const (
		         = 31 // amount of usable bits of val for OIDs.
		     = 7
		 = (1 << ( - )) - 1
	)

	 := 0

	for ,  := range .der {
		if  >  {
			return nil, false
		}

		 <<= 
		 |= int( & 0x7F)

		if &0x80 == 0 {
			if len() == 0 {
				if  < 80 {
					 = append(, /40)
					 = append(, %40)
				} else {
					 = append(, 2)
					 = append(, -80)
				}
				 = 0
				continue
			}
			 = append(, )
			 = 0
		}
	}

	return , true
}