mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
193 lines
6.3 KiB
V
193 lines
6.3 KiB
V
// Copyright (c) 2022, 2024 blackshirt. All rights reserved.
|
|
// Use of this source code is governed by a MIT License
|
|
// that can be found in the LICENSE file.
|
|
module asn1
|
|
|
|
// Handling functionality of Element's serialization.
|
|
//
|
|
|
|
// `encode` serializes element into bytes array. By default, its encode in .der rule with empty options.
|
|
// See `encode_with_options` if you want pass an option string. See `field.v` for more option in detail.
|
|
//
|
|
// Examples:
|
|
//
|
|
// ```v
|
|
// import x.encoding.asn1
|
|
//
|
|
// obj := asn1.Utf8String.new('hi')!
|
|
// out := asn1.encode(obj)!
|
|
// assert out == [u8(0x0C), 0x02, 0x68, 0x69]
|
|
// ```
|
|
pub fn encode(el Element) ![]u8 {
|
|
// without options, we call `.encode_with_rule` directly on element.
|
|
return encode_with_rule(el, .der)!
|
|
}
|
|
|
|
// `encode_with_options` serializes element into bytes array with options string passed to drive the result.
|
|
//
|
|
// Examples:
|
|
//
|
|
// `Utf8String` defined as `[5] IMPLICIT UTF8String` was encoded into `85 02 68 69`.
|
|
// `Utf8String` defined as `[5] EXPLICIT UTF8String` was encoded into `A5 04 0C 02 68 69`.
|
|
//
|
|
// ```v
|
|
// obj := asn1.Utf8String.new('hi')!
|
|
// implicit_out := asn1.encode_with_options(obj, 'context_specific:5;implicit;inner:12')!
|
|
// assert implicit_out == [u8(0x85), 0x02, 0x68, 0x69]
|
|
//
|
|
// explicit_out := asn1.encode_with_options(obj, 'context_specific:5;explicit;inner:0x0c')!
|
|
// assert explicit_out == [u8(0xA5), 0x04, 0x0C, 0x02, 0x68, 0x69]
|
|
// ```
|
|
pub fn encode_with_options(el Element, opt string) ![]u8 {
|
|
// treated as without option when empty
|
|
if opt.len == 0 {
|
|
return encode_with_rule(el, .der)!
|
|
}
|
|
fo := FieldOptions.from_string(opt)!
|
|
return encode_with_field_options(el, fo)!
|
|
}
|
|
|
|
// `encode_with_field_options` serializes this element into bytes array with options defined in fo.
|
|
pub fn encode_with_field_options(el Element, fo FieldOptions) ![]u8 {
|
|
// validates options again this element.
|
|
el.validate_options(fo)!
|
|
|
|
// check for default_value for this element
|
|
// if we have it matching with current element,
|
|
// by default, in .der mode, it should not be serialized.
|
|
if fo.has_default {
|
|
def_element := fo.default_value or { return error('bad default_value') }
|
|
// If this element is equal with default_value, by default its should not be serialized.
|
|
if el.equal(def_element) {
|
|
return []u8{}
|
|
}
|
|
}
|
|
|
|
// apply field options, turns this element
|
|
// into optional, wrapped element or original one.
|
|
new_el := el.apply_field_options(fo)!
|
|
|
|
// if new_el is Optional, encode with optional behaviour
|
|
if new_el is Optional {
|
|
return new_el.encode()!
|
|
}
|
|
// otherwise, just serializing it
|
|
return encode_with_rule(new_el, .der)!
|
|
}
|
|
|
|
// Helper for wrapping element
|
|
//
|
|
//
|
|
// into_optional turns this element into Optional with present bit.
|
|
// When you set with_present into true, its makes this optional was present.
|
|
fn (el Element) into_optional(with_present bool) !Element {
|
|
if el is Optional {
|
|
return error('already optional element')
|
|
}
|
|
return Optional.new(el, with_present)!
|
|
}
|
|
|
|
// apply_field_options applies rules in field options into current element
|
|
// and turns this into another element.
|
|
// by default, optional attribute is more higher precedence over wrapper attribut, ie,
|
|
// take the wrap step and then turn into optional (if true)
|
|
fn (el Element) apply_field_options(fo FieldOptions) !Element {
|
|
el.validate_options(fo)!
|
|
// if there a wrapper
|
|
if fo.cls != '' {
|
|
wrapped_el := el.wrap_with_options(fo)!
|
|
if fo.optional {
|
|
return wrapped_el.into_optional(fo.present)!
|
|
}
|
|
// not-optional, just return wrapped element
|
|
return wrapped_el
|
|
}
|
|
// no-wrapper, check for optional
|
|
if fo.optional {
|
|
return el.into_optional(fo.present)!
|
|
}
|
|
// otherwise, its no-wrapper and non-optional
|
|
return el
|
|
}
|
|
|
|
// set_default_value installs default value within FieldOptions for the element
|
|
pub fn (el Element) set_default_value(mut fo FieldOptions, value Element) ! {
|
|
// the default tag should match with the current tag
|
|
if !el.tag().equal(value.tag()) {
|
|
return error('unmatching tag of default value')
|
|
}
|
|
fo.install_default(value, false)!
|
|
el.validate_default(fo)!
|
|
}
|
|
|
|
// wrap_with_rule wraps universal element into another class.
|
|
// we prohibit dan defines some rules when its happen and returns an error instead
|
|
// 1. wrapping into .universal class is not allowed
|
|
// 2. wrapping with the same class is not allowed too
|
|
// 3. wrapping non-universal class element is not allowed (maybe removed on futures.)
|
|
// Notes :
|
|
// Three additional information about tagging:
|
|
// CHOICEs are always explicitly tagged even if implicit tagging is in effect.
|
|
// EXPLICIT TAGs are always constructed, they encapsulate the TLV they prefix.
|
|
// An IMPLICIT TAG 'inherits' the constructed bit of the TLV whose 'T' is overwritten,
|
|
// examples:
|
|
// a) '[5] IMPLICIT INTEGER' has tag 0x85 (overwriting 0x02 = INTEGER)
|
|
// b) '[5] IMPLICIT SEQUENCE' has tag 0xA5 (overwriting 0x30 = SEQUENCE, CONSTRUCTED)
|
|
fn (el Element) wrap_with_options(fo FieldOptions) !Element {
|
|
// validates options.
|
|
el.validate_options(fo)!
|
|
|
|
mode := TaggedMode.from_string(fo.mode)!
|
|
cls := TagClass.from_string(fo.cls)!
|
|
|
|
return wrap(el, cls, fo.tagnum, mode)!
|
|
}
|
|
|
|
// wrao performs wrapping to element and turns this element into another one.
|
|
fn wrap(el Element, cls TagClass, number int, mode TaggedMode) !Element {
|
|
if el is Optional {
|
|
return error('Optional cant be wrapped')
|
|
}
|
|
if cls == .universal {
|
|
return error('you cant wrap into universal')
|
|
}
|
|
match cls {
|
|
.context_specific {
|
|
return ContextElement.from_element(el, number, mode)!
|
|
}
|
|
.application {
|
|
return ApplicationElement.from_element(el, number, mode)!
|
|
}
|
|
.private {
|
|
return PrivateELement.from_element(el, number, mode)!
|
|
}
|
|
else {
|
|
return error('Wraps to the wrong class')
|
|
}
|
|
}
|
|
}
|
|
|
|
// encode_with_rule encodes element into bytes array with rule.
|
|
fn encode_with_rule(el Element, rule EncodingRule) ![]u8 {
|
|
if rule != .der && rule != .ber {
|
|
return error('Element: unsupported rule')
|
|
}
|
|
mut dst := []u8{}
|
|
|
|
// when this element is Optional without presence flag, by default would
|
|
// serialize this element into empty bytes otherwise, would serialize underlying element.
|
|
if el is Optional {
|
|
return el.encode()!
|
|
}
|
|
// otherwise, just serializes as normal
|
|
el.tag().encode_with_rule(mut dst, rule)!
|
|
// calculates the length of element, and serialize this length
|
|
payload := el.payload()!
|
|
length := Length.new(payload.len)!
|
|
length.encode_with_rule(mut dst, rule)!
|
|
// append the element payload to destination
|
|
dst << payload
|
|
|
|
return dst
|
|
}
|