// 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 ( "encoding" "encoding/asn1" "math" "testing" ) var oidTests = []struct { raw []byte valid bool str string ints []uint64 }{ {[]byte{}, false, "", nil}, {[]byte{0x80, 0x01}, false, "", nil}, {[]byte{0x01, 0x80, 0x01}, false, "", nil}, {[]byte{1, 2, 3}, true, "0.1.2.3", []uint64{0, 1, 2, 3}}, {[]byte{41, 2, 3}, true, "1.1.2.3", []uint64{1, 1, 2, 3}}, {[]byte{86, 2, 3}, true, "2.6.2.3", []uint64{2, 6, 2, 3}}, {[]byte{41, 255, 255, 255, 127}, true, "1.1.268435455", []uint64{1, 1, 268435455}}, {[]byte{41, 0x87, 255, 255, 255, 127}, true, "1.1.2147483647", []uint64{1, 1, 2147483647}}, {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", []uint64{1, 1, 34359738367}}, {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", []uint64{1, 2, 9223372036854775807}}, {[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615", []uint64{1, 3, 18446744073709551615}}, {[]byte{44, 0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.4.36893488147419103231", nil}, {[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.1180591620717411303423", nil}, {[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.19342813113834066795298815", nil}, {[]byte{255, 255, 255, 127}, true, "2.268435375", []uint64{2, 268435375}}, {[]byte{0x87, 255, 255, 255, 127}, true, "2.2147483567", []uint64{2, 2147483567}}, {[]byte{255, 127}, true, "2.16303", []uint64{2, 16303}}, {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", []uint64{2, 34359738287}}, {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", []uint64{2, 9223372036854775727}}, {[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535", []uint64{2, 18446744073709551535}}, {[]byte{0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.36893488147419103151", nil}, {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.1180591620717411303343", nil}, {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.19342813113834066795298735", nil}, {[]byte{41, 0x80 | 66, 0x80 | 44, 0x80 | 11, 33}, true, "1.1.139134369", []uint64{1, 1, 139134369}}, {[]byte{0x80 | 66, 0x80 | 44, 0x80 | 11, 33}, true, "2.139134289", []uint64{2, 139134289}}, } func TestOID(t *testing.T) { for _, v := range oidTests { oid, ok := newOIDFromDER(v.raw) if ok != v.valid { t.Errorf("newOIDFromDER(%v) = (%v, %v); want = (OID, %v)", v.raw, oid, ok, v.valid) continue } if !ok { continue } if str := oid.String(); str != v.str { t.Errorf("(%#v).String() = %v, want; %v", oid, str, v.str) } var asn1OID asn1.ObjectIdentifier for _, v := range v.ints { if v > math.MaxInt32 { asn1OID = nil break } asn1OID = append(asn1OID, int(v)) } o, ok := oid.toASN1OID() if shouldOk := asn1OID != nil; shouldOk != ok { t.Errorf("(%#v).toASN1OID() = (%v, %v); want = (%v, %v)", oid, o, ok, asn1OID, shouldOk) continue } if asn1OID != nil && !o.Equal(asn1OID) { t.Errorf("(%#v).toASN1OID() = (%v, true); want = (%v, true)", oid, o, asn1OID) } if v.ints != nil { oid2, err := OIDFromInts(v.ints) if err != nil { t.Errorf("OIDFromInts(%v) = (%v, %v); want = (%v, nil)", v.ints, oid2, err, oid) } if !oid2.Equal(oid) { t.Errorf("OIDFromInts(%v) = (%v, nil); want = (%v, nil)", v.ints, oid2, oid) } } } } func TestInvalidOID(t *testing.T) { cases := []struct { str string ints []uint64 }{ {str: "", ints: []uint64{}}, {str: "1", ints: []uint64{1}}, {str: "3", ints: []uint64{3}}, {str: "3.100.200", ints: []uint64{3, 100, 200}}, {str: "1.81", ints: []uint64{1, 81}}, {str: "1.81.200", ints: []uint64{1, 81, 200}}, } for _, tt := range cases { oid, err := OIDFromInts(tt.ints) if err == nil { t.Errorf("OIDFromInts(%v) = (%v, %v); want = (OID{}, %v)", tt.ints, oid, err, errInvalidOID) } oid2, err := ParseOID(tt.str) if err == nil { t.Errorf("ParseOID(%v) = (%v, %v); want = (OID{}, %v)", tt.str, oid2, err, errInvalidOID) } var oid3 OID err = oid3.UnmarshalText([]byte(tt.str)) if err == nil { t.Errorf("(*OID).UnmarshalText(%v) = (%v, %v); want = (OID{}, %v)", tt.str, oid3, err, errInvalidOID) } } } func TestOIDEqual(t *testing.T) { var cases = []struct { oid OID oid2 OID eq bool }{ {oid: mustNewOIDFromInts(t, []uint64{1, 2, 3}), oid2: mustNewOIDFromInts(t, []uint64{1, 2, 3}), eq: true}, {oid: mustNewOIDFromInts(t, []uint64{1, 2, 3}), oid2: mustNewOIDFromInts(t, []uint64{1, 2, 4}), eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 2, 3}), oid2: mustNewOIDFromInts(t, []uint64{1, 2, 3, 4}), eq: false}, {oid: mustNewOIDFromInts(t, []uint64{2, 33, 22}), oid2: mustNewOIDFromInts(t, []uint64{2, 33, 23}), eq: false}, {oid: OID{}, oid2: OID{}, eq: true}, {oid: OID{}, oid2: mustNewOIDFromInts(t, []uint64{2, 33, 23}), eq: false}, } for _, tt := range cases { if eq := tt.oid.Equal(tt.oid2); eq != tt.eq { t.Errorf("(%v).Equal(%v) = %v, want %v", tt.oid, tt.oid2, eq, tt.eq) } } } var ( _ encoding.BinaryMarshaler = OID{} _ encoding.BinaryUnmarshaler = new(OID) _ encoding.TextMarshaler = OID{} _ encoding.TextUnmarshaler = new(OID) ) func TestOIDMarshal(t *testing.T) { cases := []struct { in string out OID err error }{ {in: "", err: errInvalidOID}, {in: "0", err: errInvalidOID}, {in: "1", err: errInvalidOID}, {in: ".1", err: errInvalidOID}, {in: ".1.", err: errInvalidOID}, {in: "1.", err: errInvalidOID}, {in: "1..", err: errInvalidOID}, {in: "1.2.", err: errInvalidOID}, {in: "1.2.333.", err: errInvalidOID}, {in: "1.2.333..", err: errInvalidOID}, {in: "1.2..", err: errInvalidOID}, {in: "+1.2", err: errInvalidOID}, {in: "-1.2", err: errInvalidOID}, {in: "1.-2", err: errInvalidOID}, {in: "1.2.+333", err: errInvalidOID}, } for _, v := range oidTests { oid, ok := newOIDFromDER(v.raw) if !ok { continue } cases = append(cases, struct { in string out OID err error }{ in: v.str, out: oid, err: nil, }) } for _, tt := range cases { o, err := ParseOID(tt.in) if err != tt.err { t.Errorf("ParseOID(%q) = %v; want = %v", tt.in, err, tt.err) continue } var o2 OID err = o2.UnmarshalText([]byte(tt.in)) if err != tt.err { t.Errorf("(*OID).UnmarshalText(%q) = %v; want = %v", tt.in, err, tt.err) continue } if err != nil { continue } if !o.Equal(tt.out) { t.Errorf("(*OID).UnmarshalText(%q) = %v; want = %v", tt.in, o, tt.out) continue } if !o2.Equal(tt.out) { t.Errorf("ParseOID(%q) = %v; want = %v", tt.in, o2, tt.out) continue } marshalled, err := o.MarshalText() if string(marshalled) != tt.in || err != nil { t.Errorf("(%#v).MarshalText() = (%v, %v); want = (%v, nil)", o, string(marshalled), err, tt.in) continue } textAppend := make([]byte, 4) textAppend, err = o.AppendText(textAppend) textAppend = textAppend[4:] if string(textAppend) != tt.in || err != nil { t.Errorf("(%#v).AppendText() = (%v, %v); want = (%v, nil)", o, string(textAppend), err, tt.in) continue } binary, err := o.MarshalBinary() if err != nil { t.Errorf("(%#v).MarshalBinary() = %v; want = nil", o, err) } var o3 OID if err := o3.UnmarshalBinary(binary); err != nil { t.Errorf("(*OID).UnmarshalBinary(%v) = %v; want = nil", binary, err) } if !o3.Equal(tt.out) { t.Errorf("(*OID).UnmarshalBinary(%v) = %v; want = %v", binary, o3, tt.out) continue } binaryAppend := make([]byte, 4) binaryAppend, err = o.AppendBinary(binaryAppend) binaryAppend = binaryAppend[4:] if err != nil { t.Errorf("(%#v).AppendBinary() = %v; want = nil", o, err) } var o4 OID if err := o4.UnmarshalBinary(binaryAppend); err != nil { t.Errorf("(*OID).UnmarshalBinary(%v) = %v; want = nil", binaryAppend, err) } if !o4.Equal(tt.out) { t.Errorf("(*OID).UnmarshalBinary(%v) = %v; want = %v", binaryAppend, o4, tt.out) continue } } } func TestOIDEqualASN1OID(t *testing.T) { maxInt32PlusOne := int64(math.MaxInt32) + 1 var cases = []struct { oid OID oid2 asn1.ObjectIdentifier eq bool }{ {oid: mustNewOIDFromInts(t, []uint64{1, 2, 3}), oid2: asn1.ObjectIdentifier{1, 2, 3}, eq: true}, {oid: mustNewOIDFromInts(t, []uint64{1, 2, 3}), oid2: asn1.ObjectIdentifier{1, 2, 4}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 2, 3}), oid2: asn1.ObjectIdentifier{1, 2, 3, 4}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 22}), oid2: asn1.ObjectIdentifier{1, 33, 23}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 23}), oid2: asn1.ObjectIdentifier{1, 33, 22}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 127}), oid2: asn1.ObjectIdentifier{1, 33, 127}, eq: true}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 128}), oid2: asn1.ObjectIdentifier{1, 33, 127}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 128}), oid2: asn1.ObjectIdentifier{1, 33, 128}, eq: true}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 129}), oid2: asn1.ObjectIdentifier{1, 33, 129}, eq: true}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 128}), oid2: asn1.ObjectIdentifier{1, 33, 129}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 129}), oid2: asn1.ObjectIdentifier{1, 33, 128}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 255}), oid2: asn1.ObjectIdentifier{1, 33, 255}, eq: true}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 256}), oid2: asn1.ObjectIdentifier{1, 33, 256}, eq: true}, {oid: mustNewOIDFromInts(t, []uint64{2, 33, 257}), oid2: asn1.ObjectIdentifier{2, 33, 256}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{2, 33, 256}), oid2: asn1.ObjectIdentifier{2, 33, 257}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33}), oid2: asn1.ObjectIdentifier{1, 33, math.MaxInt32}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, math.MaxInt32}), oid2: asn1.ObjectIdentifier{1, 33}, eq: false}, {oid: mustNewOIDFromInts(t, []uint64{1, 33, math.MaxInt32}), oid2: asn1.ObjectIdentifier{1, 33, math.MaxInt32}, eq: true}, { oid: mustNewOIDFromInts(t, []uint64{1, 33, math.MaxInt32 + 1}), oid2: asn1.ObjectIdentifier{1, 33 /*convert to int, so that it compiles on 32bit*/, int(maxInt32PlusOne)}, eq: false, }, {oid: mustNewOIDFromInts(t, []uint64{1, 33, 256}), oid2: asn1.ObjectIdentifier{}, eq: false}, {oid: OID{}, oid2: asn1.ObjectIdentifier{1, 33, 256}, eq: false}, {oid: OID{}, oid2: asn1.ObjectIdentifier{}, eq: false}, } for _, tt := range cases { if eq := tt.oid.EqualASN1OID(tt.oid2); eq != tt.eq { t.Errorf("(%v).EqualASN1OID(%v) = %v, want %v", tt.oid, tt.oid2, eq, tt.eq) } } } func TestOIDUnmarshalBinary(t *testing.T) { for _, tt := range oidTests { var o OID err := o.UnmarshalBinary(tt.raw) expectErr := errInvalidOID if tt.valid { expectErr = nil } if err != expectErr { t.Errorf("(o *OID).UnmarshalBinary(%v) = %v; want = %v; (o = %v)", tt.raw, err, expectErr, o) } } } func BenchmarkOIDMarshalUnmarshalText(b *testing.B) { oid := mustNewOIDFromInts(b, []uint64{1, 2, 3, 9999, 1024}) for range b.N { text, err := oid.MarshalText() if err != nil { b.Fatal(err) } var o OID if err := o.UnmarshalText(text); err != nil { b.Fatal(err) } } } func mustNewOIDFromInts(t testing.TB, ints []uint64) OID { t.Helper() oid, err := OIDFromInts(ints) if err != nil { t.Fatalf("OIDFromInts(%v) unexpected error: %v", ints, err) } return oid }