v2 / vlib / x / encoding / asn1 / element_encode.v
192 lines · 175 sloc · 6.35 KB · 94905820e6da47fbb60eb4f30a3c553ba6d73a95
Raw
1// Copyright (c) 2022, 2024 blackshirt. All rights reserved.
2// Use of this source code is governed by a MIT License
3// that can be found in the LICENSE file.
4module asn1
5
6// Handling functionality of Element's serialization.
7//
8
9// `encode` serializes element into bytes array. By default, its encode in .der rule with empty options.
10// See `encode_with_options` if you want pass an option string. See `field.v` for more option in detail.
11//
12// Examples:
13//
14// ```v
15// import x.encoding.asn1
16//
17// obj := asn1.Utf8String.new('hi')!
18// out := asn1.encode(obj)!
19// assert out == [u8(0x0C), 0x02, 0x68, 0x69]
20// ```
21pub fn encode(el Element) ![]u8 {
22 // without options, we call `.encode_with_rule` directly on element.
23 return encode_with_rule(el, .der)!
24}
25
26// `encode_with_options` serializes element into bytes array with options string passed to drive the result.
27//
28// Examples:
29//
30// `Utf8String` defined as `[5] IMPLICIT UTF8String` was encoded into `85 02 68 69`.
31// `Utf8String` defined as `[5] EXPLICIT UTF8String` was encoded into `A5 04 0C 02 68 69`.
32//
33// ```v
34// obj := asn1.Utf8String.new('hi')!
35// implicit_out := asn1.encode_with_options(obj, 'context_specific:5;implicit;inner:12')!
36// assert implicit_out == [u8(0x85), 0x02, 0x68, 0x69]
37//
38// explicit_out := asn1.encode_with_options(obj, 'context_specific:5;explicit;inner:0x0c')!
39// assert explicit_out == [u8(0xA5), 0x04, 0x0C, 0x02, 0x68, 0x69]
40// ```
41pub fn encode_with_options(el Element, opt string) ![]u8 {
42 // treated as without option when empty
43 if opt.len == 0 {
44 return encode_with_rule(el, .der)!
45 }
46 fo := FieldOptions.from_string(opt)!
47 return encode_with_field_options(el, fo)!
48}
49
50// `encode_with_field_options` serializes this element into bytes array with options defined in fo.
51pub fn encode_with_field_options(el Element, fo FieldOptions) ![]u8 {
52 // validates options again this element.
53 el.validate_options(fo)!
54
55 // check for default_value for this element
56 // if we have it matching with current element,
57 // by default, in .der mode, it should not be serialized.
58 if fo.has_default {
59 def_element := fo.default_value or { return error('bad default_value') }
60 // If this element is equal with default_value, by default its should not be serialized.
61 if el.equal(def_element) {
62 return []u8{}
63 }
64 }
65
66 // apply field options, turns this element
67 // into optional, wrapped element or original one.
68 new_el := el.apply_field_options(fo)!
69
70 // if new_el is Optional, encode with optional behaviour
71 if new_el is Optional {
72 return new_el.encode()!
73 }
74 // otherwise, just serializing it
75 return encode_with_rule(new_el, .der)!
76}
77
78// Helper for wrapping element
79//
80//
81// into_optional turns this element into Optional with present bit.
82// When you set with_present into true, its makes this optional was present.
83fn (el Element) into_optional(with_present bool) !Element {
84 if el is Optional {
85 return error('already optional element')
86 }
87 return Optional.new(el, with_present)!
88}
89
90// apply_field_options applies rules in field options into current element
91// and turns this into another element.
92// by default, optional attribute is more higher precedence over wrapper attribut, ie,
93// take the wrap step and then turn into optional (if true)
94fn (el Element) apply_field_options(fo FieldOptions) !Element {
95 el.validate_options(fo)!
96 // if there a wrapper
97 if fo.cls != '' {
98 wrapped_el := el.wrap_with_options(fo)!
99 if fo.optional {
100 return wrapped_el.into_optional(fo.present)!
101 }
102 // not-optional, just return wrapped element
103 return wrapped_el
104 }
105 // no-wrapper, check for optional
106 if fo.optional {
107 return el.into_optional(fo.present)!
108 }
109 // otherwise, its no-wrapper and non-optional
110 return el
111}
112
113// set_default_value installs default value within FieldOptions for the element
114pub fn (el Element) set_default_value(mut fo FieldOptions, value Element) ! {
115 // the default tag should match with the current tag
116 if !el.tag().equal(value.tag()) {
117 return error('unmatching tag of default value')
118 }
119 fo.install_default(value, false)!
120 el.validate_default(fo)!
121}
122
123// wrap_with_rule wraps universal element into another class.
124// we prohibit dan defines some rules when its happen and returns an error instead
125// 1. wrapping into .universal class is not allowed
126// 2. wrapping with the same class is not allowed too
127// 3. wrapping non-universal class element is not allowed (maybe removed on futures.)
128// Notes :
129// Three additional information about tagging:
130// CHOICEs are always explicitly tagged even if implicit tagging is in effect.
131// EXPLICIT TAGs are always constructed, they encapsulate the TLV they prefix.
132// An IMPLICIT TAG 'inherits' the constructed bit of the TLV whose 'T' is overwritten,
133// examples:
134// a) '[5] IMPLICIT INTEGER' has tag 0x85 (overwriting 0x02 = INTEGER)
135// b) '[5] IMPLICIT SEQUENCE' has tag 0xA5 (overwriting 0x30 = SEQUENCE, CONSTRUCTED)
136fn (el Element) wrap_with_options(fo FieldOptions) !Element {
137 // validates options.
138 el.validate_options(fo)!
139
140 mode := TaggedMode.from_string(fo.mode)!
141 cls := TagClass.from_string(fo.cls)!
142
143 return wrap(el, cls, fo.tagnum, mode)!
144}
145
146// wrao performs wrapping to element and turns this element into another one.
147fn wrap(el Element, cls TagClass, number int, mode TaggedMode) !Element {
148 if el is Optional {
149 return error('Optional cant be wrapped')
150 }
151 if cls == .universal {
152 return error('you cant wrap into universal')
153 }
154 match cls {
155 .context_specific {
156 return ContextElement.from_element(el, number, mode)!
157 }
158 .application {
159 return ApplicationElement.from_element(el, number, mode)!
160 }
161 .private {
162 return PrivateELement.from_element(el, number, mode)!
163 }
164 else {
165 return error('Wraps to the wrong class')
166 }
167 }
168}
169
170// encode_with_rule encodes element into bytes array with rule.
171fn encode_with_rule(el Element, rule EncodingRule) ![]u8 {
172 if rule != .der && rule != .ber {
173 return error('Element: unsupported rule')
174 }
175 mut dst := []u8{}
176
177 // when this element is Optional without presence flag, by default would
178 // serialize this element into empty bytes otherwise, would serialize underlying element.
179 if el is Optional {
180 return el.encode()!
181 }
182 // otherwise, just serializes as normal
183 el.tag().encode_with_rule(mut dst, rule)!
184 // calculates the length of element, and serialize this length
185 payload := el.payload()!
186 length := Length.new(payload.len)!
187 length.encode_with_rule(mut dst, rule)!
188 // append the element payload to destination
189 dst << payload
190
191 return dst
192}
193