From 27adae0dbbd5d215b3de8e1045d0f22d87254af8 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 09:03:30 +0200 Subject: [PATCH 01/16] add OID type, String(), Equal() --- src/encoding/asn1/asn1.go | 112 +++++++++++++++++++++++++++++++++ src/encoding/asn1/asn1_test.go | 31 +++++++++ 2 files changed, 143 insertions(+) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index e7bf793a8272e6..d8d3a45a5c093c 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -20,6 +20,7 @@ package asn1 // everything by any means. import ( + "bytes" "errors" "fmt" "math" @@ -292,6 +293,117 @@ func parseObjectIdentifier(bytes []byte) (s ObjectIdentifier, err error) { return } +var errInvalidDEROid = errors.New("invalid DER Object Identifier encoding") + +type OID struct { + der []byte +} + +// ParseOID parsed DER-encoded Object Identifier. +// On success, der is referenced in the OID struct. +func ParseOID(der []byte) (OID, error) { + if !isDEROIDValid(der) { + return OID{}, errInvalidDEROid + } + return OID{der}, nil +} + +func isDEROIDValid(der []byte) bool { + if len(der) == 0 || der[len(der)-1]&0x80 != 0 { + return 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 false + } + if v&0x80 == 0 { + start = i + 1 + } + } + + return start != 0 +} + +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 (oid OID) String() string { + var b strings.Builder + b.Grow(32) + + var ( + start = 0 + val = uint64(0) + numBuf = make([]byte, 0, 21) + bigVal *big.Int + ) + + for i, v := range oid.der { + if i-start >= 64/7 { + if i-start == 64/7 { + bigVal = new(big.Int).SetUint64(val) + } + + bigVal = bigVal.Lsh(bigVal, 7).Or(bigVal, big.NewInt(int64(v&0b11111111))) + + if v&0b10000000 == 0 { + if start != 0 { + b.WriteByte('.') + } + if start == 0 { + b.Write(strconv.AppendUint(numBuf, 2, 10)) + b.WriteByte('.') + bigVal = bigVal.Sub(bigVal, big.NewInt(80)) + } + + b.WriteString(bigVal.String()) + val = 0 + start = i + 1 + } + continue + } + + val <<= 7 + val |= uint64(v & 0b01111111) + + if v&0b10000000 == 0 { + if start != 0 { + b.WriteByte('.') + } + + if start == 0 { + var val1, val2 uint64 + if val < 80 { + val1 = val / 40 + val2 = val % 40 + } else { + val1 = 2 + val2 = val - 80 + } + + b.Write(strconv.AppendUint(numBuf, val1, 10)) + b.WriteByte('.') + b.Write(strconv.AppendUint(numBuf, val2, 10)) + } else { + b.Write(strconv.AppendUint(numBuf, val, 10)) + } + + val = 0 + start = i + 1 + } + } + + return b.String() +} + // ENUMERATED // An Enumerated is represented as a plain int. diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index 9a605e245c1369..aa094edbec4003 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1175,3 +1175,34 @@ func BenchmarkObjectIdentifierString(b *testing.B) { _ = oidPublicKeyRSA.String() } } + +var testOIDs = []struct { + raw []byte + valid bool + str string +}{ + {[]byte{}, false, ""}, + {[]byte{1, 2, 3}, true, "0.1.2.3"}, + {[]byte{41, 2, 3}, true, "1.1.2.3"}, + {[]byte{86, 2, 3}, true, "2.6.2.3"}, + {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367"}, + {[]byte{41, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.9223372036854775807"}, + {[]byte{41, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.1180591620717411303423"}, +} + +func TestOID(t *testing.T) { + for _, v := range testOIDs { + oid, err := ParseOID(v.raw) + if valid := err == nil; valid != v.valid { + if valid { + t.Errorf("%v: unexpected success while parsing", v.raw) + } else { + t.Errorf("%v: unexpected failure while parsing: %v", v.raw, err) + } + continue + } + if str := oid.String(); str != v.str { + t.Errorf("%#v: unexpected string, got: %q, expected: %q", v.raw, str, v.str) + } + } +} From 9aefa80dd10f5b77dfbaf86229ad2a84fb9e952d Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 09:19:06 +0200 Subject: [PATCH 02/16] add constants, use full 64bits --- src/encoding/asn1/asn1.go | 51 +++++++++++++++++++--------------- src/encoding/asn1/asn1_test.go | 1 + 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index d8d3a45a5c093c..81e2a1f8223c17 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -339,46 +339,55 @@ func (oid OID) String() string { var b strings.Builder b.Grow(32) + const ( + valSize = 64 + bitsPerByte = 7 + maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1 + ) + var ( - start = 0 - val = uint64(0) - numBuf = make([]byte, 0, 21) - bigVal *big.Int + start = 0 + val = uint64(0) + numBuf = make([]byte, 0, 21) + bigVal *big.Int + overflow bool ) for i, v := range oid.der { - if i-start >= 64/7 { - if i-start == 64/7 { - bigVal = new(big.Int).SetUint64(val) + if v&0b10000000 == 0 { + if start != 0 { + b.WriteByte('.') } + } - bigVal = bigVal.Lsh(bigVal, 7).Or(bigVal, big.NewInt(int64(v&0b11111111))) + if !overflow && val > maxValSafeShift { + bigVal = new(big.Int).SetUint64(val) + overflow = true + } - if v&0b10000000 == 0 { - if start != 0 { - b.WriteByte('.') - } + curVal := v & 0x7F + valEnd := v&0x80 == 0 + + if overflow { + bigVal = bigVal.Lsh(bigVal, bitsPerByte).Or(bigVal, big.NewInt(int64(curVal))) + if valEnd { if start == 0 { b.Write(strconv.AppendUint(numBuf, 2, 10)) b.WriteByte('.') bigVal = bigVal.Sub(bigVal, big.NewInt(80)) } - b.WriteString(bigVal.String()) val = 0 start = i + 1 + overflow = false } continue } - val <<= 7 - val |= uint64(v & 0b01111111) - - if v&0b10000000 == 0 { - if start != 0 { - b.WriteByte('.') - } + val <<= bitsPerByte + val |= uint64(curVal) + if valEnd { if start == 0 { var val1, val2 uint64 if val < 80 { @@ -388,14 +397,12 @@ func (oid OID) String() string { val1 = 2 val2 = val - 80 } - b.Write(strconv.AppendUint(numBuf, val1, 10)) b.WriteByte('.') b.Write(strconv.AppendUint(numBuf, val2, 10)) } else { b.Write(strconv.AppendUint(numBuf, val, 10)) } - val = 0 start = i + 1 } diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index aa094edbec4003..6ee6ae7af70121 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1187,6 +1187,7 @@ var testOIDs = []struct { {[]byte{86, 2, 3}, true, "2.6.2.3"}, {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367"}, {[]byte{41, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.9223372036854775807"}, + {[]byte{41, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.18446744073709551615"}, {[]byte{41, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.1180591620717411303423"}, } From 0958f1773de7500dd7191951bb3f0fbc9209f77e Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 09:39:47 +0200 Subject: [PATCH 03/16] more String() tests --- src/encoding/asn1/asn1.go | 8 ++++++-- src/encoding/asn1/asn1_test.go | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 81e2a1f8223c17..1965cc92f8b490 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -301,6 +301,7 @@ type OID struct { // ParseOID parsed DER-encoded Object Identifier. // On success, der is referenced in the OID struct. +// der must not be modified after parsing. func ParseOID(der []byte) (OID, error) { if !isDEROIDValid(der) { return OID{}, errInvalidDEROid @@ -340,7 +341,7 @@ func (oid OID) String() string { b.Grow(32) const ( - valSize = 64 + valSize = 64 // size in bits of val. bitsPerByte = 7 maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1 ) @@ -361,7 +362,10 @@ func (oid OID) String() string { } if !overflow && val > maxValSafeShift { - bigVal = new(big.Int).SetUint64(val) + if bigVal == nil { + bigVal = new(big.Int) + } + bigVal = bigVal.SetUint64(val) overflow = true } diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index 6ee6ae7af70121..32ffe4f1b9e6e7 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1185,10 +1185,21 @@ var testOIDs = []struct { {[]byte{1, 2, 3}, true, "0.1.2.3"}, {[]byte{41, 2, 3}, true, "1.1.2.3"}, {[]byte{86, 2, 3}, true, "2.6.2.3"}, + {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367"}, - {[]byte{41, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.9223372036854775807"}, - {[]byte{41, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.18446744073709551615"}, - {[]byte{41, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.1.1180591620717411303423"}, + {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807"}, + {[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615"}, + {[]byte{44, 0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.4.36893488147419103231"}, + {[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.1180591620717411303423"}, + {[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.19342813113834066795298815"}, + + {[]byte{255, 127}, true, "2.16303"}, + {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287"}, + {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727"}, + {[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535"}, + {[]byte{0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.36893488147419103151"}, + {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.1180591620717411303343"}, + {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.19342813113834066795298735"}, } func TestOID(t *testing.T) { From 24cdcb7a304f580033f22a69976b6968bfe71bff Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 10:31:59 +0200 Subject: [PATCH 04/16] add ToObjectIdentifer and EqualObjectIdentifer --- src/encoding/asn1/asn1.go | 69 ++++++++++++++++++++++++++++++++++ src/encoding/asn1/asn1_test.go | 60 +++++++++++++++++++---------- 2 files changed, 109 insertions(+), 20 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 1965cc92f8b490..8cda491b6c5d34 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -336,6 +336,75 @@ func (oid OID) Equal(other OID) bool { return bytes.Equal(oid.der, other.der) } +func (oid OID) ToObjectIdentifer() (ObjectIdentifier, bool) { + o, err := parseObjectIdentifier(oid.der) + if err != nil { + return nil, false + } + return o, true +} + +func (oid OID) EqualObjectIdentifer(other ObjectIdentifier) bool { + const ( + valSize = 31 + bitsPerByte = 7 + maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1 + ) + + var ( + val = 0 + first = true + ) + + for _, v := range oid.der { + if val >= maxValSafeShift { + return false + } + + val <<= bitsPerByte + val |= int(v & 0x7F) + if v&0x80 == 0 { + if first { + if len(other) < 2 { + return false + } + + var val1, val2 int + if val < 80 { + val1 = val / 40 + val2 = val % 40 + } else { + val1 = 2 + val2 = val - 80 + } + + if val1 != other[0] || val2 != other[1] { + return false + } + + val = 0 + first = false + other = other[2:] + continue + } + + if len(other) == 0 { + return false + } + + if val != other[0] { + return false + } + + val = 0 + other = other[1:] + + } + } + + return true +} + func (oid OID) String() string { var b strings.Builder b.Grow(32) diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index 32ffe4f1b9e6e7..e6c15183470124 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1180,26 +1180,31 @@ var testOIDs = []struct { raw []byte valid bool str string + oid ObjectIdentifier }{ - {[]byte{}, false, ""}, - {[]byte{1, 2, 3}, true, "0.1.2.3"}, - {[]byte{41, 2, 3}, true, "1.1.2.3"}, - {[]byte{86, 2, 3}, true, "2.6.2.3"}, - - {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367"}, - {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807"}, - {[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615"}, - {[]byte{44, 0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.4.36893488147419103231"}, - {[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.1180591620717411303423"}, - {[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.19342813113834066795298815"}, - - {[]byte{255, 127}, true, "2.16303"}, - {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287"}, - {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727"}, - {[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535"}, - {[]byte{0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.36893488147419103151"}, - {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.1180591620717411303343"}, - {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.19342813113834066795298735"}, + {[]byte{}, false, "", nil}, + {[]byte{1, 2, 3}, true, "0.1.2.3", []int{0, 1, 2, 3}}, + {[]byte{41, 2, 3}, true, "1.1.2.3", []int{1, 1, 2, 3}}, + {[]byte{86, 2, 3}, true, "2.6.2.3", []int{2, 6, 2, 3}}, + + {[]byte{41, 255, 255, 255, 127}, true, "1.1.268435455", []int{1, 1, 268435455}}, + {[]byte{41, 0x87, 255, 255, 255, 127}, true, "1.1.2147483647", []int{1, 1, 2147483647}}, + {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", nil}, + {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", nil}, + {[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615", nil}, + {[]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", []int{2, 268435375}}, + {[]byte{0x87, 255, 255, 255, 127}, true, "2.2147483567", []int{2, 2147483567}}, + {[]byte{255, 127}, true, "2.16303", []int{2, 16303}}, + {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", nil}, + {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", nil}, + {[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535", nil}, + {[]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}, } func TestOID(t *testing.T) { @@ -1214,7 +1219,22 @@ func TestOID(t *testing.T) { continue } if str := oid.String(); str != v.str { - t.Errorf("%#v: unexpected string, got: %q, expected: %q", v.raw, str, v.str) + t.Errorf("%v: unexpected string, got: %q, expected: %q", v.raw, str, v.str) } + + o, ok := oid.ToObjectIdentifer() + if shouldOk := v.oid != nil; shouldOk != ok { + if ok { + t.Errorf("%v: unexpected success while converting to ObjectIdentifier", v.raw) + } else { + t.Errorf("%v: unexpected failure while converting to ObjectIdentifier", v.raw) + } + continue + } + + if !o.Equal(v.oid) { + t.Errorf("%v: after ToObjectIdentifer, is not equal to %v", v.raw, v.oid) + } + } } From ebbb889930951cd00bcc75dd802a3bdd1b52fea0 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 10:41:35 +0200 Subject: [PATCH 05/16] fix EqualObjectIdentifier --- src/encoding/asn1/asn1.go | 3 ++- src/encoding/asn1/asn1_test.go | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 8cda491b6c5d34..c4c8796910bf0f 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -357,12 +357,13 @@ func (oid OID) EqualObjectIdentifer(other ObjectIdentifier) bool { ) for _, v := range oid.der { - if val >= maxValSafeShift { + if val > maxValSafeShift { return false } val <<= bitsPerByte val |= int(v & 0x7F) + if v&0x80 == 0 { if first { if len(other) < 2 { diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index e6c15183470124..f17d4c80fe09d3 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1222,6 +1222,10 @@ func TestOID(t *testing.T) { t.Errorf("%v: unexpected string, got: %q, expected: %q", v.raw, str, v.str) } + if v.oid != nil && !oid.EqualObjectIdentifer(v.oid) { + t.Errorf("%v: is not equal to %v", v.raw, v.oid) + } + o, ok := oid.ToObjectIdentifer() if shouldOk := v.oid != nil; shouldOk != ok { if ok { @@ -1232,9 +1236,8 @@ func TestOID(t *testing.T) { continue } - if !o.Equal(v.oid) { + if ok && !o.Equal(v.oid) { t.Errorf("%v: after ToObjectIdentifer, is not equal to %v", v.raw, v.oid) } - } } From af2876d90a044fdad12087d8ee6fc883d8c8b9ae Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 10:48:52 +0200 Subject: [PATCH 06/16] add FromObjectIdentifer( --- src/encoding/asn1/asn1.go | 15 ++++++++++++++- src/encoding/asn1/asn1_test.go | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index c4c8796910bf0f..426e8c015a9c15 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -293,12 +293,25 @@ func parseObjectIdentifier(bytes []byte) (s ObjectIdentifier, err error) { return } -var errInvalidDEROid = errors.New("invalid DER Object Identifier encoding") +var ( + errInvalidDEROid = errors.New("invalid DER Object Identifier encoding") + errInvalidOID = errors.New("invalid oid") +) type OID struct { der []byte } +func FromObjectIdentifer(oid ObjectIdentifier) (OID, error) { + enc, err := makeObjectIdentifier(oid) + if err != nil { + return OID{}, errInvalidOID + } + der := make([]byte, enc.Len()) + enc.Encode(der) + return OID{der}, nil +} + // ParseOID parsed DER-encoded Object Identifier. // On success, der is referenced in the OID struct. // der must not be modified after parsing. diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index f17d4c80fe09d3..43fd745aca9396 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1239,5 +1239,15 @@ func TestOID(t *testing.T) { if ok && !o.Equal(v.oid) { t.Errorf("%v: after ToObjectIdentifer, is not equal to %v", v.raw, v.oid) } + + if v.oid != nil { + oid2, err := FromObjectIdentifer(v.oid) + if err != nil { + t.Errorf("%v: failed while creating OID from ObjectIdentifier: %v", v.raw, err) + } + if !oid2.Equal(oid) { + t.Errorf("%v: OID from ObjectIdentifier is not equal to oid", v.raw) + } + } } } From fe2f9eefa5e105b429b90e262964adadc5ba67d8 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 11:03:55 +0200 Subject: [PATCH 07/16] code style update --- src/encoding/asn1/asn1.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 426e8c015a9c15..abd35313ae7da1 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -438,7 +438,10 @@ func (oid OID) String() string { ) for i, v := range oid.der { - if v&0b10000000 == 0 { + curVal := v & 0x7F + valEnd := v&0x80 == 0 + + if valEnd { if start != 0 { b.WriteByte('.') } @@ -452,15 +455,11 @@ func (oid OID) String() string { overflow = true } - curVal := v & 0x7F - valEnd := v&0x80 == 0 - if overflow { bigVal = bigVal.Lsh(bigVal, bitsPerByte).Or(bigVal, big.NewInt(int64(curVal))) if valEnd { if start == 0 { - b.Write(strconv.AppendUint(numBuf, 2, 10)) - b.WriteByte('.') + b.WriteString("2.") bigVal = bigVal.Sub(bigVal, big.NewInt(80)) } b.WriteString(bigVal.String()) From 99600927bb73b52d5ba4a3955b1219e1a4695f34 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Fri, 16 Jun 2023 15:47:15 +0200 Subject: [PATCH 08/16] use Append instead of String on big.Int --- src/encoding/asn1/asn1.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index abd35313ae7da1..a19ee32076a274 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -462,7 +462,11 @@ func (oid OID) String() string { b.WriteString("2.") bigVal = bigVal.Sub(bigVal, big.NewInt(80)) } - b.WriteString(bigVal.String()) + + numBuf = bigVal.Append(numBuf, 10) + b.Write(numBuf) + numBuf = numBuf[:0] + val = 0 start = i + 1 overflow = false From e4e072a2680799b4b111f5dac8fde093865f1039 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Tue, 20 Jun 2023 12:23:52 +0200 Subject: [PATCH 09/16] add leading 0x80 test --- src/encoding/asn1/asn1.go | 1 - src/encoding/asn1/asn1_test.go | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index a19ee32076a274..bf490d0e3b0aa6 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -339,7 +339,6 @@ func isDEROIDValid(der []byte) bool { start = i + 1 } } - return start != 0 } diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index 43fd745aca9396..e546d1a1fd03fc 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1183,6 +1183,9 @@ var testOIDs = []struct { oid ObjectIdentifier }{ {[]byte{}, false, "", nil}, + {[]byte{0x80, 0x01}, false, "", nil}, + {[]byte{0x01, 0x80, 0x01}, false, "", nil}, + {[]byte{1, 2, 3}, true, "0.1.2.3", []int{0, 1, 2, 3}}, {[]byte{41, 2, 3}, true, "1.1.2.3", []int{1, 1, 2, 3}}, {[]byte{86, 2, 3}, true, "2.6.2.3", []int{2, 6, 2, 3}}, @@ -1212,7 +1215,7 @@ func TestOID(t *testing.T) { oid, err := ParseOID(v.raw) if valid := err == nil; valid != v.valid { if valid { - t.Errorf("%v: unexpected success while parsing", v.raw) + t.Errorf("%v: unexpected success while parsing: %v", v.raw, oid) } else { t.Errorf("%v: unexpected failure while parsing: %v", v.raw, err) } From 1edc57b7fe1a1723507ddf95d7968b23acbfad17 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Tue, 20 Jun 2023 12:25:57 +0200 Subject: [PATCH 10/16] return true --- src/encoding/asn1/asn1.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index bf490d0e3b0aa6..e40889b41d366a 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -339,7 +339,7 @@ func isDEROIDValid(der []byte) bool { start = i + 1 } } - return start != 0 + return true } func (oid OID) Equal(other OID) bool { From 51139dae5d88f30274e4684820f06da1f64d8e86 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Tue, 20 Jun 2023 12:33:17 +0200 Subject: [PATCH 11/16] add comments --- src/encoding/asn1/asn1.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index e40889b41d366a..9fa1c214fc51a3 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -298,10 +298,12 @@ var ( errInvalidOID = errors.New("invalid oid") ) +// An OID represents an ASN.1 OBJECT IDENTIFIER. type OID struct { der []byte } +// FromObjectIdentifer created a new OID from ObjectIdentifier. func FromObjectIdentifer(oid ObjectIdentifier) (OID, error) { enc, err := makeObjectIdentifier(oid) if err != nil { @@ -342,12 +344,14 @@ func isDEROIDValid(der []byte) bool { return true } +// 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) } +// ToObjectIdentifer converts oid to an ObjectIdentifier. func (oid OID) ToObjectIdentifer() (ObjectIdentifier, bool) { o, err := parseObjectIdentifier(oid.der) if err != nil { @@ -356,6 +360,7 @@ func (oid OID) ToObjectIdentifer() (ObjectIdentifier, bool) { return o, true } +// Equal returns true when oid and other represents the same Object Identifier. func (oid OID) EqualObjectIdentifer(other ObjectIdentifier) bool { const ( valSize = 31 @@ -418,6 +423,7 @@ func (oid OID) EqualObjectIdentifer(other ObjectIdentifier) bool { return true } +// Strings returns the string representation of the Object Identifier. func (oid OID) String() string { var b strings.Builder b.Grow(32) From a2b86b88623453af2737c44f94fdd5d52d3e1a75 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Tue, 20 Jun 2023 12:40:07 +0200 Subject: [PATCH 12/16] typo --- src/encoding/asn1/asn1.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 9fa1c214fc51a3..ba22d49d184bc1 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -303,7 +303,7 @@ type OID struct { der []byte } -// FromObjectIdentifer created a new OID from ObjectIdentifier. +// FromObjectIdentifer creates a new OID from ObjectIdentifier. func FromObjectIdentifer(oid ObjectIdentifier) (OID, error) { enc, err := makeObjectIdentifier(oid) if err != nil { From d8a9790b88fc03429d309c4dfa99badaaa2b248c Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Tue, 20 Jun 2023 12:42:56 +0200 Subject: [PATCH 13/16] typos --- src/encoding/asn1/asn1.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index ba22d49d184bc1..5488a44ed34aa0 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -303,8 +303,8 @@ type OID struct { der []byte } -// FromObjectIdentifer creates a new OID from ObjectIdentifier. -func FromObjectIdentifer(oid ObjectIdentifier) (OID, error) { +// FromObjectIdentifier creates a new OID from ObjectIdentifier. +func FromObjectIdentifier(oid ObjectIdentifier) (OID, error) { enc, err := makeObjectIdentifier(oid) if err != nil { return OID{}, errInvalidOID @@ -351,8 +351,8 @@ func (oid OID) Equal(other OID) bool { return bytes.Equal(oid.der, other.der) } -// ToObjectIdentifer converts oid to an ObjectIdentifier. -func (oid OID) ToObjectIdentifer() (ObjectIdentifier, bool) { +// ToObjectIdentifier converts oid to an ObjectIdentifier +func (oid OID) ToObjectIdentifier() (ObjectIdentifier, bool) { o, err := parseObjectIdentifier(oid.der) if err != nil { return nil, false @@ -360,8 +360,8 @@ func (oid OID) ToObjectIdentifer() (ObjectIdentifier, bool) { return o, true } -// Equal returns true when oid and other represents the same Object Identifier. -func (oid OID) EqualObjectIdentifer(other ObjectIdentifier) bool { +// Equal returns true when oid and other represents the same Object Identifier +func (oid OID) EqualObjectIdentifier(other ObjectIdentifier) bool { const ( valSize = 31 bitsPerByte = 7 From 2ad54b0c25d032490aa20387cea0e8560faeec88 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Thu, 22 Jun 2023 13:27:34 +0200 Subject: [PATCH 14/16] support 63bit ToObjectIdentifier and EqualObjectIdentifier --- src/encoding/asn1/asn1.go | 45 ++++++++++++++++++++++++++++++---- src/encoding/asn1/asn1_test.go | 27 ++++++++++++++------ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go index 5488a44ed34aa0..900bb8ea5cf744 100644 --- a/src/encoding/asn1/asn1.go +++ b/src/encoding/asn1/asn1.go @@ -25,6 +25,7 @@ import ( "fmt" "math" "math/big" + "math/bits" "reflect" "strconv" "strings" @@ -341,6 +342,7 @@ func isDEROIDValid(der []byte) bool { start = i + 1 } } + return true } @@ -352,18 +354,51 @@ func (oid OID) Equal(other OID) bool { } // ToObjectIdentifier converts oid to an ObjectIdentifier +// Reports whether the conversion succeeded, it fails when the +// int in ObjectIdentifier is too small. to represent the oid. func (oid OID) ToObjectIdentifier() (ObjectIdentifier, bool) { - o, err := parseObjectIdentifier(oid.der) - if err != nil { - return nil, false + out := make([]int, 0, len(oid.der)+1) + + const ( + valSize = bits.UintSize - 1 // 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 o, true + + return out, true } // Equal returns true when oid and other represents the same Object Identifier func (oid OID) EqualObjectIdentifier(other ObjectIdentifier) bool { const ( - valSize = 31 + valSize = bits.UintSize - 1 // amount of usable bits of val for OIDs. bitsPerByte = 7 maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1 ) diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index e546d1a1fd03fc..716565d9db4f4e 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -10,6 +10,7 @@ import ( "fmt" "math" "math/big" + "math/bits" "reflect" "strings" "testing" @@ -1176,6 +1177,13 @@ func BenchmarkObjectIdentifierString(b *testing.B) { } } +func on64Bit(ints []int) []int { + if bits.UintSize == 64 { + return ints + } + return nil +} + var testOIDs = []struct { raw []byte valid bool @@ -1192,8 +1200,8 @@ var testOIDs = []struct { {[]byte{41, 255, 255, 255, 127}, true, "1.1.268435455", []int{1, 1, 268435455}}, {[]byte{41, 0x87, 255, 255, 255, 127}, true, "1.1.2147483647", []int{1, 1, 2147483647}}, - {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", nil}, - {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", nil}, + {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", on64Bit([]int{1, 1, 34359738367})}, + {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", on64Bit([]int{1, 2, 9223372036854775807})}, {[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615", nil}, {[]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}, @@ -1202,8 +1210,8 @@ var testOIDs = []struct { {[]byte{255, 255, 255, 127}, true, "2.268435375", []int{2, 268435375}}, {[]byte{0x87, 255, 255, 255, 127}, true, "2.2147483567", []int{2, 2147483567}}, {[]byte{255, 127}, true, "2.16303", []int{2, 16303}}, - {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", nil}, - {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", nil}, + {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", on64Bit([]int{2, 34359738287})}, + {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", on64Bit([]int{2, 9223372036854775727})}, {[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535", nil}, {[]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}, @@ -1221,15 +1229,20 @@ func TestOID(t *testing.T) { } continue } + + if err != nil { + continue + } + if str := oid.String(); str != v.str { t.Errorf("%v: unexpected string, got: %q, expected: %q", v.raw, str, v.str) } - if v.oid != nil && !oid.EqualObjectIdentifer(v.oid) { + if v.oid != nil && !oid.EqualObjectIdentifier(v.oid) { t.Errorf("%v: is not equal to %v", v.raw, v.oid) } - o, ok := oid.ToObjectIdentifer() + o, ok := oid.ToObjectIdentifier() if shouldOk := v.oid != nil; shouldOk != ok { if ok { t.Errorf("%v: unexpected success while converting to ObjectIdentifier", v.raw) @@ -1244,7 +1257,7 @@ func TestOID(t *testing.T) { } if v.oid != nil { - oid2, err := FromObjectIdentifer(v.oid) + oid2, err := FromObjectIdentifier(v.oid) if err != nil { t.Errorf("%v: failed while creating OID from ObjectIdentifier: %v", v.raw, err) } From a5a7b91df7d83a0607cdd3c6a70223d1ed17ae5b Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Thu, 22 Jun 2023 13:57:50 +0200 Subject: [PATCH 15/16] fix 32b compilation of tests --- src/encoding/asn1/asn1_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go index 716565d9db4f4e..cd2b20c4ca622f 100644 --- a/src/encoding/asn1/asn1_test.go +++ b/src/encoding/asn1/asn1_test.go @@ -1177,9 +1177,13 @@ func BenchmarkObjectIdentifierString(b *testing.B) { } } -func on64Bit(ints []int) []int { +func on64Bit(ints []int64) []int { if bits.UintSize == 64 { - return ints + out := make([]int, len(ints)) + for i := range ints { + out[i] = int(ints[i]) + } + return out } return nil } @@ -1200,8 +1204,8 @@ var testOIDs = []struct { {[]byte{41, 255, 255, 255, 127}, true, "1.1.268435455", []int{1, 1, 268435455}}, {[]byte{41, 0x87, 255, 255, 255, 127}, true, "1.1.2147483647", []int{1, 1, 2147483647}}, - {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", on64Bit([]int{1, 1, 34359738367})}, - {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", on64Bit([]int{1, 2, 9223372036854775807})}, + {[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", on64Bit([]int64{1, 1, 34359738367})}, + {[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", on64Bit([]int64{1, 2, 9223372036854775807})}, {[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615", nil}, {[]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}, @@ -1210,8 +1214,8 @@ var testOIDs = []struct { {[]byte{255, 255, 255, 127}, true, "2.268435375", []int{2, 268435375}}, {[]byte{0x87, 255, 255, 255, 127}, true, "2.2147483567", []int{2, 2147483567}}, {[]byte{255, 127}, true, "2.16303", []int{2, 16303}}, - {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", on64Bit([]int{2, 34359738287})}, - {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", on64Bit([]int{2, 9223372036854775727})}, + {[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", on64Bit([]int64{2, 34359738287})}, + {[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", on64Bit([]int64{2, 9223372036854775727})}, {[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535", nil}, {[]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}, From 99a7b8d3e5beaa4a0a7b92d18f5c91ac9f10ba04 Mon Sep 17 00:00:00 2001 From: Mateusz Poliwczak Date: Thu, 22 Jun 2023 14:27:58 +0200 Subject: [PATCH 16/16] add api file --- api/next/60665 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 api/next/60665 diff --git a/api/next/60665 b/api/next/60665 new file mode 100644 index 00000000000000..be76b14e411cf3 --- /dev/null +++ b/api/next/60665 @@ -0,0 +1,7 @@ +pkg encoding/asn1, func FromObjectIdentifier(ObjectIdentifier) (OID, error) #60665 +pkg encoding/asn1, func ParseOID([]uint8) (OID, error) #60665 +pkg encoding/asn1, method (OID) Equal(OID) bool #60665 +pkg encoding/asn1, method (OID) EqualObjectIdentifier(ObjectIdentifier) bool #60665 +pkg encoding/asn1, method (OID) String() string #60665 +pkg encoding/asn1, method (OID) ToObjectIdentifier() (ObjectIdentifier, bool) #60665 +pkg encoding/asn1, type OID struct #60665