From 39ed11eb0a73d80bfee516d5505a3e0767cfb943 Mon Sep 17 00:00:00 2001 From: Lukas Bachschwell Date: Thu, 25 Apr 2024 12:11:24 +0200 Subject: [PATCH 1/5] Add object identifier encode decode Signed-off-by: Lukas Bachschwell --- ber.go | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/ber.go b/ber.go index 53fe04e..d1c383d 100644 --- a/ber.go +++ b/ber.go @@ -9,6 +9,8 @@ import ( "math" "os" "reflect" + "strconv" + "strings" "time" "unicode/utf8" ) @@ -391,6 +393,10 @@ func readPacket(reader io.Reader) (*Packet, int, error) { p.Value = DecodeString(content) case TagNULL: case TagObjectIdentifier: + oid, err := parseObjectIdentifier(content) + if err == nil { + p.Value = OIDToString(oid) + } case TagObjectDescriptor: case TagExternal: case TagRealFloat: @@ -406,6 +412,10 @@ func readPacket(reader io.Reader) (*Packet, int, error) { p.Value = val } case TagRelativeOID: + oid, err := parseObjectIdentifier(content) + if err == nil { + p.Value = OIDToString(oid) + } case TagSequence: case TagSet: case TagNumericString: @@ -633,3 +643,167 @@ func NewReal(classType Class, tagType Type, tag Tag, value interface{}, descript } return p } + +func NewOID(classType Class, tagType Type, tag Tag, value interface{}, description string) *Packet { + p := Encode(classType, tagType, tag, nil, description) + + switch v := value.(type) { + case string: + encoded, err := encodeOID(v) + if err != nil { + fmt.Printf("faled writing %v", err) + return nil + } + fmt.Println("Writing OID ", v, encoded) + p.Value = v + p.Data.Write(encoded) + // TODO: support []int already ? + default: + panic(fmt.Sprintf("Invalid type %T, expected float{64|32}", v)) + } + return p +} + +// encodeOID takes a string representation of an OID and returns its DER-encoded byte slice along with any error. +func encodeOID(oidString string) ([]byte, error) { + // Convert the string representation to an asn1.ObjectIdentifier + parts := strings.Split(oidString, ".") + oid := make([]int, len(parts)) + for i, part := range parts { + var val int + if _, err := fmt.Sscanf(part, "%d", &val); err != nil { + return nil, fmt.Errorf("invalid OID part '%s': %w", part, err) + } + oid[i] = val + } + if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) { + panic(fmt.Sprintf("invalid object identifier % d", oid)) // TODO: not elegant + } + encoded := make([]byte, 0) + + encoded = appendBase128Int(encoded[:0], int64(oid[0]*40+oid[1])) + for i := 2; i < len(oid); i++ { + encoded = appendBase128Int(encoded, int64(oid[i])) + } + + return encoded, nil +} + +func appendBase128Int(dst []byte, n int64) []byte { + l := base128IntLength(n) + + for i := l - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + + dst = append(dst, o) + } + + return dst +} +func base128IntLength(n int64) int { + if n == 0 { + return 1 + } + + l := 0 + for i := n; i > 0; i >>= 7 { + l++ + } + + return l +} + +func OIDToString(oi []int) string { + var s strings.Builder + s.Grow(32) + + buf := make([]byte, 0, 19) + for i, v := range oi { + if i > 0 { + s.WriteByte('.') + } + s.Write(strconv.AppendInt(buf, int64(v), 10)) + } + + return s.String() +} + +// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and +// returns it. An object identifier is a sequence of variable length integers +// that are assigned in a hierarchy. +func parseObjectIdentifier(bytes []byte) (s []int, err error) { + if len(bytes) == 0 { + err = fmt.Errorf("zero length OBJECT IDENTIFIER") + return + } + + // In the worst case, we get two elements from the first byte (which is + // encoded differently) and then every varint is a single byte long. + s = make([]int, len(bytes)+1) + + // The first varint is 40*value1 + value2: + // According to this packing, value1 can take the values 0, 1 and 2 only. + // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, + // then there are no restrictions on value2. + v, offset, err := parseBase128Int(bytes, 0) + if err != nil { + return + } + if v < 80 { + s[0] = v / 40 + s[1] = v % 40 + } else { + s[0] = 2 + s[1] = v - 80 + } + + i := 2 + for ; offset < len(bytes); i++ { + v, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + s[i] = v + } + s = s[0:i] + return +} + +// parseBase128Int parses a base-128 encoded int from the given offset in the +// given byte slice. It returns the value and the new offset. +func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) { + offset = initOffset + var ret64 int64 + for shifted := 0; offset < len(bytes); shifted++ { + // 5 * 7 bits per byte == 35 bits of data + // Thus the representation is either non-minimal or too large for an int32 + if shifted == 5 { + err = fmt.Errorf("base 128 integer too large") + return + } + ret64 <<= 7 + b := bytes[offset] + // integers should be minimally encoded, so the leading octet should + // never be 0x80 + if shifted == 0 && b == 0x80 { + err = fmt.Errorf("integer is not minimally encoded") + return + } + ret64 |= int64(b & 0x7f) + offset++ + if b&0x80 == 0 { + ret = int(ret64) + // Ensure that the returned value fits in an int on all platforms + if ret64 > math.MaxInt32 { + err = fmt.Errorf("base 128 integer too large") + } + return + } + } + err = fmt.Errorf("truncated base 128 integer") + return +} From 6b4fcff0127bab74c5da1c478b030ae7cd2535e0 Mon Sep 17 00:00:00 2001 From: Lukas Bachschwell Date: Thu, 25 Apr 2024 12:11:36 +0200 Subject: [PATCH 2/5] bump go version to support go work Signed-off-by: Lukas Bachschwell --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index ee0b4be..3ee0a14 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/go-asn1-ber/asn1-ber +module gopkg.in/asn1-ber.v1 -go 1.13 +go 1.21 From 8d5f2a55fc6be9a5beafa2f980cf2b891c018eec Mon Sep 17 00:00:00 2001 From: Lukas Bachschwell Date: Thu, 25 Apr 2024 16:10:15 +0200 Subject: [PATCH 3/5] remove log Signed-off-by: Lukas Bachschwell --- ber.go | 1 - 1 file changed, 1 deletion(-) diff --git a/ber.go b/ber.go index d1c383d..e7762dd 100644 --- a/ber.go +++ b/ber.go @@ -654,7 +654,6 @@ func NewOID(classType Class, tagType Type, tag Tag, value interface{}, descripti fmt.Printf("faled writing %v", err) return nil } - fmt.Println("Writing OID ", v, encoded) p.Value = v p.Data.Write(encoded) // TODO: support []int already ? From d9c2bde2878d8487fa37a4d4ebb81bddba8c7f66 Mon Sep 17 00:00:00 2001 From: Lukas Bachschwell Date: Mon, 29 Apr 2024 22:05:24 +0200 Subject: [PATCH 4/5] Pr fixes - downgrade required go version - revert modulepath - fix typo Signed-off-by: Lukas Bachschwell --- ber.go | 2 +- go.mod | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ber.go b/ber.go index e7762dd..71b3c3a 100644 --- a/ber.go +++ b/ber.go @@ -651,7 +651,7 @@ func NewOID(classType Class, tagType Type, tag Tag, value interface{}, descripti case string: encoded, err := encodeOID(v) if err != nil { - fmt.Printf("faled writing %v", err) + fmt.Printf("failed writing %v", err) return nil } p.Value = v diff --git a/go.mod b/go.mod index 3ee0a14..ee0b4be 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module gopkg.in/asn1-ber.v1 +module github.com/go-asn1-ber/asn1-ber -go 1.21 +go 1.13 From fc53e0ccdcfcbd4fac7136644c67d88698d9d782 Mon Sep 17 00:00:00 2001 From: Lukas Bachschwell Date: Mon, 29 Apr 2024 22:11:20 +0200 Subject: [PATCH 5/5] Add Object Identifier Test Signed-off-by: Lukas Bachschwell --- ber_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ber_test.go b/ber_test.go index 7218c0e..7f0f4ed 100644 --- a/ber_test.go +++ b/ber_test.go @@ -100,6 +100,24 @@ func TestString(t *testing.T) { } } +func TestEncodeDecodeOID(t *testing.T) { + for _, v := range []string{"0.1", "1.1", "2.3", "0.4", "0.4.5.1888", "0.10.5.1888.234.324234"} { + enc, err := encodeOID(v) + if err != nil { + t.Errorf("error on encoding object identifier when encoding %s: %v", v, err) + } + parsed, err := parseObjectIdentifier(enc) + if err != nil { + t.Errorf("error on parsing object identifier when parsing %s: %v", v, err) + } + t.Log(enc) + t.Log(OIDToString(parsed)) + if v != OIDToString(parsed) { + t.Error("encoded object identifier did not match parsed") + } + } +} + func TestSequenceAndAppendChild(t *testing.T) { values := []string{ "HIC SVNT LEONES",