v2 / vlib / x / encoding / asn1 / other_element.v
373 lines · 333 sloc · 11.13 KB · 021c9535e0cbcd1e528ae89c6d4c95aa3bc7e4e4
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// This file contains structures and routines for (limited) support for
7// other class (type) of ASN.1 Element.
8//
9
10// ASN.1 Raw Element.
11pub struct RawElement {
12mut:
13 // The tag is the (outer) tag of the TLV, if this a wrpper.
14 tag Tag
15 // `content` is the value of a TLV. Its depends on the context.
16 content []u8
17 // Optional fields
18 inner_tag ?Tag
19 mode ?TaggedMode
20 default_value ?Element
21}
22
23// new creates a RawElement from tag and content. If your element is universal class,
24// use universal type constructor instead, provided in this module.
25pub fn RawElement.new(tag Tag, content []u8) !RawElement {
26 if tag.number < 0 || tag.number > max_tag_number {
27 return error('Unallowed tagnum was provided')
28 }
29 // universal class with constructed form only valid for sequence(of) and set(of) type.
30 if tag.class == .universal {
31 if tag.number > max_universal_tagnumber {
32 return error('Tag number for universal class was exceed limit')
33 }
34 if tag.constructed {
35 if tag.number != int(TagType.sequence) && tag.number != int(TagType.set) {
36 return error('unexpected_tag_value required sequence or set number')
37 }
38 }
39 if tag.number == int(TagType.sequence) || tag.number == int(TagType.set) {
40 if !tag.constructed {
41 return error('unexpected_tag_value sequence or set number should in constructed tag')
42 }
43 }
44 }
45 // otherwise, treats as a RawElement
46 return RawElement{
47 tag: tag
48 content: content
49 }
50}
51
52// from_element creates a RawElement from another element (with wrapping semantic).
53pub fn RawElement.from_element(el Element, cls TagClass, tagnum int, mode TaggedMode) !RawElement {
54 // wrapping into .universal is not allowed.
55 if cls == .universal {
56 return error('wrap with universal class is unallowed')
57 }
58 inner_form := el.tag().constructed
59 constructed := if mode == .explicit { true } else { inner_form }
60 content := if mode == .explicit { encode_with_rule(el, .der)! } else { el.payload()! }
61
62 outer_tag := Tag.new(cls, constructed, tagnum)!
63 raw := RawElement{
64 tag: outer_tag
65 content: content
66 inner_tag: el.tag()
67 mode: mode
68 }
69
70 return raw
71}
72
73// tag returns the tag of the RawElement, ie, outer tag when its a wrapper.
74pub fn (r RawElement) tag() Tag {
75 return r.tag
76}
77
78// payload returns the payload of the RawElement.
79pub fn (r RawElement) payload() ![]u8 {
80 return r.content
81}
82
83// set_mode sets the RawElement tagged mode, in explicit or implicit mode. If the mode has been set,
84// it would drop into error until you forces it by setting force flag into true value, ie, replaces the old one.
85pub fn (mut r RawElement) set_mode(mode TaggedMode, force bool) ! {
86 if r.tag.class == .universal {
87 return error('No need it on universal class')
88 }
89 if r.mode != none {
90 if !force {
91 return error('unallowed_operation r.mode != none')
92 }
93 }
94 r.mode = mode
95}
96
97// set_inner_tag sets the inner tag of the RawElement into inner_tag value. If it has been already set,
98// it would be an error until you setting force flag into true value to replace the old one.
99pub fn (mut r RawElement) set_inner_tag(inner_tag Tag, force bool) ! {
100 // not needed in universal class
101 if r.tag.class == .universal {
102 return error('No need it on universal class')
103 }
104 mode := r.mode or { return error('You dont set any mode') }
105 // when its in explicit mode, compares the provided tag with tag from the inner element.
106 if mode == .explicit {
107 if !r.tag.constructed {
108 return error('unmeet_requirement, explicit should be constructed')
109 }
110 // check inner_tag
111 itt, _ := Tag.decode(r.content)!
112 if !itt.equal(inner_tag) {
113 return error('unmeet_requirement, unequal supplied tag')
114 }
115 }
116 if r.inner_tag != none {
117 if !force {
118 return error('.unallowed_operation, r.inner_tag != none')
119 }
120 }
121 r.inner_tag = inner_tag
122}
123
124// force_set_default_value forces set default value of this RawElement into value.
125fn (mut r RawElement) set_default_value(value Element, force bool) ! {
126 // default value of this element should have an equal tag.
127 if !value.tag().equal(r.tag) {
128 return error('You provides unequal tag for default value')
129 }
130 if r.default_value != none {
131 if !force {
132 return error('The RawElement already default_value being set')
133 }
134 }
135 r.default_value = value
136}
137
138// inner_tag returns the inner tag of the RawElement if it exists, or error on fails.
139pub fn (r RawElement) inner_tag() ?Tag {
140 return r.inner_tag
141}
142
143// inner_element returns the inner element of the RawElement if its exists.
144pub fn (r RawElement) inner_element() !Element {
145 if r.tag.class == .universal {
146 return error('inner element from universal class is not availables')
147 }
148 inner_tag := r.inner_tag or { return err }
149 mode := r.mode or { return error('You dont set any mode') }
150 if mode == .explicit {
151 if !r.tag.constructed {
152 return error('tag should be constructed when in explicit')
153 }
154 }
155 // in implicit, r.content is inner element content with inner tag
156 if mode == .implicit {
157 elem := parse_element(inner_tag, r.content)!
158 return elem
159 }
160 // otherwise, treats it in explicit mode.
161 // read an inner tag from r.content
162 mut p := Parser.new(r.content)
163 tag := p.peek_tag()!
164 if !tag.equal(inner_tag) {
165 return error('gets unequal inner_tag')
166 }
167 el := p.read_tlv()!
168 // should finish
169 p.finish()!
170 return el
171}
172
173fn (r RawElement) check_inner_tag() ! {
174 if r.tag.class == .universal {
175 return error('Universal class does not have inner tag')
176 }
177 mode := r.mode or { return error('You dont set any mode') }
178 if mode != .explicit {
179 return
180 }
181 // read an inner tag from content
182 tag, _ := Tag.decode_with_rule(r.content, 0, .der)!
183 inner_tag := r.inner_tag or { return error('You dont set an inner_tag') }
184 if !tag.equal(inner_tag) {
185 return error('Get unexpected inner tag from bytes')
186 }
187}
188
189// ContextSpecific tagged type element.
190pub struct ContextElement {
191 RawElement
192}
193
194// new creates a raw context specific element. Use `ContextElement.from_element` instead
195// if your context specific element is wrapper of another element.
196pub fn ContextElement.new(tag Tag, content []u8) !ContextElement {
197 if tag.class != .context_specific {
198 return error('Your tag is not .context_specific')
199 }
200 raw := RawElement.new(tag, content)!
201 return ContextElement{raw}
202}
203
204// from_element creates a new tagged type of ContextElement from some element in inner.
205pub fn ContextElement.from_element(inner Element, tagnum int, mode TaggedMode) !ContextElement {
206 raw := RawElement.from_element(inner, .context_specific, tagnum, mode)!
207
208 ctx := ContextElement{raw}
209 return ctx
210}
211
212// tag returns the tag of context specific element.
213pub fn (ctx ContextElement) tag() Tag {
214 return ctx.RawElement.tag
215}
216
217// payload returns the payload of the context specific element.
218pub fn (ctx ContextElement) payload() ![]u8 {
219 return ctx.RawElement.content
220}
221
222// explicit_context creates a new ContextElement with explicit mode.
223pub fn ContextElement.explicit_context(inner Element, tagnum int) !ContextElement {
224 return ContextElement.from_element(inner, tagnum, .explicit)!
225}
226
227// implicit_context creates new ContextElement with implicit mode.
228pub fn ContextElement.implicit_context(inner Element, tagnum int) !ContextElement {
229 return ContextElement.from_element(inner, tagnum, .implicit)!
230}
231
232fn ContextElement.decode_raw(bytes []u8) !(ContextElement, int) {
233 tag, length_pos := Tag.decode_with_rule(bytes, 0, .der)!
234 if tag.class != .context_specific {
235 return error('tag required to be context_specific')
236 }
237 length, content_pos := Length.decode_with_rule(bytes, length_pos, .der)!
238 content := if length == 0 {
239 []u8{}
240 } else {
241 if content_pos >= bytes.len || content_pos + length > bytes.len {
242 return error('ContextElement: truncated payload bytes')
243 }
244 unsafe { bytes[content_pos..content_pos + length] }
245 }
246 next := content_pos + length
247 // Raw ContextElement, you should provide mode and inner tag.
248 ctx := ContextElement{
249 tag: tag
250 content: content
251 }
252 return ctx, next
253}
254
255fn ContextElement.decode_with_options(bytes []u8, opt string) !(ContextElement, int) {
256 if opt.len == 0 {
257 return ContextElement.decode_raw(bytes)!
258 }
259 fo := FieldOptions.from_string(opt)!
260 // get mode and inner tag
261 if !valid_mode_value(fo.mode) {
262 return error('Get unexpected mode option for ContextElement')
263 }
264 mode := TaggedMode.from_string(fo.mode)!
265 inner_tag := fo.inner_tag()!
266
267 // outer tag from bytes
268 tag, length_pos := Tag.decode_with_rule(bytes, 0, .der)!
269 if tag.class != .context_specific {
270 return error('Get non ContextSpecific tag')
271 }
272
273 // if mode is explicit without constructed form, its would return on error.
274 if mode == .explicit {
275 if !tag.constructed {
276 return error('explicit need constructed form')
277 }
278 }
279 length, content_pos := Length.decode_with_rule(bytes, length_pos, .der)!
280 content := if length == 0 {
281 []u8{}
282 } else {
283 if content_pos >= bytes.len || content_pos + length > bytes.len {
284 return error('ContextElement: truncated payload bytes')
285 }
286 unsafe { bytes[content_pos..content_pos + length] }
287 }
288 next := content_pos + length
289
290 if mode == .implicit {
291 ctx := ContextElement{
292 tag: tag
293 content: content
294 inner_tag: inner_tag
295 mode: .implicit
296 }
297 return ctx, next
298 }
299 // explicit one, build ContextElement and performs checks for inner_tag validity.
300 ctx := ContextElement{
301 tag: tag
302 content: content
303 inner_tag: inner_tag
304 mode: .explicit
305 }
306 ctx.check_inner_tag()!
307
308 return ctx, next
309}
310
311// Limited support for APPLICATION CLASS Element.
312pub struct ApplicationElement {
313 RawElement
314}
315
316// new creates a new application class element.
317pub fn ApplicationElement.new(tag Tag, content []u8) !ApplicationElement {
318 if tag.class != .application {
319 return error('Your tag is not .application')
320 }
321 raw := RawElement.new(tag, content)!
322 return ApplicationElement{raw}
323}
324
325// from_element creates application class element from some element.
326pub fn ApplicationElement.from_element(inner Element, tagnum int, mode TaggedMode) !ApplicationElement {
327 raw := RawElement.from_element(inner, .application, tagnum, mode)!
328 app := ApplicationElement{raw}
329 return app
330}
331
332// tag returns the tag of the application class element.
333pub fn (app ApplicationElement) tag() Tag {
334 return app.RawElement.tag
335}
336
337// payload returns the payload of application class element.
338pub fn (app ApplicationElement) payload() ![]u8 {
339 return app.RawElement.content
340}
341
342// Limited support for PRIVATE CLASS Element.
343pub struct PrivateELement {
344 RawElement
345}
346
347// new creates a raw private class element.
348pub fn PrivateELement.new(tag Tag, content []u8) !PrivateELement {
349 if tag.class != .private {
350 return error('Your tag is not private')
351 }
352 raw := RawElement.new(tag, content)!
353
354 return PrivateELement{raw}
355}
356
357// from_element creates a new private class element from another element.
358pub fn PrivateELement.from_element(inner Element, tagnum int, mode TaggedMode) !PrivateELement {
359 raw := RawElement.from_element(inner, .private, tagnum, mode)!
360 app := PrivateELement{raw}
361
362 return app
363}
364
365// tag returns the tag of the private element.
366pub fn (prv PrivateELement) tag() Tag {
367 return prv.RawElement.tag
368}
369
370// payload returns the payload of the private element.
371pub fn (prv PrivateELement) payload() ![]u8 {
372 return prv.RawElement.content
373}
374