v2 / vlib / x / encoding / asn1 / element.v
300 lines · 271 sloc · 9.21 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// for bytes compare
7import crypto.internal.subtle { constant_time_compare }
8
9// This file contains structures and routines for handling ASN.1 Element.
10// Its includes:
11// - basic Element interface, for support ASN.1 element in more generic way
12// - arrays of Element in the form of ElementList
13// - basic raw element in the RawElement structure, for handling arbitrary class
14// and other undefined (unsupported) generic ASN.1 Element in this module.
15// - others structures, likes an Choice, AnyDefinedBy, Optional for representing other
16// element
17
18// Element represents a generic ASN.1 Element.
19// Most of the standard Universal class element defined on this module
20// satisfies this interface. This interface was also expanded by methods
21// defined on this interface.
22pub interface Element {
23 // tag tells the ASN.1 identity of this Element.
24 tag() Tag
25 // payload tells the payload (values) of this Element.
26 // The element's size was calculated implicitly from payload.len
27 // Its depends on the tag how interpretes this payload.
28 payload() ![]u8
29}
30
31// from_object[T] transforms and creates a new Element from generic type (maybe universal type, like an OctetString).
32// Its accepts generic element t that you should pass to this function. You should make sure if this element implements
33// required methods of the Element, or an error would be returned.
34// FIXME: its not tested.
35// Examples:
36// ```v
37// oc := asn1.OctetString.new("xxx")!
38// el := asn1.Element.from_object[OctetString](oc)!
39// ```
40// and then treats your OctetString as an Element
41pub fn Element.from_object[T](t T) !Element {
42 $if T !is Element {
43 return error('Not holding element')
44 }
45 return t
46}
47
48// into_object[T] transforms and tries to cast element el into generic object T
49// if the element not holding object T, it would return error.
50// NOTE: Not tested.
51// Examples:
52// ```v
53// oc := asn1.OctetString.new("xxx")!
54// el := asn1.Element.from_object[OctetString](oc)!
55// ```
56// cast back the element into OctetString.
57// ```v
58// os := el.into_object[OctetString]()!
59// ```
60// and then treats os as an OctetString.
61pub fn (el Element) into_object[T]() !T {
62 if el is T {
63 return *el
64 }
65 return error('Element el does not holding T}')
66}
67
68// length tells the payload length of this element.
69pub fn (el Element) length() !int {
70 return el.payload()!.len
71}
72
73// UTILITY HELPER FOR ELEMENT
74//
75
76// Helper for validates FieldOptions.
77//
78// validate_options validates FieldOptions is a valid option agains current element.
79fn (el Element) validate_options(fo FieldOptions) ! {
80 el.validate_wrapper(fo)!
81 el.validate_optional(fo)!
82 el.validate_default(fo)!
83}
84
85// validate_wrapper validates wrapper's part of fields options.
86fn (el Element) validate_wrapper(fo FieldOptions) ! {
87 // Validates wrapper part
88 // Its discard all check when fo.cls is empty string, its marked as non-wrapped element.
89 if fo.cls != '' {
90 fo.check_wrapper()!
91 }
92}
93
94// validate_default validates has_default part of field options
95fn (el Element) validate_default(fo FieldOptions) ! {
96 // Validates default part
97 if fo.has_default {
98 if fo.default_value == none {
99 return error('has_default withoud default value')
100 }
101 def := fo.default_value or { return err }
102 if !el.tag().equal(def.tag()) {
103 return error('You provides different tag of default_value with tag of current element')
104 }
105 }
106}
107
108fn (el Element) validate_optional(fo FieldOptions) ! {
109 // Validates Optional part
110 // If the element is already optional, you cant make it optional again by setting optional=true
111 if fo.optional {
112 if el is Optional {
113 return error('You cant mark Optional element as nested Optional')
114 }
115 }
116}
117
118// KeyDefault is map of string (field.name) into Element for element with default semantic.
119// its is to be used for building payload of complex structures like sequence.
120// see `make_payload` below.
121pub type KeyDefault = map[string]Element
122
123// new_key_default creates empty KeyDefault maps.
124pub fn new_key_default() KeyDefault {
125 return KeyDefault(map[string]Element{})
126}
127
128// `make_payload` builds bytes of payload for some structures contains field of Elements.
129// Consider this examples from RFC 5280 defines schema.
130// ```v
131// Certificate ::= SEQUENCE {
132// tbsCertificate TBSCertificate,
133// signatureAlgorithm AlgorithmIdentifier,
134// signatureValue BIT STRING }
135// ```
136// where your structure defined as:.
137// ```v
138// struct Certificate {
139// tbs_certificate TBSCertificate
140// signature_algorithm AlgorithmIdentifier
141// signature_value BitString
142// }
143// ```
144//
145// Usually you can do.
146//
147// ```v
148// cert := Certificate.new()!
149// payload := asn1.make_payload[Certificate](cert)!
150// ```
151//
152// and then you can use the produced payload.
153//
154// BUG: there are some issues would you encounter when your T contains
155// fields that have generic in it, its would produce unexpected result,
156// see detail bug on: https://github.com/vlang/v/issues/22721.
157// So, just use this when your T.fields is not contains generic within it.
158// UPDATED: This issue has been fixed in this PR [#22724](https://github.com/vlang/v/pull/22724)
159// Thanks to @felipensp
160pub fn make_payload[T](val T, kd KeyDefault) ![]u8 {
161 mut out := []u8{}
162 $for field in val.fields {
163 // only serialiaze field that implement interfaces
164 // Issue: `$if field.typ is Element` check would false when field.type contains generic structure.
165 // even the `field.type` is fullfills the interfaces.
166 // see detail bug on: https://github.com/vlang/v/issues/22721
167 $if field.typ is Element {
168 // if there attributes option
169 if field.attrs.len != 0 {
170 mut fo := FieldOptions.from_attrs(field.attrs)!
171 // TODO: add keyDefault support
172 if fo.has_default {
173 // install default by getting default element from map
174 key := field.name
175 def_elem := kd[key] or { return error('missing defaul element') }
176 fo.install_default(def_elem, false)!
177 }
178 current := encode_with_field_options(val.$(field.name), fo)!
179 out << current
180 } else {
181 // without option
182 current := encode(val.$(field.name))!
183 out << current
184 }
185 }
186 }
187 return out
188}
189
190// `encoded_len` calculates the size in bytes when the el element was serialized.
191pub fn encoded_len(el Element) int {
192 return el.encoded_len()
193}
194
195// `encoded_len` calculates the length of bytes when this element was serialized.
196pub fn (el Element) encoded_len() int {
197 return el.encoded_len_with_rule(.der)
198}
199
200// encoded_len_with_rule informs us the length of bytes when this element serialized into bytes.
201// Different rule maybe produces different result.
202fn (el Element) encoded_len_with_rule(rule EncodingRule) int {
203 mut n := 0
204 n += el.tag().tag_size()
205 payload := el.payload() or { panic(err) }
206 length := Length.new(payload.len) or { panic(err) }
207 n += length.length_size_with_rule(rule) or { panic(err) }
208 n += payload.len
209
210 return n
211}
212
213// equal checks whether this two element equal and holds the same tag and content
214pub fn (el Element) equal(other Element) bool {
215 return (el.tag().equal(other.tag())) && (el.equal_payload(other))
216}
217
218fn (el Element) equal_payload(other Element) bool {
219 // taken from crypto.internal.subtle
220 x := el.payload() or { panic(err) }
221 y := other.payload() or { panic(err) }
222
223 return constant_time_compare(x, y) == 1
224}
225
226fn Element.decode(src []u8) !(Element, int) {
227 return Element.decode_with_rule(src, 0, .der)!
228}
229
230// decode deserializes back bytes in src from offet `loc` into Element.
231// Basically, its tries to parse a Universal class Element when it is possible.
232fn Element.decode_with_rule(src []u8, loc int, rule EncodingRule) !(Element, int) {
233 tag, length_pos := Tag.decode_with_rule(src, loc, rule)!
234 length, content_pos := Length.decode_with_rule(src, length_pos, rule)!
235 // get the bytes
236 bytes := if length == 0 {
237 []u8{}
238 } else {
239 if content_pos >= src.len || content_pos + length > src.len {
240 return error('Need more bytes to read content')
241 }
242 unsafe { src[content_pos..content_pos + length] }
243 }
244 next_pos := content_pos + length
245 elem := parse_element(tag, bytes)!
246
247 return elem, next_pos
248}
249
250// ElementList.
251//
252// ElementList is arrays of Element.
253// Many places maybe required this wells, likes Sequence or Set fields.
254pub type ElementList = []Element
255
256// payload produces bytes array from arays of Element.
257pub fn (els ElementList) payload() ![]u8 {
258 return els.payload_with_rule(.der)!
259}
260
261fn (els ElementList) payload_with_rule(rule EncodingRule) ![]u8 {
262 mut out := []u8{}
263 for el in els {
264 bytes := encode_with_rule(el, rule)!
265 out << bytes
266 }
267 return out
268}
269
270// encoded_len tells the length of bytes when the ElementList was serialized.
271pub fn (els ElementList) encoded_len() int {
272 mut n := 0
273 for el in els {
274 n += el.encoded_len()
275 }
276 return n
277}
278
279// ElementList.from_bytes parses bytes in src as series of Element or return error on fails.
280// Its does not support trailing data.
281pub fn ElementList.from_bytes(src []u8) ![]Element {
282 mut els := []Element{}
283 if src.len == 0 {
284 // empty list
285 return els
286 }
287 mut i := int(0)
288 for i < src.len {
289 el, pos := Element.decode_with_rule(src, i, .der)!
290 i = pos
291 els << el
292 }
293 if i > src.len {
294 return error('i > src.len')
295 }
296 if i < src.len {
297 return error('The src contains unprocessed bytes')
298 }
299 return els
300}
301