// 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 x509import ()var ( errInvalidOID = errors.New("invalid oid"))// An OID represents an ASN.1 OBJECT IDENTIFIER.typeOIDstruct { der []byte}// ParseOID parses a Object Identifier string, represented by ASCII numbers separated by dots.func ( string) (OID, error) {varOIDreturn , .unmarshalOIDText()}func newOIDFromDER( []byte) (OID, bool) {iflen() == 0 || [len()-1]&0x80 != 0 {returnOID{}, false } := 0for , := 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 {returnOID{}, false }if &0x80 == 0 { = + 1 } }returnOID{}, true}// OIDFromInts creates a new OID using ints, each integer is a separate component.func ( []uint64) (OID, error) {iflen() < 2 || [0] > 2 || ([0] < 2 && [1] >= 40) {returnOID{}, errInvalidOID } := base128IntLength([0]*40 + [1])for , := range [2:] { += base128IntLength() } := make([]byte, 0, ) = appendBase128Int(, [0]*40+[1])for , := range [2:] { = appendBase128Int(, ) }returnOID{}, nil}func base128IntLength( uint64) int {if == 0 {return1 }return (bits.Len64() + 6) / 7}func appendBase128Int( []byte, uint64) []byte {for := base128IntLength() - 1; >= 0; -- { := byte( >> uint(*7)) &= 0x7fif != 0 { |= 0x80 } = append(, ) }return}func base128BigIntLength( *big.Int) int {if .Cmp(big.NewInt(0)) == 0 {return1 }return (.BitLen() + 6) / 7}func appendBase128BigInt( []byte, *big.Int) []byte {if .Cmp(big.NewInt(0)) == 0 {returnappend(, 0) }for := base128BigIntLength() - 1; >= 0; -- { := byte(big.NewInt(0).Rsh(, uint()*7).Bits()[0]) &= 0x7fif != 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 ! && != '.' {returnerrInvalidOID } }var (stringstring )varbool , , = strings.Cut(, ".")if ! {returnerrInvalidOID } , , = strings.Cut(, ".")var ( = big.NewInt(0) = big.NewInt(0) )if , := .SetString(, 10); ! {returnerrInvalidOID }if , := .SetString(, 10); ! {returnerrInvalidOID }if .Cmp(big.NewInt(2)) > 0 || (.Cmp(big.NewInt(2)) < 0 && .Cmp(big.NewInt(40)) >= 0) {returnerrInvalidOID } := .Mul(, big.NewInt(40)) .Add(, ) := appendBase128BigInt(make([]byte, 0, 32), )for {varstring , , = strings.Cut(, ".") , := big.NewInt(0).SetString(, 10)if ! {returnerrInvalidOID } = appendBase128BigInt(, ) } .der = returnnil}// MarshalBinary implements [encoding.BinaryMarshaler]func ( OID) () ([]byte, error) {returnbytes.Clone(.der), nil}// UnmarshalBinary implements [encoding.BinaryUnmarshaler]func ( *OID) ( []byte) error { , := newOIDFromDER(bytes.Clone())if ! {returnerrInvalidOID } * = returnnil}// 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.returnbytes.Equal(.der, .der)}func parseBase128Int( []byte, int) (, int, bool) { = varint64for := 0; < len(); ++ {// 5 * 7 bits per byte == 35 bits of data // Thus the representation is either non-minimal or too large for an int32if == 5 { = truereturn } <<= 7 := []// integers should be minimally encoded, so the leading octet should // never be 0x80if == 0 && == 0x80 { = truereturn } |= int64( & 0x7f) ++if &0x80 == 0 { = int()// Ensure that the returned value fits in an int on all platformsif > math.MaxInt32 { = true }return } } = truereturn}// 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 {iflen() < 2 {returnfalse } , , := parseBase128Int(.der, 0)if {// This should never happen, since we've already parsed the OID, // but just in case.returnfalse }if < 80 { , := /40, %40if [0] != || [1] != {returnfalse } } else { , := 2, -80if [0] != || [1] != {returnfalse } } := 2for ; < len(.der); ++ { , , = parseBase128Int(.der, )if {// Again, shouldn't happen, since we've already parsed // the OID, but better safe than sorry.returnfalse }if >= len() || != [] {returnfalse } }return == len()}// Strings returns the string representation of the Object Identifier.func ( OID) () string {varstrings.Builder .Grow(32)const ( = 64// size in bits of val. = 7 = (1 << ( - )) - 1 )var ( = 0 = uint64(0) = make([]byte, 0, 21) *big.Intbool )for , := range .der { := & 0x7F := &0x80 == 0if {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 ) := 0for , := range .der {if > {returnnil, false } <<= |= int( & 0x7F)if &0x80 == 0 {iflen() == 0 {if < 80 { = append(, /40) = append(, %40) } else { = append(, 2) = append(, -80) } = 0continue } = append(, ) = 0 } }return , true}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.