aboutsummaryrefslogtreecommitdiff
path: root/ber
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2015-09-25 20:41:38 +0200
committerDimitri Sokolyuk <demon@dim13.org>2015-09-25 20:41:38 +0200
commit511cf41a779929b5c81b7429c37dc11bd9544182 (patch)
tree6ab421d4b7ec868c48b40da57e3de542831767dd /ber
parent0559b7d4eab07cacf0f005e8e756c1d04470e0c7 (diff)
Rewrite from scratch
Diffstat (limited to 'ber')
-rw-r--r--ber/base128.go16
-rw-r--r--ber/bool.go12
-rw-r--r--ber/bool_test.go12
-rw-r--r--ber/len.go11
-rw-r--r--ber/len_test.go4
-rw-r--r--ber/new/ber_test.go164
-rw-r--r--ber/new/class.go125
-rw-r--r--ber/new/common.go70
-rw-r--r--ber/new/dump.go43
-rw-r--r--ber/new/marshal.go107
-rw-r--r--ber/new/unmarshal.go83
-rw-r--r--ber/obj.go18
12 files changed, 628 insertions, 37 deletions
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<<y) != 0)
+ }
+ return bs
+}
diff --git a/ber/obj.go b/ber/obj.go
index 9b475c7..3de31b7 100644
--- a/ber/obj.go
+++ b/ber/obj.go
@@ -2,6 +2,7 @@ package ber
import (
"bytes"
+ "io"
"strconv"
"strings"
)
@@ -15,7 +16,9 @@ func MarshalOID(obj OID) []byte {
if obj[0] < 2 && obj[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
}