| 1 | module main |
| 2 | |
| 3 | import x.encoding.asn1 |
| 4 | |
| 5 | // This example of encode a sequence containing a UTF-8 string, an integer |
| 6 | // and an explicitly tagged object identifier, conforming to the following |
| 7 | // ASN.1 schema specification: |
| 8 | |
| 9 | // ```asn.1 |
| 10 | // Example ::= SEQUENCE { |
| 11 | // greeting UTF8String, |
| 12 | // answer INTEGER, |
| 13 | // type [1] EXPLICIT OBJECT IDENTIFIER |
| 14 | // } |
| 15 | // ``` |
| 16 | |
| 17 | // You can represent above structure with related structure in `v`, similar like: |
| 18 | |
| 19 | struct Example { |
| 20 | greeting asn1.Utf8String |
| 21 | answer asn1.Integer |
| 22 | // you can tag your struct fields with supported options. |
| 23 | tipe asn1.ObjectIdentifier @[context_specific: 1; explicit; inner: 6] |
| 24 | } |
| 25 | |
| 26 | fn (ex Example) tag() asn1.Tag { |
| 27 | return asn1.default_sequence_tag |
| 28 | } |
| 29 | |
| 30 | // you can build your payload manually or use `asn1.make_payload`, but with aware, |
| 31 | // if your structure contains generic, its maybe not work (currently). |
| 32 | fn (ex Example) payload() ![]u8 { |
| 33 | kd := asn1.KeyDefault(map[string]asn1.Element{}) |
| 34 | payload := asn1.make_payload[Example](ex, kd)! |
| 35 | return payload |
| 36 | } |
| 37 | |
| 38 | // You can write routines for deserialize Example structure. This is only examples way, |
| 39 | // but its possible to use other way with the help from this module, like use |
| 40 | // `Parser` codec. |
| 41 | fn Example.decode(bytes []u8) !Example { |
| 42 | // just call raw .decode on bytes |
| 43 | // by example, its should produce sequence type. |
| 44 | elem := asn1.decode(bytes)! |
| 45 | assert elem.tag().equal(asn1.default_sequence_tag) // should true |
| 46 | |
| 47 | // cast produced element into Sequence type and get the fields. |
| 48 | seq := elem.into_object[asn1.Sequence]()! |
| 49 | fields := seq.fields() |
| 50 | |
| 51 | // and then, turn every field into desired object based your schema. |
| 52 | // first two field is not wrapped element, so just turn into real object |
| 53 | greeting := fields[0].into_object[asn1.Utf8String]()! |
| 54 | answer := fields[1].into_object[asn1.Integer]()! |
| 55 | |
| 56 | // the third field is context_specific wrapped element, just unwrap it with the |
| 57 | // same options used to encode |
| 58 | oid_tipe := fields[2].unwrap_with_options('context_specific:1;explicit; inner:6')! |
| 59 | tipe := oid_tipe.into_object[asn1.ObjectIdentifier]()! |
| 60 | |
| 61 | // then build your Example structure |
| 62 | ex := Example{ |
| 63 | greeting: greeting |
| 64 | answer: answer |
| 65 | tipe: tipe |
| 66 | } |
| 67 | return ex |
| 68 | } |
| 69 | |
| 70 | fn main() { |
| 71 | expected_output := [u8(0x30), 18, u8(12), 5, 72, 101, 108, 108, 111, u8(2), 1, 42, u8(0xA1), |
| 72 | 6, 6, 4, 43, 6, 1, 3] |
| 73 | ex := Example{ |
| 74 | greeting: asn1.Utf8String.new('Hello')! |
| 75 | answer: asn1.Integer.from_int(42) |
| 76 | tipe: asn1.ObjectIdentifier.new('1.3.6.1.3')! |
| 77 | } |
| 78 | |
| 79 | // serialize the Example object |
| 80 | out := asn1.encode(ex)! |
| 81 | assert out == expected_output |
| 82 | |
| 83 | // test with data |
| 84 | example_obj := Example.decode(out)! |
| 85 | dump(ex.greeting == example_obj.greeting) |
| 86 | dump(ex.answer == example_obj.answer) |
| 87 | dump(ex.tipe == example_obj.tipe) |
| 88 | } |
| 89 | |