From 511cf41a779929b5c81b7429c37dc11bd9544182 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Fri, 25 Sep 2015 20:41:38 +0200 Subject: Rewrite from scratch --- ber/base128.go | 16 +---- ber/bool.go | 12 ++-- ber/bool_test.go | 12 +++- ber/len.go | 11 ++-- ber/len_test.go | 4 +- ber/new/ber_test.go | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++ ber/new/class.go | 125 +++++++++++++++++++++++++++++++++++++++ ber/new/common.go | 70 ++++++++++++++++++++++ ber/new/dump.go | 43 ++++++++++++++ ber/new/marshal.go | 107 +++++++++++++++++++++++++++++++++ ber/new/unmarshal.go | 83 ++++++++++++++++++++++++++ ber/obj.go | 18 +++--- parse/parse.go | 2 +- 13 files changed, 629 insertions(+), 38 deletions(-) create mode 100644 ber/new/ber_test.go create mode 100644 ber/new/class.go create mode 100644 ber/new/common.go create mode 100644 ber/new/dump.go create mode 100644 ber/new/marshal.go create mode 100644 ber/new/unmarshal.go diff --git a/ber/base128.go b/ber/base128.go index bf894bb..6f94f35 100644 --- a/ber/base128.go +++ b/ber/base128.go @@ -20,26 +20,12 @@ func base128(n int) (b []byte) { return } -func debase128(b []byte) (i, n int) { - for _, v := range b { - i <<= 7 - i |= int(v & 0x7f) - n++ - if v&0x80 == 0 { - return - } - } - return -} - func Debase128(r io.ByteReader) (i int) { for { b, _ := r.ReadByte() - i <<= 7 - i |= int(b & 0x7f) + i = (i << 7) | int(b&0x7f) if b&0x80 == 0 { return } } - return } diff --git a/ber/bool.go b/ber/bool.go index 39d2562..484ecb5 100644 --- a/ber/bool.go +++ b/ber/bool.go @@ -1,12 +1,16 @@ package ber -func UnmarshalBool(b byte) bool { +import "io" + +func UnmarshalBool(r io.ByteReader) bool { + b, _ := r.ReadByte() return b != 0x00 } -func MarshalBool(b bool) byte { +func MarshalBool(w io.ByteWriter, b bool) { if b { - return 0xFF + w.WriteByte(0xFF) + } else { + w.WriteByte(0) } - return 0x00 } diff --git a/ber/bool_test.go b/ber/bool_test.go index 360c3b9..1eae7e6 100644 --- a/ber/bool_test.go +++ b/ber/bool_test.go @@ -1,6 +1,9 @@ package ber -import "testing" +import ( + "bytes" + "testing" +) type boolTest struct { in bool @@ -14,11 +17,14 @@ var boolTestData = []boolTest{ func TestBool(t *testing.T) { for _, test := range boolTestData { - a := MarshalBool(test.in) + buf := &bytes.Buffer{} + MarshalBool(buf, test.in) + a, _ := buf.ReadByte() if a != test.out { t.Error(test.in, "expected", test.out, "got", a) } - n := UnmarshalBool(test.out) + buf.WriteByte(test.out) + n := UnmarshalBool(buf) if n != test.in { t.Error(test.out, "expected", test.in, "got", n) } diff --git a/ber/len.go b/ber/len.go index 2bccf59..5df8288 100644 --- a/ber/len.go +++ b/ber/len.go @@ -36,14 +36,15 @@ func lenLen(i int) (n int) { return n + 1 } -func MarshalLen(i int) []byte { +func MarshalLen(w io.ByteWriter, i int) { if i < 0x80 { - return []byte{byte(i)} + w.WriteByte(byte(i)) + return } n := lenLen(i) - b := []byte{byte(n) | 0x80} + w.WriteByte(byte(n) | 0x80) for ; n > 0; n-- { - b = append(b, byte(i>>uint((n-1)*8))) + w.WriteByte(byte(i) >> uint(n-1) * 8) } - return b + return } diff --git a/ber/len_test.go b/ber/len_test.go index 2db6453..dc4e4ea 100644 --- a/ber/len_test.go +++ b/ber/len_test.go @@ -20,7 +20,9 @@ var lenTestData = []lenTest{ func TestLen(t *testing.T) { for _, test := range lenTestData { - a := MarshalLen(test.in) + buf := &bytes.Buffer{} + MarshalLen(buf, test.in) + a := buf.Bytes() if !bytes.Equal(a, test.out) { t.Error(test.in, "expected", test.out, "got", a) } diff --git a/ber/new/ber_test.go b/ber/new/ber_test.go new file mode 100644 index 0000000..47974c5 --- /dev/null +++ b/ber/new/ber_test.go @@ -0,0 +1,164 @@ +package ber + +import ( + "bytes" + "testing" +) + +var boolTestData = map[bool][]byte{ + true: {0x01, 0xff}, + false: {0x01, 0x00}, +} + +func TestBool(t *testing.T) { + for val, out := range boolTestData { + s := &state{} + s.marshalBool(val) + o := s.Bytes() + if !bytes.Equal(o, out) { + t.Error(val, "expeced", out, "got", o) + } + v := s.unmarshalBool() + if v != val { + t.Error(out, "expected", val, "got", v) + } + } +} + +var intTestData = map[int][]byte{ + 0: {0x01, 0x00}, + 127: {0x01, 0x7f}, + 128: {0x02, 0x00, 0x80}, + 256: {0x02, 0x01, 0x00}, + -128: {0x01, 0x80}, + -129: {0x02, 0xff, 0x7f}, + 8388607: {0x03, 0x7f, 0xff, 0xff}, + -8388607: {0x03, 0x80, 0x00, 0x01}, + -136: {0x02, 0xff, 0x78}, +} + +func TestInt(t *testing.T) { + for val, out := range intTestData { + s := &state{} + s.marshalInt(val) + o := s.Bytes() + if !bytes.Equal(o, out) { + t.Error(val, "expected", out, "got", o) + } + v := s.unmarshalInt() + if v != val { + t.Error(out, "expected", val, "got", v) + } + } +} + +var stringTestData = map[string][]byte{ + "111": {0x03, 0x031, 0x031, 0x031}, + "0A16": {0x04, 0x30, 0x41, 0x31, 0x36}, +} + +func TestString(t *testing.T) { + for val, out := range stringTestData { + s := &state{} + s.marshalString(val) + o := s.Bytes() + if !bytes.Equal(o, out) { + t.Error(val, "expected", out, "got", o) + } + v := s.unmarshalString() + if v != val { + t.Error(out, "expected", val, "got", v) + } + } +} + +type oidTest struct { + val OID + out []byte +} + +var oidTestData = []oidTest{ + { + val: OID{1, 3, 12, 0, 218}, + out: []byte{0x05, 0x2B, 0x0C, 0x00, 0x81, 0x5A}, + }, + { + val: OID{1, 3, 12, 0, 285, 200}, + out: []byte{0x07, 0x2B, 0x0C, 0x00, 0x82, 0x1D, 0x81, 0x48}, + }, + { + val: OID{0, 39}, + out: []byte{0x01, 0x27}, + }, + { + val: OID{1, 39}, + out: []byte{0x01, 0x4f}, + }, + { + val: OID{2, 40}, + out: []byte{0x01, 0x78}, + }, + { + val: OID{}, + out: []byte{}, + }, + { + val: OID{1, 40}, + out: []byte{}, + }, +} + +func TestOID(t *testing.T) { + for _, test := range oidTestData { + s := &state{} + s.marshalOID(test.val) + o := s.Bytes() + if !bytes.Equal(o, test.out) { + t.Error(test.val, "expected", test.out, "got", o) + } + v := s.unmarshalOID() + if !v.Equal(test.val) { + t.Error(test.out, "expected", test.val, "got", v) + } + } +} + +type bitStringTest struct { + val BitString + out []byte +} + +var bitStringTestData = []bitStringTest{ + { + val: BitString{ + false, false, false, false, + true, false, false, false, + false, false, false, false, + false, false, false, false, + }, + out: []byte{0x03, 0x00, 0x08, 0x00}, + }, + { + val: BitString{true}, + out: []byte{0x02, 0x07, 0x80}, + }, + { + val: BitString{false, false, true, false}, + out: []byte{0x02, 0x04, 0x20}, + }, +} + +func TestBitString(t *testing.T) { + for _, test := range bitStringTestData { + s := &state{} + s.marshalBitString(test.val) + o := s.Bytes() + if !bytes.Equal(o, test.out) { + t.Error(test.val, "expected", test.out, "got", o) + } + v := s.unmarshalBitString() + if !v.Equal(test.val) { + t.Error(test.out, "expected", test.val, "got", v) + } + } +} diff --git a/ber/new/class.go b/ber/new/class.go new file mode 100644 index 0000000..7e4bfd3 --- /dev/null +++ b/ber/new/class.go @@ -0,0 +1,125 @@ +package ber + +type Header struct { + Class + Kind + Tag +} + +type Class int + +const ( + classUniversal Class = iota << 6 + classApplication + classContextSpecific + classPrivate + classMask Class = 3 << 6 +) + +var classNames = map[Class]string{ + classUniversal: "Universal", + classApplication: "Application", + classContextSpecific: "Context-specific", + classPrivate: "Private", +} + +func (c Class) String() string { return classNames[c] } + +type Kind int + +const ( + kindPrimitive Kind = iota << 5 + kindConstructed + kindMask Kind = 1 << 5 +) + +var kindNames = map[Kind]string{ + kindPrimitive: "Primitive", + kindConstructed: "Constructed", +} + +func (k Kind) String() string { return kindNames[k] } + +type Tag int + +const ( + tagEOT Tag = iota + tagBoolean + tagInteger + tagBitString + tagOctetString + tagNull + tagObjectIdentifier + tagObjectDescriptor + tagInstanceOf + tagReal + tagEnumerated + tagEmbeddedPDV + tagUTF8String + tagRelativeOID + _ + _ + tagSequence // SequenceOf + tagSet // SetOf + tagNumericString + tagPrintableString + tagTeletexString // T61String + tagVideotexString + tagIA5String + tagUTCTime + tagGeneralizedTime + tagGraphicString + tagVisibleString // ISO646String + tagGeneralString + tagUniversalString + tagCharacterString + tagBMPString + tagMask Tag = (1 << 5) - 1 +) + +var tagNames = map[Tag]string{ + tagEOT: "End-of-Content", + tagBoolean: "Boolean", + tagInteger: "Integer", + tagBitString: "Bit String", + tagOctetString: "Octet String", + tagNull: "Null", + tagObjectIdentifier: "Object Identifier", + tagObjectDescriptor: "Object Descriptor", + tagInstanceOf: "Instance Of", + tagReal: "Real", + tagEnumerated: "Enumerated", + tagEmbeddedPDV: "Embedded PDV", + tagUTF8String: "UTF8 String", + tagRelativeOID: "Relative OID", + tagSequence: "Sequence (Of)", + tagSet: "Set (Of)", + tagNumericString: "Numeric String", + tagPrintableString: "Printable String", + tagTeletexString: "Teletext String", + tagVideotexString: "Videotex String", + tagIA5String: "IA5 String", + tagUTCTime: "UTC Time", + tagGeneralizedTime: "Generalized Time", + tagGraphicString: "Graphic String", + tagVisibleString: "Visible String", + tagGeneralString: "General String", + tagUniversalString: "Universal String", + tagCharacterString: "Character String", + tagBMPString: "BMP String", +} + +func (t Tag) String() string { return tagNames[t] } + +func (s *state) ident() Header { + b, _ := s.ReadByte() + tag := Tag(b) & tagMask + if tag == tagMask { + tag = Tag(s.unmarshalBase128()) + } + return Header{ + Class: Class(b) & classMask, + Kind: Kind(b) & kindMask, + Tag: tag, + } +} diff --git a/ber/new/common.go b/ber/new/common.go new file mode 100644 index 0000000..823dc23 --- /dev/null +++ b/ber/new/common.go @@ -0,0 +1,70 @@ +package ber + +import ( + "bytes" + "strconv" + "strings" +) + +type state struct{ bytes.Buffer } + +func newState(b []byte) *state { + s := &state{} + s.Write(b) + return s +} + +func (s *state) subState() *state { + ss := &state{} + ss.Write(s.next()) + return ss +} + +func (s *state) next() []byte { + return s.Next(s.unmarshalLen()) +} + +type OID []int + +func (o OID) Equal(p OID) bool { + for i := range o { + if o[i] != p[i] { + return false + } + } + return true +} + +func (o OID) String() string { + s := make([]string, len(o)) + for i, v := range o { + s[i] = strconv.Itoa(v) + } + return strings.Join(s, ".") +} + +type BitString []bool + +func (o BitString) Equal(p BitString) bool { + for i := range o { + if o[i] != p[i] { + return false + } + } + return true +} + +func (o BitString) String() string { + bmap := map[bool]string{ + true: "1", + false: "0", + } + var s string + for i, bit := range o { + if i != 0 && i%4 == 0 { + s += " " + } + s += bmap[bit] + } + return s +} diff --git a/ber/new/dump.go b/ber/new/dump.go new file mode 100644 index 0000000..5baaf1e --- /dev/null +++ b/ber/new/dump.go @@ -0,0 +1,43 @@ +package ber + +import ( + "bytes" + "fmt" +) + +func Dump(b []byte) string { + buf := bytes.NewBuffer(b) + s := &state{*buf} + return s.dump(0) +} + +func (s *state) dump(ident int) string { + h := s.ident() + fmt.Printf("%*s", 2*ident, "") + fmt.Printf("%v %x\n", h, s.Bytes()) + if h.Kind == kindConstructed { + s.subState().dump(ident + 1) + } + if h.Class == classUniversal { + /* + switch h.Tag { + case tagInteger: + fmt.Println(h.Tag, s.unmarshalInt()) + case tagObjectIdentifier: + fmt.Println(h.Tag, s.unmarshalOID()) + case tagBitString: + fmt.Println(h.Tag, s.unmarshalBitString()) + case tagIA5String, tagOctetString: + fmt.Println(h.Tag, s.unmarshalString()) + case tagBoolean: + fmt.Println(h.Tag, s.unmarshalBool()) + default: + fmt.Println(h.Tag, s.next()) + } + */ + } + if s.Len() > 0 { + s.subState().dump(ident) + } + return "" +} diff --git a/ber/new/marshal.go b/ber/new/marshal.go new file mode 100644 index 0000000..c45e61b --- /dev/null +++ b/ber/new/marshal.go @@ -0,0 +1,107 @@ +package ber + +func lenLen(i int) int { + var n int + for ; i > 255; i >>= 8 { + n++ + } + return n + 1 +} + +func (s *state) marshalLen(val int) { + if val < 0x80 { + s.WriteByte(byte(val)) + return + } + n := lenLen(val) + s.WriteByte(byte(n) | 0x80) + for ; n > 0; n-- { + s.WriteByte(byte(val >> uint(n-1) * 8)) + } +} + +func (s *state) marshalBool(val bool) { + s.marshalLen(1) + if val { + s.WriteByte(0xff) + } else { + s.WriteByte(0) + } +} + +func intLen(i int) int { + var n int + for ; i > 127; i >>= 8 { + n++ + } + for ; i < -128; i >>= 8 { + n++ + } + return n + 1 +} + +func (s *state) marshalInt(val int) { + n := intLen(val) + s.marshalLen(n) + for ; n > 0; n-- { + s.WriteByte(byte(val >> uint((n-1)*8))) + } +} + +func (s *state) marshalString(val string) { + s.marshalLen(len(val)) + s.Write([]byte(val)) +} + +func (s *state) marshalBase128(val int) { + if val == 0 { + s.WriteByte(0) + return + } + var l int + for i := val; i > 0; i >>= 7 { + l++ + } + for i := l - 1; i >= 0; i-- { + o := byte(val >> uint(i*7) & 0x7f) + if i != 0 { + o |= 0x80 + } + s.WriteByte(o) + } +} + +func (s *state) marshalOID(val OID) { + if len(val) < 2 || val[0] > 2 { + return + } + if val[0] < 2 && val[1] > 39 { + return + } + buf := &state{} + buf.marshalBase128(val[0]*40 + val[1]) + for _, v := range val[2:] { + buf.marshalBase128(v) + } + s.marshalLen(buf.Len()) + s.Write(buf.Bytes()) +} + +func (s *state) marshalBitString(val BitString) { + pad := (8 - len(val)%8) % 8 + l := len(val) / 8 + if pad != 0 { + l++ + } + b := make([]byte, l) + for i, v := range val { + if v { + x := i / 8 + y := 7 - uint(i%8) + b[x] |= 1 << y + } + } + s.marshalLen(l + 1) + s.WriteByte(byte(pad)) + s.Write(b) +} diff --git a/ber/new/unmarshal.go b/ber/new/unmarshal.go new file mode 100644 index 0000000..9fd27cd --- /dev/null +++ b/ber/new/unmarshal.go @@ -0,0 +1,83 @@ +package ber + +func (s *state) unmarshalLen() int { + b, _ := s.ReadByte() + if b&0x80 == 0 { + return int(b) + } + var n int + for i, v := range s.Next(int(b)) { + n |= int(v) << uint(i*8) + } + return n +} + +func (s *state) unmarshalBase128() int { + var i int + for { + b, err := s.ReadByte() + if err != nil { + return i + } + i = (i << 7) | int(b&0x7f) + if b&0x80 == 0 { + return i + } + } +} + +func (s *state) unmarshalBool() bool { + b := s.next() + return b[0] != 0x00 +} + +func (s *state) unmarshalInt() int { + b := s.next() + neg := b[0]&0x80 != 0 + var i int + for _, v := range b { + if neg { + v = ^v + } + i = (i << 8) | int(v) + } + if neg { + i = ^i + } + return i +} + +func (s *state) unmarshalString() string { + return string(s.next()) +} + +func (s *state) unmarshalOID() OID { + var o OID + if s.Len() < 2 { + return o + } + buf := s.subState() + v := buf.unmarshalBase128() + if v < 80 { + o = OID{v / 40, v % 40} + } else { + o = OID{2, v - 80} + } + for buf.Len() > 0 { + o = append(o, buf.unmarshalBase128()) + } + return o +} + +func (s *state) unmarshalBitString() BitString { + var bs BitString + b := s.next() + pad := int(b[0]) + l := (len(b)-1)*8 - pad + for i := 0; i < l; i++ { + x := 1 + i/8 + y := 7 - uint(i%8) + bs = append(bs, b[x]&(1< 39 { return nil } - buf := bytes.NewBuffer(base128(obj[0]*40 + obj[1])) + buf := &bytes.Buffer{} + MarshalLen(buf, len(obj)) + buf.Write(base128(obj[0]*40 + obj[1])) for _, o := range obj[2:] { buf.Write(base128(o)) } @@ -26,20 +29,17 @@ func (o OID) Marshal() ([]byte, error) { return MarshalOID(o), nil } -func UnmarshalOID(b []byte) (o OID) { - if len(b) < 1 { - return - } - v, n := debase128(b) +func UnmarshalOID(r io.ByteReader) (o OID) { + l := UnmarshalLen(r) + v := Debase128(r) if v < 80 { o = OID{v / 40, v % 40} } else { o = OID{2, v - 80} } - for i := n; i < len(b); i += n { - v, n = debase128(b[i:]) - o = append(o, v) + for i := 0; i < l; i++ { + o = append(o, Debase128(r)) } return } diff --git a/parse/parse.go b/parse/parse.go index 56d46ea..2429b9d 100644 --- a/parse/parse.go +++ b/parse/parse.go @@ -3,7 +3,7 @@ package main import ( "fmt" - "dim13.org/asn1/ber" + "dim13.org/asn1/ber/new" ) func main() { -- cgit v1.2.3