|
1 | 1 | # BER-TLV |
2 | 2 |
|
3 | | -[](https://travis-ci.org/skythen/bertlv) |
4 | | -[](https://coveralls.io/github/skythen/bertlv?branch=master) |
5 | | -[](http://godoc.org/github.com/skythen/bertlv) |
| 3 | +[](https://pkg.go.dev/github.com/skythen/bertlv) |
6 | 4 | [](https://goreportcard.com/report/github.com/skythen/bertlv) |
7 | 5 |
|
8 | | -Package bertlv implements parsing and building of BER-TLV structures. |
9 | | - |
10 | | -Please note that this is not a complete implementation of the X.690 standard as it is agnostic about classes (Universal, Application, Context-specific, Private) and therefore does not check for correct encoding of tags/values. |
| 6 | +Package bertlv implements encoding and decoding of BER-TLV structures using struct tags, similar to `encoding/json`. |
11 | 7 |
|
12 | 8 | `go get github.com/skythen/bertlv` |
13 | 9 |
|
14 | | -## Parse |
| 10 | +## Struct Tags |
| 11 | + |
| 12 | +Use the `bertlv` struct tag to map struct fields to BER-TLV tags. |
| 13 | + |
| 14 | +### Hex Format |
| 15 | + |
| 16 | +Use the `0x` prefix to specify tags as hex bytes. Class and construction are derived from the encoded tag bytes: |
| 17 | + |
| 18 | +```go |
| 19 | +type FCI struct { |
| 20 | + AID []byte `bertlv:"0x4F"` |
| 21 | + Label []byte `bertlv:"0x50"` |
| 22 | + Track2 []byte `bertlv:"0x57"` |
| 23 | + ExpDate []byte `bertlv:"0x5F24"` // multi-byte tags |
| 24 | + Records []Record `bertlv:"0x70"` // constructed tags decode recursively |
| 25 | +} |
| 26 | +``` |
| 27 | + |
| 28 | +### Decimal Format |
| 29 | + |
| 30 | +Specify tag number and class explicitly: |
| 31 | + |
| 32 | +`"<number>,<class>[,options...]"` |
| 33 | + |
| 34 | +- **number**: Decimal tag number (0-16383) |
| 35 | +- **class**: One of `universal`, `application`, `context`, `private` |
| 36 | + |
| 37 | +```go |
| 38 | +type Data struct { |
| 39 | + Field1 []byte `bertlv:"1,universal"` |
| 40 | + Field2 []byte `bertlv:"2,application"` |
| 41 | + Field3 []byte `bertlv:"3,context"` |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +### Options |
| 46 | + |
| 47 | +- `omitempty` - skip field during encoding if empty/zero |
| 48 | +- `required` - return error during decoding if tag is not present |
| 49 | + |
| 50 | +```go |
| 51 | +type Data struct { |
| 52 | + Required []byte `bertlv:"0x4F,required"` |
| 53 | + Optional []byte `bertlv:"0x50,omitempty"` |
| 54 | + Both []byte `bertlv:"1,universal,required,omitempty"` |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +## Unmarshal |
15 | 59 |
|
16 | | -You can parse BER-TLV encoded data from bytes: |
| 60 | +Decode BER-TLV data into a struct: |
17 | 61 |
|
18 | 62 | ```go |
19 | | -b := []byte{0x71, 0x10, 0xB0, 0x0E, 0x0F, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0E, 0x05, 0x05, 0x04, 0x03, 0x02, 0x01} |
20 | | -bertlvs, err := Parse(b) |
| 63 | +var result struct { |
| 64 | + AID []byte `bertlv:"0x4F"` |
| 65 | + Label []byte `bertlv:"0x50"` |
| 66 | +} |
| 67 | + |
| 68 | +err := bertlv.Unmarshal(data, &result) |
21 | 69 | ``` |
22 | | -### Constructed objects |
23 | | -You can check if a BerTLV is constructed and get first or all children or filter child objects by tag: |
| 70 | + |
| 71 | +### Options |
| 72 | + |
24 | 73 | ```go |
25 | | -if bertlvs[0].Tag.IsConstructed() { |
26 | | - child := bertlvs[0].FirstChild(NewOneByteTag(0x0F)) |
| 74 | +opts := bertlv.Options{ |
| 75 | + DisallowUnknownFields: true, |
27 | 76 | } |
| 77 | + |
| 78 | +err := bertlv.UnmarshalWithOptions(data, &result, opts) |
28 | 79 | ``` |
29 | 80 |
|
30 | | -## Create |
31 | | -You can create single BER-TLVs with NewBerTLV: |
| 81 | +### Custom Unmarshaler |
| 82 | + |
| 83 | +Implement the `Unmarshaler` interface for custom decoding: |
| 84 | + |
32 | 85 | ```go |
33 | | -val := []byte{0xB0, 0x0E, 0x0F, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0E, 0x05, 0x05, 0x04, 0x03, 0x02, 0x01} |
34 | | -bertlv, err := NewBerTLV(NewOneByteTag(0x71), val) |
| 86 | +type MyType struct { |
| 87 | + Value string |
| 88 | +} |
| 89 | + |
| 90 | +func (m *MyType) UnmarshalBERTLV(value []byte) error { |
| 91 | + m.Value = string(value) |
| 92 | + return nil |
| 93 | +} |
35 | 94 | ``` |
36 | 95 |
|
37 | | -If you want to create complex constructed objects you use the Builder: |
| 96 | +## Marshal |
| 97 | + |
| 98 | +Encode a struct into BER-TLV format: |
| 99 | + |
38 | 100 | ```go |
39 | | -builder := Builder{} |
40 | | -nestedBuilder := Builder{} |
41 | | -anotherNestedBuilder := Builder{} |
| 101 | +data := struct { |
| 102 | + AID []byte `bertlv:"0x4F"` |
| 103 | + Label []byte `bertlv:"0x50"` |
| 104 | +}{ |
| 105 | + AID: []byte{0xA0, 0x00, 0x00, 0x00, 0x04, 0x10, 0x10}, |
| 106 | + Label: "GoCard", |
| 107 | +} |
42 | 108 |
|
43 | | -berTlvs, err := builder.AddBytes(NewOneByteTag(0x71), // first level, constructed object |
44 | | - nestedBuilder.AddBytes(NewOneByteTag(0xB0), // second level, constructed object |
45 | | - anotherNestedBuilder. |
46 | | - AddBytes(NewOneByteTag(0x0F), []byte{0x01, 0x02, 0x03, 0x04, 0x05}). // third level primitive object |
47 | | - AddBytes(NewOneByteTag(0x0E), []byte{0x05, 0x04, 0x03, 0x02, 0x01}). // third level primitive object |
48 | | - Bytes()). |
49 | | - Bytes()). |
50 | | -BuildBerTLVs() |
| 109 | +encoded, err := bertlv.Marshal(data) |
51 | 110 | ``` |
52 | 111 |
|
53 | | -You can also use |
54 | | -- builder.AddEmpty() to add objects without value |
55 | | -- builder.AddByte() to add objects with a value that consists of a single byte |
56 | | -- builder.AddRaw() to add raw bytes |
| 112 | +### Custom Marshaler |
| 113 | + |
| 114 | +Implement the `Marshaler` interface for custom encoding: |
| 115 | + |
| 116 | +```go |
| 117 | +func (m MyType) MarshalBERTLV() ([]byte, error) { |
| 118 | + return []byte(m.Value), nil |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +## Supported Types |
| 123 | + |
| 124 | +The following Go types can be used as struct fields: |
| 125 | + |
| 126 | +- `[]byte` - raw TLV value |
| 127 | +- `string` - UTF-8 encoded value |
| 128 | +- `int`, `int8`, `int16`, `int32`, `int64` - big-endian signed integers |
| 129 | +- `uint`, `uint8`, `uint16`, `uint32`, `uint64` - big-endian unsigned integers |
| 130 | +- `bool` - single byte (0x00 = false, non-zero = true) |
| 131 | +- `struct` - nested constructed TLV |
| 132 | +- `[]T` (slice of structs) - repeated TLV elements |
0 commit comments