From 897ec51480ee51714f03534117f603eb28dae7fa Mon Sep 17 00:00:00 2001 From: blackshirt Date: Thu, 14 Nov 2024 00:38:27 +0700 Subject: [PATCH] x.encoding.asn1: cleanup code and comments (#22847) --- vlib/x/encoding/asn1/README.md | 4 +- vlib/x/encoding/asn1/any.v | 22 ++++------ vlib/x/encoding/asn1/bitstring.v | 42 +++++++++++-------- vlib/x/encoding/asn1/boolean.v | 1 - vlib/x/encoding/asn1/choices.v | 1 - vlib/x/encoding/asn1/core.v | 1 - vlib/x/encoding/asn1/enumerated.v | 5 +-- vlib/x/encoding/asn1/field_options.v | 17 ++++---- vlib/x/encoding/asn1/generalstring.v | 11 +++-- vlib/x/encoding/asn1/ia5string.v | 1 - vlib/x/encoding/asn1/integer.v | 6 +-- vlib/x/encoding/asn1/numericstring.v | 1 - vlib/x/encoding/asn1/octetstring.v | 4 +- vlib/x/encoding/asn1/oid.v | 1 - vlib/x/encoding/asn1/optional.v | 1 - vlib/x/encoding/asn1/other_element.v | 4 -- vlib/x/encoding/asn1/printablestring.v | 1 - vlib/x/encoding/asn1/sequence.v | 7 +++- vlib/x/encoding/asn1/sequence_test.v | 6 +-- vlib/x/encoding/asn1/set.v | 7 +++- vlib/x/encoding/asn1/time.v | 58 +++++++++++++++++++++++++- vlib/x/encoding/asn1/time_test.v | 42 +++++++++++++++++++ vlib/x/encoding/asn1/utf8string.v | 1 - vlib/x/encoding/asn1/visiblestring.v | 1 - 24 files changed, 169 insertions(+), 76 deletions(-) diff --git a/vlib/x/encoding/asn1/README.md b/vlib/x/encoding/asn1/README.md index d38090c87..37d933389 100644 --- a/vlib/x/encoding/asn1/README.md +++ b/vlib/x/encoding/asn1/README.md @@ -69,11 +69,11 @@ fn main() { assert int_el_value == 32 } ``` -See more complete examples in the [examples](examples/) directory. +See more complete examples in the [examples](https://github.com/vlang/v/tree/master/vlib/x/encoding/asn1/examples/) directory. ## Documentation -See the [documentation](DOCS.md) for more detailed information on +See the [documentation](https://github.com/vlang/v/blob/master/vlib/x/encoding/asn1/DOCS.md) for more detailed information on how to use functionality in this module. ## License diff --git a/vlib/x/encoding/asn1/any.v b/vlib/x/encoding/asn1/any.v index ce3e56bfa..0e771d6cb 100644 --- a/vlib/x/encoding/asn1/any.v +++ b/vlib/x/encoding/asn1/any.v @@ -5,29 +5,23 @@ module asn1 // ANY DEFINED BY // -@[noinit] -pub struct Any { -mut: - marker string = 'any' +pub struct AnyDefinedBy { pub: - params Element + // default to null element + params Element = Null{} } -// Any.new creates a new ANY DEFINED BY element with marker as an identifier. -pub fn Any.new(marker string, params Element) Any { - return Any{marker, params} +// AnyDefinedBy.new creates a new ANY DEFINED BY element. +pub fn AnyDefinedBy.new(params Element) AnyDefinedBy { + return AnyDefinedBy{params} } // tag returns the underlying tag of ANY DEFINED BY element. -pub fn (a Any) tag() Tag { +pub fn (a AnyDefinedBy) tag() Tag { return a.params.tag() } // payload returns the underlying payload of ANY DEFINED BY element. -pub fn (a Any) payload() ![]u8 { +pub fn (a AnyDefinedBy) payload() ![]u8 { return a.params.payload()! } - -fn (a Any) params() Element { - return a.params -} diff --git a/vlib/x/encoding/asn1/bitstring.v b/vlib/x/encoding/asn1/bitstring.v index 6ea7333fa..6c371b95d 100644 --- a/vlib/x/encoding/asn1/bitstring.v +++ b/vlib/x/encoding/asn1/bitstring.v @@ -5,6 +5,9 @@ module asn1 import arrays +// default_bitstring_tag is the default tag of the ASN.1 BITSTRING type. +pub const default_bitstring_tag = Tag{.universal, false, int(TagType.bitstring)} + // ASN.1 UNIVERSAL CLASS OF BITSTRING TYPE. // // The BIT STRING type denotes an arbitrary string of bits (ones and zeroes). @@ -14,16 +17,27 @@ import arrays // However, in DER all types that have an encoding choice between primitive and constructed // must use the primitive encoding. DER restricts the encoding to primitive only. // The same applies for BITSTRING. ie, For BIT STRING and OCTET STRING types, -// DER does not allow the constructed form (breaking a string into multiple TLVs) or the indefinite length form. -@[noinit] +// DER does not allow the constructed form (breaking a string into multiple TLVs) +// or the indefinite length form. pub struct BitString { mut: data []u8 pad u8 // numbers of unused bits } -// default_bitstring_tag is the default tag of the ASN.1 BITSTRING type. -pub const default_bitstring_tag = Tag{.universal, false, int(TagType.bitstring)} +// check performs check internal validity of the BitString data. +fn (bs BitString) check() ! { + // to align with octet size, ie, 8 in length, pad bits only need maximum 7 bits + // and when the data.len is multiples of 8, no need to pad, ie, pad should 0. + if bs.pad > 7 || (bs.data.len == 0 && bs.pad != 0) { + return error('BitString: bad pad bits or zero length') + } + // this check if the pad != 0, whether the last `pad` number of bits of the last byte + // is all bits cleared, and it was not used in the BitString data. + if bs.pad > 0 && (bs.data[bs.data.len - 1]) & ((1 << bs.pad) - 1) != 0 { + return error('BitString: bad args') + } +} // tag returns the tag of BITSTRING type. pub fn (bs BitString) tag() Tag { @@ -32,6 +46,7 @@ pub fn (bs BitString) tag() Tag { // payload returns the payload of BITSTRING instance. pub fn (bs BitString) payload() ![]u8 { + bs.check()! mut out := []u8{} out << bs.pad out << bs.data @@ -106,6 +121,7 @@ pub fn BitString.new(s string) !BitString { } // from_bytes creates a new BitString from bytes array in src. +// Note: Your first byte of the src as a pad bit. fn BitString.from_bytes(src []u8) !BitString { if src.len < 1 { return error('BitString error: need more bytes') @@ -113,23 +129,15 @@ fn BitString.from_bytes(src []u8) !BitString { return BitString.new_with_pad(src[1..], src[0])! } -// new_with_pad creates a new BitString from bytes array in bytes with specific -// padding bits in pad +// new_with_pad creates a new BitString from bytes array in bytes with +// specific padding bits in pad fn BitString.new_with_pad(bytes []u8, pad u8) !BitString { - // to align with octet size, ie, 8 in length, pad bits only need maximum 7 bits - // and when the bytes.len is multiples of 8, no need to pad, ie, pad should 0. - if pad > 7 || (bytes.len == 0 && pad != 0) { - return error('BitString: bad pad bits or zero length') - } - // this check if the pad != 0, whether the last `pad` number of bits of the last byte - // is all bits cleared, and it was not used in the BitString data. - if pad > 0 && (bytes[bytes.len - 1]) & ((1 << pad) - 1) != 0 { - return error('BitString: bad args') - } - return BitString{ + bs := BitString{ data: bytes pad: pad } + bs.check()! + return bs } fn (bs BitString) bytes_len() int { diff --git a/vlib/x/encoding/asn1/boolean.v b/vlib/x/encoding/asn1/boolean.v index 922b1572b..3852908eb 100644 --- a/vlib/x/encoding/asn1/boolean.v +++ b/vlib/x/encoding/asn1/boolean.v @@ -12,7 +12,6 @@ pub const default_boolean_tag = Tag{.universal, false, int(TagType.boolean)} // ASN.1 DER encoding restricts encoding of boolean true value into 0xff // and otherwise, encodes into zero (0x00) for false value. // The encoding of a boolean value shall be primitive. The contents octets shall consist of a single octet. -@[noinit] pub struct Boolean { mut: // boolean value represented in single byte to allow stores multiple value represents diff --git a/vlib/x/encoding/asn1/choices.v b/vlib/x/encoding/asn1/choices.v index 8842d5b6e..912feae7d 100644 --- a/vlib/x/encoding/asn1/choices.v +++ b/vlib/x/encoding/asn1/choices.v @@ -9,7 +9,6 @@ const default_choices_size = 64 // ASN.1 CHOICE // // Choice is an ASN.1 Element -@[noinit] pub struct Choice { mut: size int = default_choices_size diff --git a/vlib/x/encoding/asn1/core.v b/vlib/x/encoding/asn1/core.v index 277788994..312e5baa0 100644 --- a/vlib/x/encoding/asn1/core.v +++ b/vlib/x/encoding/asn1/core.v @@ -33,7 +33,6 @@ const max_universal_tagnumber = 255 // Its big enough to accomodate and represent different of yours own tag number. // Its represents 2 bytes length where maximum bytes arrays to represent tag number // in multibyte (long) form is `[u8(0x1f), 0xff, 0x7f]` or 16383 in base 128. -@[noinit] pub struct Tag { mut: class TagClass = .universal diff --git a/vlib/x/encoding/asn1/enumerated.v b/vlib/x/encoding/asn1/enumerated.v index 44ba53433..63f748cb7 100644 --- a/vlib/x/encoding/asn1/enumerated.v +++ b/vlib/x/encoding/asn1/enumerated.v @@ -9,9 +9,8 @@ const default_enumerated_tag = Tag{.universal, false, int(TagType.enumerated)} // ASN.1 ENUMERATED TYPE. // // Enumerated type treated as ordinary integer, only differs on tag value. -// The encoding of an enumerated value shall be that of the integer value with which it is associated. -// NOTE: It is primitive. -@[noinit] +// The encoding of an enumerated value shall be that of the integer value +// with which it is associated. In DER rule, Enumerated type should be primitive type. pub struct Enumerated { pub: value int diff --git a/vlib/x/encoding/asn1/field_options.v b/vlib/x/encoding/asn1/field_options.v index 896ea2975..7df55becc 100644 --- a/vlib/x/encoding/asn1/field_options.v +++ b/vlib/x/encoding/asn1/field_options.v @@ -13,8 +13,8 @@ const max_attributes_length = 5 // // - `class:number`, for wrapping the element with other non-universal class, for examole: `private:100`. // - `explicit` or `implicit` mode. -// - `inner:5` the tag number of element being wrapped, should in UNIVERSAL class. -// - `optional` tagging for element with OPTIONAL behaviour. +// - `inner:5` for universal class form or `inner:class,constructed,number` for extended form. +// - `optional` or `optional:present' tagging for element with OPTIONAL behaviour. // - `has_default` tagging for element with DEFAULT behaviour. // // Field options attributes handling. @@ -24,20 +24,17 @@ const max_attributes_length = 5 // For example, you can tagging your fields of some element with tagging // like `@[context_specific:10; explicit; inner:5; optional]`. // Its will be parsed and can be used to drive encoding or decoding of Element. -@[heap; noinit] pub struct FieldOptions { mut: // The fields `cls`, `tagnum`, `mode` and `inner` was used // for wrapping (and unwrapping) purposes. turn some element // into another element configured with this options. - // This fields currently strictly applied to UNIVERSAL element. // In the encoding (decoding) phase, it would be checked // if this options meet required criteria. // Limitation applied on the wrapper fields: // 1. Wrap into UNIVERSAL is not allowed (cls != universal) - // 2. Wrapped element should have UNIVERSAL class. - // 3. You should provide mode for wrapping, explicit or implicit. - // 4. If cls == '', no wrapping is performed, discarding all wrapper options + // 2. You should provide mode for wrapping, explicit or implicit. + // 3. If cls == '', no wrapping is performed, discarding all wrapper options cls string // should cls != 'universal' tagnum int = -1 // Provides with wrapper tag number, as n outer tag number. mode string // explicit or implicit, depends on definition schema. @@ -54,13 +51,15 @@ mut: // This field applied to element with DEFAULT keyword behaviour. // Its applied into wrapping of element or optionality of the element. - // If some element has DEFAULT keyword, set this field to true and gives default element into `default_value` field. + // If some element has DEFAULT keyword, set this field to true and gives default element + // into `default_value` field. has_default bool default_value ?Element } // `from_string` parses string as an attribute of field options. -// Its allows string similar to `application:4; optional; has_default` to be treated as an field options. +// Its allows string similar to `application:4; optional; has_default` +// to be treated as an field options. // See FieldOptions in `field_options.v` for more detail. pub fn FieldOptions.from_string(s string) !FieldOptions { if s.len == 0 { diff --git a/vlib/x/encoding/asn1/generalstring.v b/vlib/x/encoding/asn1/generalstring.v index bed592352..de5f1ac8a 100644 --- a/vlib/x/encoding/asn1/generalstring.v +++ b/vlib/x/encoding/asn1/generalstring.v @@ -15,12 +15,17 @@ pub const default_generalstring_tag = Tag{.universal, false, int(TagType.general // For historical reasons, the characters SPACE (number 32) and DELETE (number 127) // are not considered to be in either the C set or the G set, but instead stand on their own // We only treated GeneralString as an us-ascii charset -@[noinit] pub struct GeneralString { pub: value string } +fn (g GeneralString) check() ! { + if !g.value.is_ascii() { + return error('GeneralString: contains non-ascii chars') + } +} + // new creates a GeneralString element from string s. pub fn GeneralString.new(s string) !GeneralString { if !s.is_ascii() { @@ -52,9 +57,7 @@ fn (gst GeneralString) payload_with_rule(rule EncodingRule) ![]u8 { if rule != .der && rule != .ber { return error('GeneralString: not supported rule') } - if !gst.value.is_ascii() { - return error('GeneralString: contains non-ascii chars') - } + gst.check()! return gst.value.bytes() } diff --git a/vlib/x/encoding/asn1/ia5string.v b/vlib/x/encoding/asn1/ia5string.v index 365537016..170343a6f 100644 --- a/vlib/x/encoding/asn1/ia5string.v +++ b/vlib/x/encoding/asn1/ia5string.v @@ -8,7 +8,6 @@ pub const default_ia5string_tag = Tag{.universal, false, int(TagType.ia5string)} // ASN.1 IA5String type handling routine. // IA5String is a standard ASCII characters -@[noinit] pub struct IA5String { pub: value string diff --git a/vlib/x/encoding/asn1/integer.v b/vlib/x/encoding/asn1/integer.v index 6c647ed04..6f89e2b09 100644 --- a/vlib/x/encoding/asn1/integer.v +++ b/vlib/x/encoding/asn1/integer.v @@ -28,10 +28,10 @@ const max_integer_length = 2048 // then the bits of the first octet and bit 8 of the second octet. // a) shall not all be ones; and. // b) shall not all be zero. -// NOTE – These rules ensure that an integer value is always encoded in the smallest possible number of octets. -@[heap; noinit] +// NOTE – These rules ensure that an integer value is always encoded in +// the smallest possible number of octets. pub struct Integer { -mut: +pub: // underlying integer value with support from `i64` and `big.Integer` value IntValue } diff --git a/vlib/x/encoding/asn1/numericstring.v b/vlib/x/encoding/asn1/numericstring.v index ef50ad349..adf24eb0f 100644 --- a/vlib/x/encoding/asn1/numericstring.v +++ b/vlib/x/encoding/asn1/numericstring.v @@ -12,7 +12,6 @@ pub const default_numericstring_tag = Tag{.universal, false, int(TagType.numeric // restricted to sequences of zero, one or more characters from some // specified collection of characters. // That was : digit : 0,1,..9 and spaces char (0x20) -@[noinit] pub struct NumericString { pub: value string diff --git a/vlib/x/encoding/asn1/octetstring.v b/vlib/x/encoding/asn1/octetstring.v index 97ee71c61..7e2dd1333 100644 --- a/vlib/x/encoding/asn1/octetstring.v +++ b/vlib/x/encoding/asn1/octetstring.v @@ -13,9 +13,9 @@ const max_octetstring_size = 1 << 32 - 1 // ASN.1 UNIVERSAL TYPE OF OCTETSTRING. // // The ASN.1 OCTET STRING type contains arbitrary strings of octets. -// This type is very similar to BIT STRING, except that all values must be an integral number of eight bits. +// This type is very similar to BIT STRING, except that all values must +// be an integral number of eight bits. // You can use constraints to specify a maximum length for an OCTET STRING type. -@[noinit] pub struct OctetString { pub: value string diff --git a/vlib/x/encoding/asn1/oid.v b/vlib/x/encoding/asn1/oid.v index 76e32bc21..bd53f7198 100644 --- a/vlib/x/encoding/asn1/oid.v +++ b/vlib/x/encoding/asn1/oid.v @@ -12,7 +12,6 @@ pub const default_oid_tag = Tag{.universal, false, int(TagType.oid)} // ASN.1 ObjectIdentifier type. // // The ASN. 1 OBJECT IDENTIFIER type is used when you need to provide a unique identifier. -@[noinit] pub struct ObjectIdentifier { mut: value []int diff --git a/vlib/x/encoding/asn1/optional.v b/vlib/x/encoding/asn1/optional.v index e562813df..8852ba58e 100644 --- a/vlib/x/encoding/asn1/optional.v +++ b/vlib/x/encoding/asn1/optional.v @@ -8,7 +8,6 @@ module asn1 // (except for primitive type values in PER which are required by the PER standard to be absent in the encoding), // while with others (like DER) the DEFAULT value is NEVER encoded. For all encoding rules, // if the component that has a DEFAULT value is not encoded the receiving application must behave as though the DEFAULT value had been encoded. -@[noinit] pub struct Optional { // underlying element marked as an optional elem Element diff --git a/vlib/x/encoding/asn1/other_element.v b/vlib/x/encoding/asn1/other_element.v index cbb533fcf..747db01ae 100644 --- a/vlib/x/encoding/asn1/other_element.v +++ b/vlib/x/encoding/asn1/other_element.v @@ -8,7 +8,6 @@ module asn1 // // ASN.1 Raw Element. -@[noinit] pub struct RawElement { mut: // The tag is the (outer) tag of the TLV, if this a wrpper. @@ -244,7 +243,6 @@ fn (r RawElement) check_inner_tag() ! { } // ContextSpecific tagged type element. -@[noinit] pub struct ContextElement { RawElement } @@ -370,7 +368,6 @@ fn ContextElement.decode_with_options(bytes []u8, opt string) !(ContextElement, } // Limited support for APPLICATION CLASS Element. -@[noinit] pub struct ApplicationElement { RawElement } @@ -402,7 +399,6 @@ pub fn (app ApplicationElement) payload() ![]u8 { } // Limited support for PRIVATE CLASS Element. -@[noinit] pub struct PrivateELement { RawElement } diff --git a/vlib/x/encoding/asn1/printablestring.v b/vlib/x/encoding/asn1/printablestring.v index 09deb2781..bb9040a6a 100644 --- a/vlib/x/encoding/asn1/printablestring.v +++ b/vlib/x/encoding/asn1/printablestring.v @@ -16,7 +16,6 @@ pub const default_printablestring_tag = Tag{.universal, false, int(TagType.print // Digits 0, 1, ... 9 // symbols: (space) ' ( ) + , - . / : = ? // -@[noinit] pub struct PrintableString { pub: value string diff --git a/vlib/x/encoding/asn1/sequence.v b/vlib/x/encoding/asn1/sequence.v index 5f43aad02..3d1aff981 100644 --- a/vlib/x/encoding/asn1/sequence.v +++ b/vlib/x/encoding/asn1/sequence.v @@ -24,7 +24,6 @@ const default_sequence_size = 64 // default size // Sequence structure can represents both SEQUENCE and SEQUENCE OF type. // The encoding of a sequence value shall be constructed. // in DER encoded of SEQUENCE or SET, never encode a default value. -@[noinit] pub struct Sequence { mut: // maximal size of this sequence fields @@ -235,7 +234,6 @@ pub fn (seq Sequence) into_sequence_of[T]() !SequenceOf[T] { // ASN.1 SEQUENCE OF TYPE. // SequenceOf[T] is an arrays of generic T, so the generic T should fullfill Element interface. // We dont use generic aliases because generic type aliases are not yet implemented. -@[heap; noinit] pub struct SequenceOf[T] { mut: size int = default_sequence_size @@ -291,3 +289,8 @@ fn (so SequenceOf[T]) payload_with_rule(rule EncodingRule) ![]u8 { } return out } + +// fields returns underlying arrays of T from the SequenceOf[T]. +pub fn (so SequenceOf[T]) fields() []T { + return so.fields +} diff --git a/vlib/x/encoding/asn1/sequence_test.v b/vlib/x/encoding/asn1/sequence_test.v index 1ae8f60d3..8a633cef9 100644 --- a/vlib/x/encoding/asn1/sequence_test.v +++ b/vlib/x/encoding/asn1/sequence_test.v @@ -318,7 +318,7 @@ fn test_sequnce_of_sequence() { // was serialized into: 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 struct AlgorithmIdentifier { algorithm ObjectIdentifier - parameters Any + parameters AnyDefinedBy } fn (a AlgorithmIdentifier) tag() Tag { @@ -338,7 +338,7 @@ fn test_sequence_algorithm_identifier() ! { 0x05, 0x00] algo := AlgorithmIdentifier{ algorithm: ObjectIdentifier.new('1.2.840.113549.1.1.11')! - parameters: Any.new('null', Null.new()) + parameters: AnyDefinedBy.new(Null.new()) } out := encode(algo)! assert out == expected @@ -349,7 +349,7 @@ fn test_sequence_algorithm_identifier() ! { assert s0 == algo.algorithm s1 := seq.fields[1] as Null - prm := algo.parameters.params() as Null + prm := algo.parameters.params as Null assert s1 == prm } diff --git a/vlib/x/encoding/asn1/set.v b/vlib/x/encoding/asn1/set.v index 38cd8ba3c..da421b97b 100644 --- a/vlib/x/encoding/asn1/set.v +++ b/vlib/x/encoding/asn1/set.v @@ -15,7 +15,6 @@ const max_set_size = 255 // This differs from a SEQUENCE which contains an ordered list. // in DER encoding, SET types elements are sorted into tag order, and, // for SET OF types elements are sorted into ascending order of encoding. -@[noinit] pub struct Set { mut: // maximal size of this set fields @@ -234,7 +233,6 @@ fn (mut set Set) sort_set_fields() { // ASN.1 SET OF // -@[heap; noinit] pub struct SetOf[T] { mut: size int = default_set_size @@ -274,6 +272,11 @@ pub fn (so SetOf[T]) tag() Tag { return default_set_tag } +// fields returns underlying arrays of T from the SetOf[T]. +pub fn (so SetOf[T]) fields() []T { + return so.fields +} + // payload returns the payload of SetOf[T] element. pub fn (so SetOf[T]) payload() ![]u8 { mut sto := so diff --git a/vlib/x/encoding/asn1/time.v b/vlib/x/encoding/asn1/time.v index 1445a82ac..247790f59 100644 --- a/vlib/x/encoding/asn1/time.v +++ b/vlib/x/encoding/asn1/time.v @@ -3,11 +3,16 @@ // that can be found in the LICENSE file. module asn1 +import time + // default_utctime_tag is the default tag of ASN.1 UTCTIME type. pub const default_utctime_tag = Tag{.universal, false, int(TagType.utctime)} // default_generalizedtime_tag is the default tag of ASN.1 GENERALIZEDTIME type. pub const default_generalizedtime_tag = Tag{.universal, false, int(TagType.generalizedtime)} +const default_utctime_format = 'YYMMDDHHmmssZ' +const default_genztime_format = 'YYYYMMDDHHmmssZ' + // ASN.1 UNIVERSAL CLASS OF UTCTIME TYPE. // // For this time, UtcTime represented by simple string with format "YYMMDDhhmmssZ" @@ -28,7 +33,6 @@ pub const default_generalizedtime_tag = Tag{.universal, false, int(TagType.gener // TODO: // - check for invalid representation of date and hhmmss part. // - represented UtcTime in time.Time -@[noinit] pub struct UtcTime { pub: value string @@ -46,6 +50,18 @@ pub fn UtcTime.new(s string) !UtcTime { } } +fn UtcTime.from_time(t time.Time) !UtcTime { + // changes into utc time + utime := t.local_to_utc() + s := utime.custom_format(default_utctime_format) // 20241113060446+0 + // Its rather a hack, not efieient as should be. + // TODO: make it better + str := s.split('+') + val := str[0] + 'Z' + utc := UtcTime.new(val)! + return utc +} + fn UtcTime.from_bytes(b []u8) !UtcTime { return UtcTime.new(b.bytestr())! } @@ -67,6 +83,20 @@ pub fn (utc UtcTime) payload() ![]u8 { return utc.payload_with_rule(.der)! } +// into_utctime turns this UtcTime into corresponding UTC time. +pub fn (utc UtcTime) into_utctime() !time.Time { + valid := validate_utctime(utc.value)! + + if !valid { + return error('UtcTime: fail on validate utctime value') + } + + st := time.parse_format(utc.value, default_utctime_format)! + utime := st.local_to_utc() + + return utime +} + fn (utc UtcTime) payload_with_rule(rule EncodingRule) ![]u8 { valid := validate_utctime(utc.value)! @@ -196,6 +226,32 @@ pub fn GeneralizedTime.new(s string) !GeneralizedTime { } } +// from_time creates GeneralizedTime element from tine.Time (as an UTC time). +pub fn GeneralizedTime.from_time(t time.Time) !GeneralizedTime { + u := t.local_to_utc() + s := u.custom_format(default_genztime_format) + // adds support directly from time.Time + src := s.split('+') + val := src[0] + 'Z' + gt := GeneralizedTime.new(val)! + + return gt +} + +// into_utctime turns this GeneralizedTime into corresponding UTC time. +pub fn (gt GeneralizedTime) into_utctime() !time.Time { + valid := validate_generalizedtime(gt.value)! + + if !valid { + return error('GeneralizedTime: fail on validate value') + } + + st := time.parse_format(gt.value, default_genztime_format)! + utime := st.local_to_utc() + + return utime +} + // The tag of GeneralizedTime element. pub fn (gt GeneralizedTime) tag() Tag { return default_generalizedtime_tag diff --git a/vlib/x/encoding/asn1/time_test.v b/vlib/x/encoding/asn1/time_test.v index 77399a973..5e6b04f23 100644 --- a/vlib/x/encoding/asn1/time_test.v +++ b/vlib/x/encoding/asn1/time_test.v @@ -3,6 +3,8 @@ // that can be found in the LICENSE file. module asn1 +import time + fn test_serialize_utctime_basic() ! { inp := '191215190210Z' @@ -20,6 +22,26 @@ fn test_serialize_utctime_basic() ! { assert back.value == inp } +fn test_create_utctime_from_std_time() ! { + now := time.new(year: 2024, month: 11, day: 13, hour: 17, minute: 45, second: 50) + + utb := UtcTime.from_time(now)! + utc := UtcTime.from_time(now)! + + assert utb.value == utc.value + + tt := utc.into_utctime()! + assert now == tt + + inp := '191215190210Z' + t_inp := UtcTime.new(inp)! + t_inp_utc := t_inp.into_utctime()! + + // time to UtcTime back + tinp_back := UtcTime.from_time(t_inp_utc)! + assert tinp_back.value == inp +} + fn test_serialize_utctime_error_without_z() ! { // this input does not contains zulu 'Z' part inp := '191215190210' @@ -73,3 +95,23 @@ fn test_serialize_decode_generalizedtime() ! { assert back.value == s assert back.tag().tag_number() == int(TagType.generalizedtime) } + +fn test_create_generalizedtime_from_std_time() ! { + now := time.new(year: 2024, month: 11, day: 13, hour: 17, minute: 45, second: 50) + + gtb := GeneralizedTime.from_time(now)! + gtc := GeneralizedTime.from_time(now)! + + assert gtb.value == gtc.value + + tt := gtc.into_utctime()! + assert now == tt + + s := '20100102030405Z' + gt := GeneralizedTime.new(s)! + + g_utc := gt.into_utctime()! + g_utc_back := GeneralizedTime.from_time(g_utc)! + + assert g_utc_back.value == s +} diff --git a/vlib/x/encoding/asn1/utf8string.v b/vlib/x/encoding/asn1/utf8string.v index ebedd4fbc..029fed3eb 100644 --- a/vlib/x/encoding/asn1/utf8string.v +++ b/vlib/x/encoding/asn1/utf8string.v @@ -11,7 +11,6 @@ pub const default_utf8string_tag = Tag{.universal, false, int(TagType.utf8string // ASN.1 UNIVERSAL CLASS OF UTF8STRING TYPE. // // UTF8STRING is UTF8 unicode charset -@[noinit] pub struct Utf8String { pub: value string diff --git a/vlib/x/encoding/asn1/visiblestring.v b/vlib/x/encoding/asn1/visiblestring.v index def79046a..228e80bef 100644 --- a/vlib/x/encoding/asn1/visiblestring.v +++ b/vlib/x/encoding/asn1/visiblestring.v @@ -9,7 +9,6 @@ pub const default_visiblestring_tag = Tag{.universal, false, int(TagType.visible // ASN.1 UNIVERSAL CLASS OF VISIBLESTRING TYPE. // // The ASN.1 VisibleString type supports a subset of ASCII characters that does not include control characters. -@[noinit] pub struct VisibleString { pub: value string -- 2.39.5