v/vlib/x/encoding/asn1/examples/examples2.v

374 lines
13 KiB
V
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}