Skip to content

Commit

Permalink
x.encoding.asn1: cleanup code and comments (vlang#22847)
Browse files Browse the repository at this point in the history
  • Loading branch information
blackshirt authored Nov 13, 2024
1 parent 62bdf99 commit 897ec51
Show file tree
Hide file tree
Showing 24 changed files with 169 additions and 76 deletions.
4 changes: 2 additions & 2 deletions vlib/x/encoding/asn1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 8 additions & 14 deletions vlib/x/encoding/asn1/any.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
42 changes: 25 additions & 17 deletions vlib/x/encoding/asn1/bitstring.v
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -106,30 +121,23 @@ 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')
}
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 {
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/boolean.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/choices.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/core.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions vlib/x/encoding/asn1/enumerated.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 8 additions & 9 deletions vlib/x/encoding/asn1/field_options.v
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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 {
Expand Down
11 changes: 7 additions & 4 deletions vlib/x/encoding/asn1/generalstring.v
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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()
}

Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/ia5string.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions vlib/x/encoding/asn1/integer.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/numericstring.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions vlib/x/encoding/asn1/octetstring.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/oid.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/optional.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions vlib/x/encoding/asn1/other_element.v
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -244,7 +243,6 @@ fn (r RawElement) check_inner_tag() ! {
}

// ContextSpecific tagged type element.
@[noinit]
pub struct ContextElement {
RawElement
}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -402,7 +399,6 @@ pub fn (app ApplicationElement) payload() ![]u8 {
}

// Limited support for PRIVATE CLASS Element.
@[noinit]
pub struct PrivateELement {
RawElement
}
Expand Down
1 change: 0 additions & 1 deletion vlib/x/encoding/asn1/printablestring.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions vlib/x/encoding/asn1/sequence.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Loading

0 comments on commit 897ec51

Please sign in to comment.