v2 / vlib / x / encoding / asn1 / examples / examples2.v
380 lines · 331 sloc · 13.17 KB · e2e5cf8db56f3562c7baa735061690be936bdf3e
Raw
1module main
2
3import x.encoding.asn1
4
5// This example takes more complex scenario, in the sense of nested wrapping, the use of
6// other class Element supported in this module.
7//
8// This examples is taken from ITU-T X.690 Information technology – ASN.1 encoding rules:
9// Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and
10// Distinguished Encoding Rules (DER) document.
11//
12// Especially from Annex A. Example of encodings of the document.
13
14// from A.1 ASN.1 description of the record structure.
15// The structure of the hypothetical personnel record is formally described below using ASN.1 specified in
16// ITU-T Rec. X.680 | ISO/IEC 8824-1 for defining types.
17//
18// PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
19// name Name,
20// title [0] VisibleString,
21// number EmployeeNumber,
22// dateOfHire [1] Date,
23// nameOfSpouse [2] Name,
24// children [3] IMPLICIT SEQUENCE OF ChildInformation DEFAULT {}
25// }
26//
27struct PersonnelRecord {
28 name Name
29 title asn1.VisibleString
30 number EmployeeNumber
31 date_of_hire Date
32 name_of_spouse Name
33 children asn1.SequenceOf[ChildInformation]
34}
35
36fn (pr PersonnelRecord) tag() asn1.Tag {
37 return asn1.default_set_tag
38}
39
40fn (pr PersonnelRecord) payload() ![]u8 {
41 mut out := []u8{}
42 out << asn1.encode(pr.name)!
43 out << asn1.encode_with_options(pr.title, 'context_specific:0;explicit;inner:26')!
44 out << asn1.encode(pr.number)!
45 out << asn1.encode_with_options(pr.date_of_hire,
46 'context_specific: 1; explicit; inner:application,false,3')!
47 out << asn1.encode_with_options(pr.name_of_spouse,
48 'context_specific: 2; explicit; inner:application,true,1')!
49 out << asn1.encode_with_options(pr.children, 'context_specific: 3; implicit; inner:16')!
50
51 return out
52}
53
54// deserializer of PersonellRecord bytes
55fn PersonnelRecord.decode(bytes []u8) !PersonnelRecord {
56 // we perfrom decoding as a reverse of the serialization with the same options.
57 // after that, we get an Element (with the Set type of PersonellRecord)
58 el := asn1.decode_with_options(bytes, 'application:0;implicit;inner:17')!
59 assert el.tag() == asn1.default_set_tag
60 set := el.into_object[asn1.Set]()!
61
62 // fields is series of element ([]Element), PersonellRecord fields
63 fields := set.fields()
64
65 // we turn series of element into underlying desired type.
66 // its rather clumsy to transforms it, because Sequenve (and Set) fields is an []Element, so you should care
67 // to turn this into desired object
68 name_app := fields[0].into_object[asn1.ApplicationElement]()!
69 name := Name(name_app)
70
71 // Title
72 title_elem := fields[1].unwrap_with_options('context_specific:0;explicit;inner:26')!
73 title := title_elem.into_object[asn1.VisibleString]()!
74
75 // EmployeNumber
76 emp_num := fields[2].into_object[asn1.ApplicationElement]()!
77 employe_number := EmployeeNumber(emp_num)
78
79 // dateOfHire
80 doh :=
81 fields[3].unwrap_with_options('context_specific: 1; explicit; inner:application,false,3')!
82 doh_app := doh.into_object[asn1.ApplicationElement]()!
83 date_of_hire := Date(doh_app)
84
85 // nameOfSpouse
86 nosp :=
87 fields[4].unwrap_with_options('context_specific: 2; explicit; inner:application,true,1')!
88 nosp_app := nosp.into_object[asn1.ApplicationElement]()!
89 name_of_spouse := Name(nosp_app)
90
91 // The children is Sequence of Set, first we unwrap it then turn into sequence.
92 children := fields[5].unwrap_with_options('context_specific: 3; implicit; inner:16')!
93 children_seq := children.into_object[asn1.Sequence]()!
94 childset_fields := children_seq.fields() // []Element
95
96 mut childset := []ChildInformation{}
97 for item in childset_fields {
98 // item is an Element
99 obj := item.into_object[asn1.Set]()!
100 i := ChildInformation.from_set(obj)!
101 childset << i
102 }
103
104 pr := PersonnelRecord{
105 name: name
106 title: title
107 number: employe_number
108 date_of_hire: date_of_hire
109 name_of_spouse: name_of_spouse
110 children: asn1.SequenceOf.from_list[ChildInformation](childset)!
111 }
112 return pr
113}
114
115// ChildInformation ::= SET {
116// name Name,
117// dateOfBirth [0] Date
118// }
119//
120// Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
121// givenName VisibleString,
122// initial VisibleString,
123// familyName VisibleString
124// }
125//
126// EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER
127// Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD
128
129// ChildInformation ::= SET {
130// name Name,
131// dateOfBirth [0] Date
132// }
133struct ChildInformation {
134 name Name
135 date_of_birth Date
136}
137
138// s should Set with series of Element with []ChildInformation within underlying fields.
139fn ChildInformation.from_set(s asn1.Set) !ChildInformation {
140 if s.fields().len != 2 {
141 return error('Bad ChildInformation set')
142 }
143 fields := s.fields() // serialized name and dateOfBirth of ChildInformation
144 name := fields[0].into_object[asn1.ApplicationElement]()!
145 doh :=
146 fields[1].unwrap_with_options('context_specific: 0; explicit; inner:application,false,3')!
147 date := doh.into_object[asn1.ApplicationElement]()!
148 ch := ChildInformation{
149 name: Name(name)
150 date_of_birth: Date(date)
151 }
152 return ch
153}
154
155fn (ci ChildInformation) tag() asn1.Tag {
156 return asn1.default_set_tag
157}
158
159fn (ci ChildInformation) payload() ![]u8 {
160 mut out := []u8{}
161 out << asn1.encode(ci.name)!
162 out << asn1.encode_with_options(ci.date_of_birth,
163 'context_specific: 0; explicit; inner:application,false,3')!
164
165 return out
166}
167
168// EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER
169type EmployeeNumber = asn1.ApplicationElement
170
171fn EmployeeNumber.new(val asn1.Integer) !asn1.ApplicationElement {
172 return asn1.ApplicationElement.from_element(val, 2, .implicit)!
173}
174
175// Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD
176type Date = asn1.ApplicationElement
177
178fn Date.new(val asn1.VisibleString) !asn1.ApplicationElement {
179 return asn1.ApplicationElement.from_element(val, 3, .implicit)!
180}
181
182// Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
183// givenName VisibleString,
184// initial VisibleString,
185// familyName VisibleString
186// }
187type Name = asn1.ApplicationElement
188
189fn Name.new(el NameEntry) !asn1.ApplicationElement {
190 return asn1.ApplicationElement.from_element(el, 1, .implicit)!
191}
192
193struct NameEntry {
194 given_name asn1.VisibleString
195 initial asn1.VisibleString
196 family_name asn1.VisibleString
197}
198
199fn (n NameEntry) tag() asn1.Tag {
200 return asn1.default_sequence_tag
201}
202
203fn (n NameEntry) payload() ![]u8 {
204 mut out := []u8{}
205 out << asn1.encode(n.given_name)!
206 out << asn1.encode(n.initial)!
207 out << asn1.encode(n.family_name)!
208
209 return out
210}
211
212// The value of John Smith's personnel record is formally described below using ASN.1.
213// { name {givenName "John",initial "P",familyName "Smith"},
214// title "Director",
215// number 51,
216// dateOfHire "19710917",
217// nameOfSpouse {givenName "Mary",initial "T",familyName "Smith"},
218// children {
219// { name {givenName "Ralph",initial "T",familyName "Smith"},
220// dateOfBirth "19571111"
221// },
222// { name {givenName "Susan",initial "B",familyName "Jones"},
223// dateOfBirth "19590717"
224// }
225// }
226// }
227
228// Representation of this record value
229//
230// 60 8185
231// 61 10 1A 04 'John' // name
232// 1A 01 'P'
233// 1A 05 'Smith'
234// A0 0A 1A 08 'Director' // title
235// 42 01 33 // number
236// A1 0A 43 08 '19710917' // dateOfHire
237// A2 12 61 10 1A 04 'Mary' // nameOfSpouse
238// 1A 01 'T'
239// 1A 05 'Smith'
240// A3 42 31 1F 61 11 1A 05 'Ralph' => 52 61 6c 70 68 // children
241// 1A 01 'T' => 54
242// 1A 05 'Smith' => 53 6d 69 74 68
243// A0 0A 43 08 '19571111' => 31 39 35 37 31 31 31 31
244// 31 1F 61 11 1A 05 'Susan' => 53 75 73 61 6e
245// 1A 01 'B' => 42
246// 1A 05 'Jones' => 4a 6f 6e 65 73
247// A0 0A 43 08 '19590717' => 31 39 35 39 30 37 31 37
248
249fn main() {
250 // We detailed every pieces of element
251
252 // PersonnelRecord.name
253 pr_name := Name.new(NameEntry{
254 given_name: asn1.VisibleString.new('John')!
255 initial: asn1.VisibleString.new('P')!
256 family_name: asn1.VisibleString.new('Smith')!
257 })!
258 // 61 10 1A 04 'John' => 4a 6f 68 6e // name
259 // 1A 01 'P' => 50
260 // 1A 05 'Smith' => 53 6d 69 74 68
261 pr_name_bytes := [u8(0x61), 0x10, 0x1A, 0x04, 0x4a, 0x6f, 0x68, 0x6e, 0x1A, 0x01, 0x50, 0x1A,
262 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68]
263
264 assert asn1.encode(pr_name)! == pr_name_bytes
265
266 // PersonnelRecord.title
267 // A0 0A 1A 08 'Director' => 44 69 72 65 63 74 6f 72 // title
268 title := asn1.VisibleString.new('Director')!
269 title_bytes := [u8(0xA0), 0x0A, 0x1A, 0x08, u8(0x44), 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72]
270 assert asn1.encode_with_options(title, 'context_specific:0;explicit;inner:26')! == title_bytes
271
272 // PersonnelRecord.EmployeeNumber
273 // 42 01 33 // number
274 emp_num := EmployeeNumber.new(asn1.Integer.from_int(51))!
275 emp_bytes := [u8(0x42), 0x01, 0x33]
276 assert asn1.encode(emp_num)! == emp_bytes
277
278 // PersonnelRecord.dateOfHire
279 // A1 0A 43 08 '19710917' => 31 39 37 31 30 39 31 37 // dateOfHire
280 // dateOfHire [1] Date,
281 doh := Date.new(asn1.VisibleString.new('19710917')!)!
282 doh_bytes := [u8(0xA1), 0x0A, 0x43, 0x08, 0x31, 0x39, 0x37, 0x31, 0x30, 0x39, 0x31, 0x37]
283 assert asn1.encode_with_options(doh, 'context_specific: 1; explicit; inner:application,false,3')! == doh_bytes
284
285 // PersonnelRecord.nameOfSpouse
286 // A2 12 61 10 1A 04 'Mary' => 4d 61 72 79 // nameOfSpouse
287 // 1A 01 'T' => 54
288 // 1A 05 'Smith' => 53 6d 69 74 68
289 nosp := Name.new(NameEntry{
290 given_name: asn1.VisibleString.new('Mary')!
291 initial: asn1.VisibleString.new('T')!
292 family_name: asn1.VisibleString.new('Smith')!
293 })!
294 nosp_bytes := [u8(0xA2), 0x12, 0x61, 0x10, 0x1A, 0x04, 0x4d, 0x61, 0x72, 0x79, 0x1A, 0x01,
295 0x54, 0x1A, 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68]
296 // nameOfSpouse [2] Name,
297 assert asn1.encode_with_options(nosp, 'context_specific: 2; explicit; inner:application,true,1')! == nosp_bytes
298
299 // { name {givenName "Ralph",initial "T",familyName "Smith"},
300 // dateOfBirth "19571111"
301 // },
302 childinfo0 := ChildInformation{
303 name: Name.new(NameEntry{
304 given_name: asn1.VisibleString.new('Ralph')!
305 initial: asn1.VisibleString.new('T')!
306 family_name: asn1.VisibleString.new('Smith')!
307 })!
308 date_of_birth: Date.new(asn1.VisibleString.new('19571111')!)!
309 }
310 // 31 1F 61 11 1A 05 'Ralph' => 52 61 6c 70 68
311 // 1A 01 'T' => 54
312 // 1A 05 'Smith' => 53 6d 69 74 68
313 // A0 0A 43 08 '19571111' => 31 39 35 37 31 31 31 31
314 ch0_bytes := [u8(0x31), 0x1F, 0x61, 0x11, 0x1A, 0x05, 0x52, 0x61, 0x6c, 0x70, 0x68, 0x1A, 0x01,
315 0x54, 0x1A, 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68, 0xA0, 0x0A, 0x43, 0x08, 0x31, 0x39, 0x35,
316 0x37, 0x31, 0x31, 0x31, 0x31]
317 assert asn1.encode(childinfo0)! == ch0_bytes
318
319 childinfo1 := ChildInformation{
320 name: Name.new(NameEntry{
321 given_name: asn1.VisibleString.new('Susan')!
322 initial: asn1.VisibleString.new('B')!
323 family_name: asn1.VisibleString.new('Jones')!
324 })!
325 date_of_birth: Date.new(asn1.VisibleString.new('19590717')!)!
326 }
327 // 31 1F 61 11 1A 05 'Susan' => 53 75 73 61 6e
328 // 1A 01 'B' => 42
329 // 1A 05 'Jones' => 4a 6f 6e 65 73
330 // A0 0A 43 08 '19590717' => 31 39 35 39 30 37 31 37
331 ch1_bytes := [u8(0x31), 0x1F, 0x61, 0x11, 0x1A, 0x05, 0x53, 0x75, 0x73, 0x61, 0x6e, 0x1A, 0x01,
332 0x42, 0x1A, 0x05, 0x4a, 0x6f, 0x6e, 0x65, 0x73, 0xA0, 0x0A, 0x43, 0x08, 0x31, 0x39, 0x35,
333 0x39, 0x30, 0x37, 0x31, 0x37]
334 assert asn1.encode(childinfo1)! == ch1_bytes
335
336 // PersonnelRecord.children
337 children := asn1.SequenceOf.from_list[ChildInformation]([childinfo0, childinfo1])!
338 // A3 42 31 1F 61 11 1A 05 'Ralph' => 52 61 6c 70 68 // children
339 // 1A 01 'T' => 54
340 // 1A 05 'Smith' => 53 6d 69 74 68
341 // A0 0A 43 08 '19571111' => 31 39 35 37 31 31 31 31
342 // 31 1F 61 11 1A 05 'Susan' => 53 75 73 61 6e
343 // 1A 01 'B' => 42
344 // 1A 05 'Jones' => 4a 6f 6e 65 73
345 // A0 0A 43 08 '19590717' => 31 39 35 39 30 37 31 37
346 children_bytes := [u8(0xA3), 0x42, u8(0x31), 0x1F, 0x61, 0x11, 0x1A, 0x05, 0x52, 0x61, 0x6c,
347 0x70, 0x68, 0x1A, 0x01, 0x54, 0x1A, 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68, 0xA0, 0x0A, 0x43,
348 0x08, 0x31, 0x39, 0x35, 0x37, 0x31, 0x31, 0x31, 0x31, u8(0x31), 0x1F, 0x61, 0x11, 0x1A,
349 0x05, 0x53, 0x75, 0x73, 0x61, 0x6e, 0x1A, 0x01, 0x42, 0x1A, 0x05, 0x4a, 0x6f, 0x6e, 0x65,
350 0x73, 0xA0, 0x0A, 0x43, 0x08, 0x31, 0x39, 0x35, 0x39, 0x30, 0x37, 0x31, 0x37]
351 assert asn1.encode_with_options(children, 'context_specific: 3; implicit; inner:16')! == children_bytes
352
353 // PersonnelRecord entries
354 pr := PersonnelRecord{
355 name: pr_name
356 title: title
357 number: emp_num
358 date_of_hire: doh
359 name_of_spouse: nosp
360 children: children
361 }
362
363 mut expected_record_bytes := [u8(0x60), 0x81, 0x85]
364 expected_record_bytes << pr_name_bytes
365 expected_record_bytes << title_bytes
366 expected_record_bytes << emp_bytes
367 expected_record_bytes << doh_bytes
368 expected_record_bytes << nosp_bytes
369 expected_record_bytes << children_bytes
370
371 // PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
372 pr_record_output := asn1.encode_with_options(pr, 'application:0;implicit;inner:17')!
373 assert pr_record_output == expected_record_bytes
374
375 pr_record := PersonnelRecord.decode(pr_record_output)!
376 pr_record_encoded_back :=
377 asn1.encode_with_options(pr_record, 'application:0;implicit;inner:17')!
378
379 dump(pr_record_encoded_back == expected_record_bytes) // true
380}
381