mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
374 lines
13 KiB
V
374 lines
13 KiB
V
module main
|
||
|
||
import x.encoding.asn1
|
||
|
||
// This example takes more complex scenario, in the sense of nested wrapping, the use of
|
||
// other class Element supported in this module.
|
||
//
|
||
// This examples is taken from ITU-T X.690 Information technology – ASN.1 encoding rules:
|
||
// Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and
|
||
// Distinguished Encoding Rules (DER) document.
|
||
//
|
||
// Especially from Annex A. Example of encodings of the document.
|
||
|
||
// from A.1 ASN.1 description of the record structure.
|
||
// The structure of the hypothetical personnel record is formally described below using ASN.1 specified in
|
||
// ITU-T Rec. X.680 | ISO/IEC 8824-1 for defining types.
|
||
//
|
||
// PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
|
||
// name Name,
|
||
// title [0] VisibleString,
|
||
// number EmployeeNumber,
|
||
// dateOfHire [1] Date,
|
||
// nameOfSpouse [2] Name,
|
||
// children [3] IMPLICIT SEQUENCE OF ChildInformation DEFAULT {}
|
||
// }
|
||
//
|
||
struct PersonnelRecord {
|
||
name Name
|
||
title asn1.VisibleString
|
||
number EmployeeNumber
|
||
date_of_hire Date
|
||
name_of_spouse Name
|
||
children asn1.SequenceOf[ChildInformation]
|
||
}
|
||
|
||
fn (pr PersonnelRecord) tag() asn1.Tag {
|
||
return asn1.default_set_tag
|
||
}
|
||
|
||
fn (pr PersonnelRecord) payload() ![]u8 {
|
||
mut out := []u8{}
|
||
out << asn1.encode(pr.name)!
|
||
out << asn1.encode_with_options(pr.title, 'context_specific:0;explicit;inner:26')!
|
||
out << asn1.encode(pr.number)!
|
||
out << asn1.encode_with_options(pr.date_of_hire, 'context_specific: 1; explicit; inner:application,false,3')!
|
||
out << asn1.encode_with_options(pr.name_of_spouse, 'context_specific: 2; explicit; inner:application,true,1')!
|
||
out << asn1.encode_with_options(pr.children, 'context_specific: 3; implicit; inner:16')!
|
||
|
||
return out
|
||
}
|
||
|
||
// deserializer of PersonellRecord bytes
|
||
fn PersonnelRecord.decode(bytes []u8) !PersonnelRecord {
|
||
// we perfrom decoding as a reverse of the serialization with the same options.
|
||
// after that, we get an Element (with the Set type of PersonellRecord)
|
||
el := asn1.decode_with_options(bytes, 'application:0;implicit;inner:17')!
|
||
assert el.tag() == asn1.default_set_tag
|
||
set := el.into_object[asn1.Set]()!
|
||
|
||
// fields is series of element ([]Element), PersonellRecord fields
|
||
fields := set.fields()
|
||
|
||
// we turn series of element into underlying desired type.
|
||
// its rather clumsy to transforms it, because Sequenve (and Set) fields is an []Element, so you should care
|
||
// to turn this into desired object
|
||
name_app := fields[0].into_object[asn1.ApplicationElement]()!
|
||
name := Name(name_app)
|
||
|
||
// Title
|
||
title_elem := fields[1].unwrap_with_options('context_specific:0;explicit;inner:26')!
|
||
title := title_elem.into_object[asn1.VisibleString]()!
|
||
|
||
// EmployeNumber
|
||
emp_num := fields[2].into_object[asn1.ApplicationElement]()!
|
||
employe_number := EmployeeNumber(emp_num)
|
||
|
||
// dateOfHire
|
||
doh := fields[3].unwrap_with_options('context_specific: 1; explicit; inner:application,false,3')!
|
||
doh_app := doh.into_object[asn1.ApplicationElement]()!
|
||
date_of_hire := Date(doh_app)
|
||
|
||
// nameOfSpouse
|
||
nosp := fields[4].unwrap_with_options('context_specific: 2; explicit; inner:application,true,1')!
|
||
nosp_app := nosp.into_object[asn1.ApplicationElement]()!
|
||
name_of_spouse := Name(nosp_app)
|
||
|
||
// The children is Sequence of Set, first we unwrap it then turn into sequence.
|
||
children := fields[5].unwrap_with_options('context_specific: 3; implicit; inner:16')!
|
||
children_seq := children.into_object[asn1.Sequence]()!
|
||
childset_fields := children_seq.fields() // []Element
|
||
|
||
mut childset := []ChildInformation{}
|
||
for item in childset_fields {
|
||
// item is an Element
|
||
obj := item.into_object[asn1.Set]()!
|
||
i := ChildInformation.from_set(obj)!
|
||
childset << i
|
||
}
|
||
|
||
pr := PersonnelRecord{
|
||
name: name
|
||
title: title
|
||
number: employe_number
|
||
date_of_hire: date_of_hire
|
||
name_of_spouse: name_of_spouse
|
||
children: asn1.SequenceOf.from_list[ChildInformation](childset)!
|
||
}
|
||
return pr
|
||
}
|
||
|
||
// ChildInformation ::= SET {
|
||
// name Name,
|
||
// dateOfBirth [0] Date
|
||
// }
|
||
//
|
||
// Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
|
||
// givenName VisibleString,
|
||
// initial VisibleString,
|
||
// familyName VisibleString
|
||
// }
|
||
//
|
||
// EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER
|
||
// Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD
|
||
|
||
// ChildInformation ::= SET {
|
||
// name Name,
|
||
// dateOfBirth [0] Date
|
||
// }
|
||
struct ChildInformation {
|
||
name Name
|
||
date_of_birth Date
|
||
}
|
||
|
||
// s should Set with series of Element with []ChildInformation within underlying fields.
|
||
fn ChildInformation.from_set(s asn1.Set) !ChildInformation {
|
||
if s.fields().len != 2 {
|
||
return error('Bad ChildInformation set')
|
||
}
|
||
fields := s.fields() // serialized name and dateOfBirth of ChildInformation
|
||
name := fields[0].into_object[asn1.ApplicationElement]()!
|
||
doh := fields[1].unwrap_with_options('context_specific: 0; explicit; inner:application,false,3')!
|
||
date := doh.into_object[asn1.ApplicationElement]()!
|
||
ch := ChildInformation{
|
||
name: Name(name)
|
||
date_of_birth: Date(date)
|
||
}
|
||
return ch
|
||
}
|
||
|
||
fn (ci ChildInformation) tag() asn1.Tag {
|
||
return asn1.default_set_tag
|
||
}
|
||
|
||
fn (ci ChildInformation) payload() ![]u8 {
|
||
mut out := []u8{}
|
||
out << asn1.encode(ci.name)!
|
||
out << asn1.encode_with_options(ci.date_of_birth, 'context_specific: 0; explicit; inner:application,false,3')!
|
||
|
||
return out
|
||
}
|
||
|
||
// EmployeeNumber ::= [APPLICATION 2] IMPLICIT INTEGER
|
||
type EmployeeNumber = asn1.ApplicationElement
|
||
|
||
fn EmployeeNumber.new(val asn1.Integer) !asn1.ApplicationElement {
|
||
return asn1.ApplicationElement.from_element(val, 2, .implicit)!
|
||
}
|
||
|
||
// Date ::= [APPLICATION 3] IMPLICIT VisibleString -- YYYYMMDD
|
||
type Date = asn1.ApplicationElement
|
||
|
||
fn Date.new(val asn1.VisibleString) !asn1.ApplicationElement {
|
||
return asn1.ApplicationElement.from_element(val, 3, .implicit)!
|
||
}
|
||
|
||
// Name ::= [APPLICATION 1] IMPLICIT SEQUENCE {
|
||
// givenName VisibleString,
|
||
// initial VisibleString,
|
||
// familyName VisibleString
|
||
// }
|
||
type Name = asn1.ApplicationElement
|
||
|
||
fn Name.new(el NameEntry) !asn1.ApplicationElement {
|
||
return asn1.ApplicationElement.from_element(el, 1, .implicit)!
|
||
}
|
||
|
||
struct NameEntry {
|
||
given_name asn1.VisibleString
|
||
initial asn1.VisibleString
|
||
family_name asn1.VisibleString
|
||
}
|
||
|
||
fn (n NameEntry) tag() asn1.Tag {
|
||
return asn1.default_sequence_tag
|
||
}
|
||
|
||
fn (n NameEntry) payload() ![]u8 {
|
||
mut out := []u8{}
|
||
out << asn1.encode(n.given_name)!
|
||
out << asn1.encode(n.initial)!
|
||
out << asn1.encode(n.family_name)!
|
||
|
||
return out
|
||
}
|
||
|
||
// The value of John Smith's personnel record is formally described below using ASN.1.
|
||
// { name {givenName "John",initial "P",familyName "Smith"},
|
||
// title "Director",
|
||
// number 51,
|
||
// dateOfHire "19710917",
|
||
// nameOfSpouse {givenName "Mary",initial "T",familyName "Smith"},
|
||
// children {
|
||
// { name {givenName "Ralph",initial "T",familyName "Smith"},
|
||
// dateOfBirth "19571111"
|
||
// },
|
||
// { name {givenName "Susan",initial "B",familyName "Jones"},
|
||
// dateOfBirth "19590717"
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// Representation of this record value
|
||
//
|
||
// 60 8185
|
||
// 61 10 1A 04 'John' // name
|
||
// 1A 01 'P'
|
||
// 1A 05 'Smith'
|
||
// A0 0A 1A 08 'Director' // title
|
||
// 42 01 33 // number
|
||
// A1 0A 43 08 '19710917' // dateOfHire
|
||
// A2 12 61 10 1A 04 'Mary' // nameOfSpouse
|
||
// 1A 01 'T'
|
||
// 1A 05 'Smith'
|
||
// A3 42 31 1F 61 11 1A 05 'Ralph' => 52 61 6c 70 68 // children
|
||
// 1A 01 'T' => 54
|
||
// 1A 05 'Smith' => 53 6d 69 74 68
|
||
// A0 0A 43 08 '19571111' => 31 39 35 37 31 31 31 31
|
||
// 31 1F 61 11 1A 05 'Susan' => 53 75 73 61 6e
|
||
// 1A 01 'B' => 42
|
||
// 1A 05 'Jones' => 4a 6f 6e 65 73
|
||
// A0 0A 43 08 '19590717' => 31 39 35 39 30 37 31 37
|
||
|
||
fn main() {
|
||
// We detailed every pieces of element
|
||
|
||
// PersonnelRecord.name
|
||
pr_name := Name.new(NameEntry{
|
||
given_name: asn1.VisibleString.new('John')!
|
||
initial: asn1.VisibleString.new('P')!
|
||
family_name: asn1.VisibleString.new('Smith')!
|
||
})!
|
||
// 61 10 1A 04 'John' => 4a 6f 68 6e // name
|
||
// 1A 01 'P' => 50
|
||
// 1A 05 'Smith' => 53 6d 69 74 68
|
||
pr_name_bytes := [u8(0x61), 0x10, 0x1A, 0x04, 0x4a, 0x6f, 0x68, 0x6e, 0x1A, 0x01, 0x50, 0x1A,
|
||
0x05, 0x53, 0x6d, 0x69, 0x74, 0x68]
|
||
|
||
assert asn1.encode(pr_name)! == pr_name_bytes
|
||
|
||
// PersonnelRecord.title
|
||
// A0 0A 1A 08 'Director' => 44 69 72 65 63 74 6f 72 // title
|
||
title := asn1.VisibleString.new('Director')!
|
||
title_bytes := [u8(0xA0), 0x0A, 0x1A, 0x08, u8(0x44), 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72]
|
||
assert asn1.encode_with_options(title, 'context_specific:0;explicit;inner:26')! == title_bytes
|
||
|
||
// PersonnelRecord.EmployeeNumber
|
||
// 42 01 33 // number
|
||
emp_num := EmployeeNumber.new(asn1.Integer.from_int(51))!
|
||
emp_bytes := [u8(0x42), 0x01, 0x33]
|
||
assert asn1.encode(emp_num)! == emp_bytes
|
||
|
||
// PersonnelRecord.dateOfHire
|
||
// A1 0A 43 08 '19710917' => 31 39 37 31 30 39 31 37 // dateOfHire
|
||
// dateOfHire [1] Date,
|
||
doh := Date.new(asn1.VisibleString.new('19710917')!)!
|
||
doh_bytes := [u8(0xA1), 0x0A, 0x43, 0x08, 0x31, 0x39, 0x37, 0x31, 0x30, 0x39, 0x31, 0x37]
|
||
assert asn1.encode_with_options(doh, 'context_specific: 1; explicit; inner:application,false,3')! == doh_bytes
|
||
|
||
// PersonnelRecord.nameOfSpouse
|
||
// A2 12 61 10 1A 04 'Mary' => 4d 61 72 79 // nameOfSpouse
|
||
// 1A 01 'T' => 54
|
||
// 1A 05 'Smith' => 53 6d 69 74 68
|
||
nosp := Name.new(NameEntry{
|
||
given_name: asn1.VisibleString.new('Mary')!
|
||
initial: asn1.VisibleString.new('T')!
|
||
family_name: asn1.VisibleString.new('Smith')!
|
||
})!
|
||
nosp_bytes := [u8(0xA2), 0x12, 0x61, 0x10, 0x1A, 0x04, 0x4d, 0x61, 0x72, 0x79, 0x1A, 0x01,
|
||
0x54, 0x1A, 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68]
|
||
// nameOfSpouse [2] Name,
|
||
assert asn1.encode_with_options(nosp, 'context_specific: 2; explicit; inner:application,true,1')! == nosp_bytes
|
||
|
||
// { name {givenName "Ralph",initial "T",familyName "Smith"},
|
||
// dateOfBirth "19571111"
|
||
// },
|
||
childinfo0 := ChildInformation{
|
||
name: Name.new(NameEntry{
|
||
given_name: asn1.VisibleString.new('Ralph')!
|
||
initial: asn1.VisibleString.new('T')!
|
||
family_name: asn1.VisibleString.new('Smith')!
|
||
})!
|
||
date_of_birth: Date.new(asn1.VisibleString.new('19571111')!)!
|
||
}
|
||
// 31 1F 61 11 1A 05 'Ralph' => 52 61 6c 70 68
|
||
// 1A 01 'T' => 54
|
||
// 1A 05 'Smith' => 53 6d 69 74 68
|
||
// A0 0A 43 08 '19571111' => 31 39 35 37 31 31 31 31
|
||
ch0_bytes := [u8(0x31), 0x1F, 0x61, 0x11, 0x1A, 0x05, 0x52, 0x61, 0x6c, 0x70, 0x68, 0x1A, 0x01,
|
||
0x54, 0x1A, 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68, 0xA0, 0x0A, 0x43, 0x08, 0x31, 0x39, 0x35,
|
||
0x37, 0x31, 0x31, 0x31, 0x31]
|
||
assert asn1.encode(childinfo0)! == ch0_bytes
|
||
|
||
childinfo1 := ChildInformation{
|
||
name: Name.new(NameEntry{
|
||
given_name: asn1.VisibleString.new('Susan')!
|
||
initial: asn1.VisibleString.new('B')!
|
||
family_name: asn1.VisibleString.new('Jones')!
|
||
})!
|
||
date_of_birth: Date.new(asn1.VisibleString.new('19590717')!)!
|
||
}
|
||
// 31 1F 61 11 1A 05 'Susan' => 53 75 73 61 6e
|
||
// 1A 01 'B' => 42
|
||
// 1A 05 'Jones' => 4a 6f 6e 65 73
|
||
// A0 0A 43 08 '19590717' => 31 39 35 39 30 37 31 37
|
||
ch1_bytes := [u8(0x31), 0x1F, 0x61, 0x11, 0x1A, 0x05, 0x53, 0x75, 0x73, 0x61, 0x6e, 0x1A, 0x01,
|
||
0x42, 0x1A, 0x05, 0x4a, 0x6f, 0x6e, 0x65, 0x73, 0xA0, 0x0A, 0x43, 0x08, 0x31, 0x39, 0x35,
|
||
0x39, 0x30, 0x37, 0x31, 0x37]
|
||
assert asn1.encode(childinfo1)! == ch1_bytes
|
||
|
||
// PersonnelRecord.children
|
||
children := asn1.SequenceOf.from_list[ChildInformation]([childinfo0, childinfo1])!
|
||
// A3 42 31 1F 61 11 1A 05 'Ralph' => 52 61 6c 70 68 // children
|
||
// 1A 01 'T' => 54
|
||
// 1A 05 'Smith' => 53 6d 69 74 68
|
||
// A0 0A 43 08 '19571111' => 31 39 35 37 31 31 31 31
|
||
// 31 1F 61 11 1A 05 'Susan' => 53 75 73 61 6e
|
||
// 1A 01 'B' => 42
|
||
// 1A 05 'Jones' => 4a 6f 6e 65 73
|
||
// A0 0A 43 08 '19590717' => 31 39 35 39 30 37 31 37
|
||
children_bytes := [u8(0xA3), 0x42, u8(0x31), 0x1F, 0x61, 0x11, 0x1A, 0x05, 0x52, 0x61, 0x6c,
|
||
0x70, 0x68, 0x1A, 0x01, 0x54, 0x1A, 0x05, 0x53, 0x6d, 0x69, 0x74, 0x68, 0xA0, 0x0A, 0x43,
|
||
0x08, 0x31, 0x39, 0x35, 0x37, 0x31, 0x31, 0x31, 0x31, u8(0x31), 0x1F, 0x61, 0x11, 0x1A,
|
||
0x05, 0x53, 0x75, 0x73, 0x61, 0x6e, 0x1A, 0x01, 0x42, 0x1A, 0x05, 0x4a, 0x6f, 0x6e, 0x65,
|
||
0x73, 0xA0, 0x0A, 0x43, 0x08, 0x31, 0x39, 0x35, 0x39, 0x30, 0x37, 0x31, 0x37]
|
||
assert asn1.encode_with_options(children, 'context_specific: 3; implicit; inner:16')! == children_bytes
|
||
|
||
// PersonnelRecord entries
|
||
pr := PersonnelRecord{
|
||
name: pr_name
|
||
title: title
|
||
number: emp_num
|
||
date_of_hire: doh
|
||
name_of_spouse: nosp
|
||
children: children
|
||
}
|
||
|
||
mut expected_record_bytes := [u8(0x60), 0x81, 0x85]
|
||
expected_record_bytes << pr_name_bytes
|
||
expected_record_bytes << title_bytes
|
||
expected_record_bytes << emp_bytes
|
||
expected_record_bytes << doh_bytes
|
||
expected_record_bytes << nosp_bytes
|
||
expected_record_bytes << children_bytes
|
||
|
||
// PersonnelRecord ::= [APPLICATION 0] IMPLICIT SET {
|
||
pr_record_output := asn1.encode_with_options(pr, 'application:0;implicit;inner:17')!
|
||
assert pr_record_output == expected_record_bytes
|
||
|
||
pr_record := PersonnelRecord.decode(pr_record_output)!
|
||
pr_record_encoded_back := asn1.encode_with_options(pr_record, 'application:0;implicit;inner:17')!
|
||
|
||
dump(pr_record_encoded_back == expected_record_bytes) // true
|
||
}
|