v2 / vlib / x / encoding / asn1 / enumerated.v
154 lines · 129 sloc · 3.59 KB · 897ec51480ee51714f03534117f603eb28dae7fa
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// The default tag of ASN.1 ENUMERATED type.
7const default_enumerated_tag = Tag{.universal, false, int(TagType.enumerated)}
8
9// ASN.1 ENUMERATED TYPE.
10//
11// Enumerated type treated as ordinary integer, only differs on tag value.
12// The encoding of an enumerated value shall be that of the integer value
13// with which it is associated. In DER rule, Enumerated type should be primitive type.
14pub struct Enumerated {
15pub:
16 value int
17}
18
19fn (e Enumerated) str() string {
20 return 'Enumerated (${e.value})'
21}
22
23// new creates a new Enumerated from int value.
24pub fn Enumerated.new(val int) Enumerated {
25 return Enumerated{
26 value: val
27 }
28}
29
30// The tag of enumerated element.
31pub fn (e Enumerated) tag() Tag {
32 return default_enumerated_tag
33}
34
35// The payload of the enumerated element.
36pub fn (e Enumerated) payload() ![]u8 {
37 return e.payload_with_rule(.der)!
38}
39
40fn Enumerated.from_bytes(bytes []u8) !Enumerated {
41 if !valid_bytes(bytes, true) {
42 return error('Enumerated: failed check')
43 }
44 mut ret := int(0)
45 for i := 0; i < bytes.len; i++ {
46 ret <<= 8
47 ret |= int(bytes[i])
48 }
49
50 ret <<= 64 - u8(bytes.len) * 8
51 ret >>= 64 - u8(bytes.len) * 8
52
53 if ret != int(int(ret)) {
54 return error('integer too large')
55 }
56 return Enumerated{
57 value: int(ret)
58 }
59}
60
61// parse into Enumerated type from parser p.
62fn Enumerated.parse(mut p Parser) !Enumerated {
63 tag := p.read_tag()!
64 if !tag.equal(default_enumerated_tag) {
65 return error('Bad Enumerated tag')
66 }
67 length := p.read_length()!
68 bytes := p.read_bytes(length)!
69
70 res := Enumerated.from_bytes(bytes)!
71
72 return res
73}
74
75fn Enumerated.decode(src []u8) !(Enumerated, int) {
76 return Enumerated.decode_with_rule(src, .der)!
77}
78
79fn Enumerated.decode_with_rule(bytes []u8, rule EncodingRule) !(Enumerated, int) {
80 tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)!
81 if !tag.equal(default_enumerated_tag) {
82 return error('Unexpected non-Enumerated tag')
83 }
84 length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)!
85 content := if length == 0 {
86 []u8{}
87 } else {
88 if content_pos >= bytes.len || content_pos + length > bytes.len {
89 return error('Enumerated: truncated payload bytes')
90 }
91 unsafe { bytes[content_pos..content_pos + length] }
92 }
93
94 val := Enumerated.from_bytes(content)!
95 next := content_pos + length
96
97 return val, next
98}
99
100fn (e Enumerated) payload_with_rule(rule EncodingRule) ![]u8 {
101 if rule != .der && rule != .ber {
102 return error('Enumerated.pack: unsupported rule')
103 }
104 mut n := e.enumerated_len()
105 mut dst := []u8{len: n}
106
107 for j := 0; j < n; j++ {
108 dst[j] = u8(e.value >> u32(n - 1 - j) * 8)
109 }
110 return dst
111}
112
113fn (e Enumerated) enumerated_len() int {
114 mut i := e.value
115 mut n := 1
116
117 for i > 127 {
118 n++
119 i >>= 8
120 }
121
122 for i < -128 {
123 n++
124 i >>= 8
125 }
126
127 return n
128}
129
130// Utility function
131//
132// valid_bytes validates bytes meets some requirement for BER/DER encoding.
133fn valid_bytes(src []u8, signed bool) bool {
134 // Requirement for der encoding
135 // The contents octets shall consist of one or more octets.
136 if src.len == 0 {
137 return false
138 }
139
140 // check for minimaly encoded
141 // If the contents octets of an integer value encoding consist of more
142 // than one octet, then the bits of the first octet and bit 8 of
143 // the second octets shall not all be ones; and shall not all be zero.
144 if src.len > 1 && ((src[0] == 0 && src[1] & 0x80 == 0)
145 || (src[0] == 0xff && src[1] & 0x80 == 0x80)) {
146 return false
147 }
148
149 // reject negative for unsigned type
150 if !signed && src[0] & 0x80 == 0x80 {
151 return false
152 }
153 return true
154}
155