v2 / vlib / x / encoding / asn1 / examples / examples1.v
105 lines · 87 sloc · 3.59 KB · fdc49dc51a0d7e83abd5b383afaaab3f2793f2cf
Raw
1module main
2
3import x.encoding.asn1
4
5// This example was taken from https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html
6// But little modified with removed optional key in the structure and serialization.
7// Example schema:
8//
9// ModuleName DEFINITIONS AUTOMATIC TAGS ::= BEGIN
10// PersonelEntry ::= SEQUENCE {
11// name OCTET STRING,
12// location INTEGER { home(0), field(1), roving(2)} OPTIONAL,
13// age INTEGER OPTIONAL
14// }
15// END
16
17// The syntax above is equivalent with the following one:
18
19// PersonelEntry ::= SEQUENCE {
20// name [0] IMPLICIT OCTET STRING,
21// location [1] IMPLICIT INTEGER {home(0), field(1), roving(2)} OPTIONAL,
22// age [2] IMPLICIT INTEGER OPTIONAL
23// }
24struct PersonelEntry {
25mut:
26 name asn1.OctetString @[context_specific: 0; implicit; inner: 4]
27 location asn1.Integer @[context_specific: 1; implicit; inner: 2]
28 age asn1.Integer @[context_specific: 2; implicit; inner: 2]
29}
30
31fn (pr PersonelEntry) tag() asn1.Tag {
32 return asn1.default_sequence_tag
33}
34
35fn (pr PersonelEntry) payload() ![]u8 {
36 mut out := []u8{}
37
38 // by default, in .der, optional element was not included in the output, so, we remove optional options here.
39 out << asn1.encode_with_options(pr.name, 'context_specific:0; implicit; inner:4')!
40 out << asn1.encode_with_options(pr.location, 'context_specific:1; implicit; inner:2')!
41 // the example the third element is optional, but in .der it would not be serializable until you set it to present
42 out << asn1.encode_with_options(pr.age, 'context_specific:2; implicit; inner:2')!
43
44 return out
45}
46
47// This is an example of way how we can write routine for decode PersonelEntry from bytes.
48fn PersonelEntry.decode(bytes []u8) !PersonelEntry {
49 // decode should produces Sequence type
50 elem := asn1.decode(bytes)!
51 assert elem.tag().equal(asn1.default_sequence_tag)
52
53 // cast it into Sequence type and get the fields
54 seq := elem as asn1.Sequence
55 fields := seq.fields()
56
57 // every fields of the sequence is raw of wrapped element, so we should unwrap it with
58 // the same options used to wrap in encode step, and turn to the real underlying object.
59 el_name := fields[0].unwrap_with_options('context_specific:0; implicit; inner:4')!
60 name := el_name.into_object[asn1.OctetString]()!
61
62 el_location := fields[1].unwrap_with_options('context_specific:1; implicit; inner:2')!
63 location := el_location.into_object[asn1.Integer]()!
64
65 el_age := fields[2].unwrap_with_options('context_specific:2; implicit; inner:2')!
66 age := el_age.into_object[asn1.Integer]()!
67
68 return PersonelEntry{
69 name: name
70 location: location
71 age: age
72 }
73}
74
75// expected output :
76// 30 10
77// 80 08 6269672068656164 // bytestr: 'big head'
78// 81 01 02
79// 82 01 1A
80fn main() {
81 expected_output := [u8(0x30), 0x10, u8(0x80), 0x08, 0x62, 0x69, 0x67, 0x20, 0x68, 0x65, 0x61,
82 0x64, u8(0x81), 0x01, 0x02, u8(0x82), 0x01, 0x1A]
83
84 rock_star1 := PersonelEntry{
85 name: asn1.OctetString.from_hexstring('6269672068656164')!
86 location: asn1.Integer.from_int(2)
87 age: asn1.Integer.from_int(26)
88 }
89
90 // serializes the object into bytes array and check the result
91 out := asn1.encode(rock_star1)!
92 dump(out == expected_output) // out == expected_output: true
93
94 // deserializes bytes back into PersonelEntry object.
95 out_back := PersonelEntry.decode(out)!
96 dump(out_back)
97 // out_back: PersonelEntry{
98 // name: OctetString (big head)
99 // location: Integer 2
100 // age: Integer 26
101 // }
102 dump(out_back.name == rock_star1.name) // true
103 dump(out_back.location == rock_star1.location) // true
104 dump(out_back.age == rock_star1.age) // true
105}
106