Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,18 @@
|
||||
This file contains examples on about how to use this `asn1` module to write your ASN.1 element,
|
||||
how to serialize it and write routines how to decode it. Its contains 4 of examples, ie:
|
||||
|
||||
- `example0.v` contains examples on how to write simple thing for some parts of
|
||||
Kerberos Network Authentication protocol parser, how to define your structure
|
||||
based on the ASN.1 Schema, use tagged field for wrapping element, how to do serialization
|
||||
and deserialization of this object, etc.
|
||||
|
||||
- `example1.v` contains sample from already availables on the internet world, taken from
|
||||
[oss](https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html).
|
||||
Its contains more complex structure, write serializer and deserializer for that.
|
||||
|
||||
- `example2.v` contains samples encoding for DER mode comes from `ITU X.690 Document`,
|
||||
Especially from Annex A. Example of encodings of the document.
|
||||
This examples contains some complex nested elements wrapping detailed in step by step
|
||||
for building the ASN.1 DER serializer with this modules.
|
||||
|
||||
- `examples3.v` is an example from README files.
|
||||
@@ -0,0 +1,113 @@
|
||||
module main
|
||||
|
||||
import x.encoding.asn1
|
||||
|
||||
// Previously, defined type KerberosString = asn1.GeneralString directly
|
||||
// without reduplicating required Element methods, would produces
|
||||
// RUNTIME ERROR: invalid memory access.
|
||||
// **Updates**
|
||||
// Its has been resolved in [22901](https://github.com/vlang/v/issues/22901)
|
||||
// Thanks to @felipensp
|
||||
type KerberosString = asn1.GeneralString
|
||||
|
||||
fn KerberosString.new(s string) !KerberosString {
|
||||
return KerberosString(asn1.GeneralString.new(s)!)
|
||||
}
|
||||
|
||||
type KerberosStringList = []KerberosString
|
||||
|
||||
fn (ksl KerberosStringList) tag() asn1.Tag {
|
||||
return asn1.default_sequence_tag
|
||||
}
|
||||
|
||||
fn (ksl KerberosStringList) payload() ![]u8 {
|
||||
mut out := []u8{}
|
||||
for item in ksl {
|
||||
// maybe produces x00000000: at ???: RUNTIME ERROR: invalid memory access
|
||||
// `item` cannot be used as interface object outside `unsafe` blocks as it
|
||||
// might be stored on stack. Consider declaring `KerberosString` as `@[heap]`
|
||||
obj := unsafe { item }
|
||||
out << asn1.encode(obj)!
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// PrincipalName ::= SEQUENCE {
|
||||
// name-type [0] Int32,
|
||||
// name-string [1] SEQUENCE OF KerberosString
|
||||
// }
|
||||
struct PrincipalName {
|
||||
name_type asn1.Integer @[context_specific: 0; explicit; inner: 2] // integer tag = (universal, false, 2)
|
||||
|
||||
// defines name_string as asn1.SequenceOf[KerberosString] would produces unexpected result
|
||||
// when you build payload with `asn1.make_payload`,
|
||||
// see issues at https://github.com/vlang/v/issues/22721
|
||||
// so we build with KerberosStringList
|
||||
name_string KerberosStringList @[context_specific: 1; explicit; inner: 16] // sequence tag = (universal, true, 16)
|
||||
}
|
||||
|
||||
fn (pn PrincipalName) tag() asn1.Tag {
|
||||
return asn1.default_sequence_tag
|
||||
}
|
||||
|
||||
fn (pn PrincipalName) payload() ![]u8 {
|
||||
kd := asn1.new_key_default()
|
||||
payload := asn1.make_payload[PrincipalName](pn, kd)!
|
||||
return payload
|
||||
}
|
||||
|
||||
fn PrincipalName.decode(bytes []u8) !PrincipalName {
|
||||
// decode should produces Sequence type
|
||||
elem := asn1.decode(bytes)!
|
||||
assert elem.tag().equal(asn1.default_sequence_tag)
|
||||
|
||||
// cast it into Sequence type and get the fields
|
||||
seq := elem.into_object[asn1.Sequence]()!
|
||||
fields := seq.fields()
|
||||
|
||||
// every fields of the sequence is raw of wrapped element, so we should unwrap it with
|
||||
// the same options used to wrap in encode step, and turn to the real underlying object.
|
||||
el_name_type := fields[0].unwrap_with_options('context_specific: 0; explicit; inner: 2')!
|
||||
name_type := el_name_type.into_object[asn1.Integer]()!
|
||||
|
||||
// tag 16 is sequence tag, its decoded into Sequence type
|
||||
el_name_string := fields[1].unwrap_with_options('context_specific: 1; explicit; inner: 16')!
|
||||
el_seq := el_name_string.into_object[asn1.Sequence]()!
|
||||
|
||||
// KerberosString string has a general_string tag, so it would be parsed as asn1.GeneralString
|
||||
// so, we transforms it into KerberosString back and build KerberosStringList.
|
||||
mut a := []KerberosString{}
|
||||
for item in el_seq.fields() {
|
||||
gst := item.into_object[asn1.GeneralString]()!
|
||||
obj := KerberosString(gst)
|
||||
a << obj
|
||||
}
|
||||
|
||||
name_string_list := KerberosStringList(a)
|
||||
|
||||
return PrincipalName{
|
||||
name_type: name_type
|
||||
name_string: name_string_list
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Basically this is a Kerberos PrincipalName data
|
||||
data := [u8(0x30), 0x15, 0xa0, 0x03, 0x02, 0x01, 0x01, 0xa1, 0x0e, 0x30, 0x0c, 0x1b, 0x0a,
|
||||
0x62, 0x6f, 0x62, 0x62, 0x61, 0x2d, 0x66, 0x65, 0x74, 0x74]
|
||||
els := [KerberosString.new('bobba-fett')!]
|
||||
|
||||
pn := PrincipalName{
|
||||
name_type: asn1.Integer.from_int(1)
|
||||
name_string: KerberosStringList(els)
|
||||
}
|
||||
|
||||
out1 := asn1.encode(pn)!
|
||||
back := PrincipalName.decode(data)!
|
||||
dump(back == pn) // should assert to true
|
||||
dump(out1.hex() == data.hex()) // should assert to true
|
||||
|
||||
// run this produces:
|
||||
// [examples/examples_0.v:128] back == pn: true
|
||||
// [examples/examples_0.v:129] out1.hex() == data.hex(): true
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
module main
|
||||
|
||||
import x.encoding.asn1
|
||||
|
||||
// This example was taken from https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/sequence.html
|
||||
// But little modified with removed optional key in the structure and serialization.
|
||||
// Example schema:
|
||||
//
|
||||
// ModuleName DEFINITIONS AUTOMATIC TAGS ::= BEGIN
|
||||
// PersonelEntry ::= SEQUENCE {
|
||||
// name OCTET STRING,
|
||||
// location INTEGER { home(0), field(1), roving(2)} OPTIONAL,
|
||||
// age INTEGER OPTIONAL
|
||||
// }
|
||||
// END
|
||||
|
||||
// The syntax above is equivalent with the following one:
|
||||
|
||||
// PersonelEntry ::= SEQUENCE {
|
||||
// name [0] IMPLICIT OCTET STRING,
|
||||
// location [1] IMPLICIT INTEGER {home(0), field(1), roving(2)} OPTIONAL,
|
||||
// age [2] IMPLICIT INTEGER OPTIONAL
|
||||
// }
|
||||
struct PersonelEntry {
|
||||
mut:
|
||||
name asn1.OctetString @[context_specific: 0; implicit; inner: 4]
|
||||
location asn1.Integer @[context_specific: 1; implicit; inner: 2]
|
||||
age asn1.Integer @[context_specific: 2; implicit; inner: 2]
|
||||
}
|
||||
|
||||
fn (pr PersonelEntry) tag() asn1.Tag {
|
||||
return asn1.default_sequence_tag
|
||||
}
|
||||
|
||||
fn (pr PersonelEntry) payload() ![]u8 {
|
||||
mut out := []u8{}
|
||||
|
||||
// by default, in .der, optional element was not included in the output, so, we remove optional options here.
|
||||
out << asn1.encode_with_options(pr.name, 'context_specific:0; implicit; inner:4')!
|
||||
out << asn1.encode_with_options(pr.location, 'context_specific:1; implicit; inner:2')!
|
||||
// the example the third element is optional, but in .der it would not be serializable until you set it to present
|
||||
out << asn1.encode_with_options(pr.age, 'context_specific:2; implicit; inner:2')!
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// This is an example of way how we can write routine for decode PersonelEntry from bytes.
|
||||
fn PersonelEntry.decode(bytes []u8) !PersonelEntry {
|
||||
// decode should produces Sequence type
|
||||
elem := asn1.decode(bytes)!
|
||||
assert elem.tag().equal(asn1.default_sequence_tag)
|
||||
|
||||
// cast it into Sequence type and get the fields
|
||||
seq := elem as asn1.Sequence
|
||||
fields := seq.fields()
|
||||
|
||||
// every fields of the sequence is raw of wrapped element, so we should unwrap it with
|
||||
// the same options used to wrap in encode step, and turn to the real underlying object.
|
||||
el_name := fields[0].unwrap_with_options('context_specific:0; implicit; inner:4')!
|
||||
name := el_name.into_object[asn1.OctetString]()!
|
||||
|
||||
el_location := fields[1].unwrap_with_options('context_specific:1; implicit; inner:2')!
|
||||
location := el_location.into_object[asn1.Integer]()!
|
||||
|
||||
el_age := fields[2].unwrap_with_options('context_specific:2; implicit; inner:2')!
|
||||
age := el_age.into_object[asn1.Integer]()!
|
||||
|
||||
return PersonelEntry{
|
||||
name: name
|
||||
location: location
|
||||
age: age
|
||||
}
|
||||
}
|
||||
|
||||
// expected output :
|
||||
// 30 10
|
||||
// 80 08 6269672068656164 // bytestr: 'big head'
|
||||
// 81 01 02
|
||||
// 82 01 1A
|
||||
fn main() {
|
||||
expected_output := [u8(0x30), 0x10, u8(0x80), 0x08, 0x62, 0x69, 0x67, 0x20, 0x68, 0x65, 0x61,
|
||||
0x64, u8(0x81), 0x01, 0x02, u8(0x82), 0x01, 0x1A]
|
||||
|
||||
rock_star1 := PersonelEntry{
|
||||
name: asn1.OctetString.from_hexstring('6269672068656164')!
|
||||
location: asn1.Integer.from_int(2)
|
||||
age: asn1.Integer.from_int(26)
|
||||
}
|
||||
|
||||
// serializes the object into bytes array and check the result
|
||||
out := asn1.encode(rock_star1)!
|
||||
dump(out == expected_output) // out == expected_output: true
|
||||
|
||||
// deserializes bytes back into PersonelEntry object.
|
||||
out_back := PersonelEntry.decode(out)!
|
||||
dump(out_back)
|
||||
// out_back: PersonelEntry{
|
||||
// name: OctetString (big head)
|
||||
// location: Integer 2
|
||||
// age: Integer 26
|
||||
// }
|
||||
dump(out_back.name == rock_star1.name) // true
|
||||
dump(out_back.location == rock_star1.location) // true
|
||||
dump(out_back.age == rock_star1.age) // true
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
module main
|
||||
|
||||
import x.encoding.asn1
|
||||
|
||||
// This example of encode a sequence containing a UTF-8 string, an integer
|
||||
// and an explicitly tagged object identifier, conforming to the following
|
||||
// ASN.1 schema specification:
|
||||
|
||||
// ```asn.1
|
||||
// Example ::= SEQUENCE {
|
||||
// greeting UTF8String,
|
||||
// answer INTEGER,
|
||||
// type [1] EXPLICIT OBJECT IDENTIFIER
|
||||
// }
|
||||
// ```
|
||||
|
||||
// You can represent above structure with related structure in `v`, similar like:
|
||||
|
||||
struct Example {
|
||||
greeting asn1.Utf8String
|
||||
answer asn1.Integer
|
||||
// you can tag your struct fields with supported options.
|
||||
tipe asn1.ObjectIdentifier @[context_specific: 1; explicit; inner: 6]
|
||||
}
|
||||
|
||||
fn (ex Example) tag() asn1.Tag {
|
||||
return asn1.default_sequence_tag
|
||||
}
|
||||
|
||||
// you can build your payload manually or use `asn1.make_payload`, but with aware,
|
||||
// if your structure contains generic, its maybe not work (currently).
|
||||
fn (ex Example) payload() ![]u8 {
|
||||
kd := asn1.KeyDefault(map[string]asn1.Element{})
|
||||
payload := asn1.make_payload[Example](ex, kd)!
|
||||
return payload
|
||||
}
|
||||
|
||||
// You can write routines for deserialize Example structure. This is only examples way,
|
||||
// but its possible to use other way with the help from this module, like use
|
||||
// `Parser` codec.
|
||||
fn Example.decode(bytes []u8) !Example {
|
||||
// just call raw .decode on bytes
|
||||
// by example, its should produce sequence type.
|
||||
elem := asn1.decode(bytes)!
|
||||
assert elem.tag().equal(asn1.default_sequence_tag) // should true
|
||||
|
||||
// cast produced element into Sequence type and get the fields.
|
||||
seq := elem.into_object[asn1.Sequence]()!
|
||||
fields := seq.fields()
|
||||
|
||||
// and then, turn every field into desired object based your schema.
|
||||
// first two field is not wrapped element, so just turn into real object
|
||||
greeting := fields[0].into_object[asn1.Utf8String]()!
|
||||
answer := fields[1].into_object[asn1.Integer]()!
|
||||
|
||||
// the third field is context_specific wrapped element, just unwrap it with the
|
||||
// same options used to encode
|
||||
oid_tipe := fields[2].unwrap_with_options('context_specific:1;explicit; inner:6')!
|
||||
tipe := oid_tipe.into_object[asn1.ObjectIdentifier]()!
|
||||
|
||||
// then build your Example structure
|
||||
ex := Example{
|
||||
greeting: greeting
|
||||
answer: answer
|
||||
tipe: tipe
|
||||
}
|
||||
return ex
|
||||
}
|
||||
|
||||
fn main() {
|
||||
expected_output := [u8(0x30), 18, u8(12), 5, 72, 101, 108, 108, 111, u8(2), 1, 42, u8(0xA1),
|
||||
6, 6, 4, 43, 6, 1, 3]
|
||||
ex := Example{
|
||||
greeting: asn1.Utf8String.new('Hello')!
|
||||
answer: asn1.Integer.from_int(42)
|
||||
tipe: asn1.ObjectIdentifier.new('1.3.6.1.3')!
|
||||
}
|
||||
|
||||
// serialize the Example object
|
||||
out := asn1.encode(ex)!
|
||||
assert out == expected_output
|
||||
|
||||
// test with data
|
||||
example_obj := Example.decode(out)!
|
||||
dump(ex.greeting == example_obj.greeting)
|
||||
dump(ex.answer == example_obj.answer)
|
||||
dump(ex.tipe == example_obj.tipe)
|
||||
}
|
||||
Reference in new issue
Block a user