From a08be6b395949a74efc8115a726112d64e96b44d Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Thu, 16 Sep 2021 13:51:43 +0200 Subject: [PATCH] Patch ASN.1 BER encoding of integers and length + unit tests --- goldap/asn1.go | 40 ++++++++++++++++------ goldap/asn1_test.go | 54 ++++++++++++++++++++++++++++++ goldap/message_test.go | 76 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 goldap/asn1_test.go create mode 100644 goldap/message_test.go diff --git a/goldap/asn1.go b/goldap/asn1.go index d1539d9..08164eb 100644 --- a/goldap/asn1.go +++ b/goldap/asn1.go @@ -223,19 +223,33 @@ func parseInt64(bytes []byte) (ret int64, err error) { return } -func sizeInt64(i int64) (size int) { - for ; i != 0 || size == 0; i >>= 8 { - size++ +func sizeInt64(i int64) int { + n := 1 + + for i > 127 { + n++ + i >>= 8 } - return + + for i < -128 { + n++ + i >>= 8 + } + + return n } -func writeInt64(bytes *Bytes, i int64) (size int) { - for ; i != 0 || size == 0; i >>= 8 { // Write at least one byte even if the value is 0 - bytes.writeBytes([]byte{byte(i)}) - size++ +func writeInt64(bytes *Bytes, i int64) int { + n := sizeInt64(i) + buf := [8]byte{} + + for j := 0; j < n; j++ { + b := i >> uint((n-1-j)*8) + buf[j] = byte(b) } - return + bytes.writeBytes(buf[:n]) + + return n } // parseInt treats the given bytes as a big-endian, signed integer and returns @@ -713,7 +727,13 @@ func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) { panic("Can't have a negative length") } else if t.Length >= 128 { - lengthBytes := writeInt64(bytes, int64(t.Length)) + lengthBytes := 0 + val := t.Length + for val > 0 { + lengthBytes++ + bytes.writeBytes([]byte{byte(val & 0xff)}) + val >>= 8 + } bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))}) size += lengthBytes + 1 diff --git a/goldap/asn1_test.go b/goldap/asn1_test.go new file mode 100644 index 0000000..eb11d29 --- /dev/null +++ b/goldap/asn1_test.go @@ -0,0 +1,54 @@ +package message + +import ( + "testing" + "bytes" +) + +func TestSizeInt64(t *testing.T) { + s := sizeInt64(0) + if s != 1 { + t.Errorf("computed size is %d, expected 1", s) + } + + s = sizeInt64(127) + if s != 1 { + t.Errorf("computed size is %d, expected 1", s) + } + + s = sizeInt64(128) + if s != 2 { + t.Errorf("computed size is %d, expected 2", s) + } + + s = sizeInt64(50000) + if s != 3 { + t.Errorf("computed size is %d, expected 3", s) + } + + s = sizeInt64(-12345) + if s != 2 { + t.Errorf("computed size is %d, expected 2", s) + } +} + +func TestWriteInt64(t *testing.T) { + vtests := []int64{0, 127, 128, 50000, -12345} + expsize := []int{1, 1, 2, 3, 2} + expresult := [][]byte{{0x00}, {0x7F}, {0x00, 0x80}, {0x00, 0xc3, 0x50}, {0xcf, 0xc7}} + + for idx, v := range vtests { + fs := sizeInt64(v) + b := NewBytes(fs, make([]byte, fs)) + t.Log("computing", v) + s := writeInt64(b, v) + if s != expsize[idx] { + t.Errorf("computed size is %d, expected %d", s, expsize[idx]) + } + if !bytes.Equal(b.Bytes(), expresult[idx]) { + t.Errorf("wrong computed bytes, got %v, expected %v", b.Bytes(), expresult[idx]) + } + a, e := parseInt64(b.Bytes()) + t.Log("parse", a, e) + } +} diff --git a/goldap/message_test.go b/goldap/message_test.go new file mode 100644 index 0000000..9d10d98 --- /dev/null +++ b/goldap/message_test.go @@ -0,0 +1,76 @@ +package message + +import ( + "testing" + "fmt" +) + +func toHex(b []byte) (r string) { + r = "[ " + for _, e := range b { + r += fmt.Sprintf("0x%x ", e) + } + return r + "]" +} + +func TestMessageID(t *testing.T) { + m := NewLDAPMessageWithProtocolOp(UnbindRequest{}) + m.SetMessageID(128) + buf, err := m.Write() + if err != nil { + t.Errorf("marshalling failed with %v", err) + } + t.Logf("%v", toHex(buf.Bytes())) + + ret, err := ReadLDAPMessage(NewBytes(0, buf.Bytes())) + if err != nil { + t.Errorf("unmarshalling failed with %v", err) + } + if _, ok := ret.ProtocolOp().(UnbindRequest); !ok { + t.Errorf("should be an unbind request") + } + if ret.MessageID() != 128 { + t.Errorf("Expect message id 128, got %d", ret.MessageID()) + } + t.Log("Done, marshal/unmarshall worked") +} + +func TestSearchEntry(t *testing.T) { + m := NewLDAPMessageWithProtocolOp(SearchResultEntry{ + objectName:"cn=êige€nbgtz,ou=users,dc=deuxfleurs,dc=fr", + attributes: PartialAttributeList{ + PartialAttribute{ + type_:"displayname", + vals:[]AttributeValue{"êiGe€NBgTZ"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"inetOrgPerson"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"organizationalPerson"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"person"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"top"}, + }, + PartialAttribute{ + type_:"structuralobjectclass", + vals:[]AttributeValue{"inetOrgPerson"}, + }, + }, + }) + m.SetMessageID(24) + buf, err := m.Write() + if err != nil { + t.Errorf("marshalling failed with %v", err) + } + if buf.Bytes()[0] != 0x30 { + t.Logf("Malformed message: %v", toHex(buf.Bytes())) + } +}