v2 / vlib / x / encoding / asn1 / bitstring.v
225 lines · 199 sloc · 6.61 KB · bd408289d501263b0464839dc32ca715308df09a
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
6import arrays
7
8// default_bitstring_tag is the default tag of the ASN.1 BITSTRING type.
9pub const default_bitstring_tag = Tag{.universal, false, int(TagType.bitstring)}
10
11// ASN.1 UNIVERSAL CLASS OF BITSTRING TYPE.
12//
13// The BIT STRING type denotes an arbitrary string of bits (ones and zeroes).
14// A BIT STRING value can have any length, including zero. This type is a string type.
15// BIT STRING, OCTET STRING, UTCTime, GeneralizedTime, and the various string types can use
16// either primitive encoding or constructed encoding, at the sender’s discretion-- in BER.
17// However, in DER all types that have an encoding choice between primitive and constructed
18// must use the primitive encoding. DER restricts the encoding to primitive only.
19// The same applies for BITSTRING. ie, For BIT STRING and OCTET STRING types,
20// DER does not allow the constructed form (breaking a string into multiple TLVs)
21// or the indefinite length form.
22pub struct BitString {
23mut:
24 data []u8
25 pad u8 // numbers of unused bits
26}
27
28// data returns underlying BitString data.
29pub fn (bs BitString) data() []u8 {
30 return bs.data
31}
32
33// pad returns underlying BitString pad byte.
34pub fn (bs BitString) pad() u8 {
35 return bs.pad
36}
37
38// check performs check internal validity of the BitString data.
39fn (bs BitString) check() ! {
40 // to align with octet size, ie, 8 in length, pad bits only need maximum 7 bits
41 // and when the data.len is multiples of 8, no need to pad, ie, pad should 0.
42 if bs.pad > 7 || (bs.data.len == 0 && bs.pad != 0) {
43 return error('BitString: bad pad bits or zero length')
44 }
45 // this check if the pad != 0, whether the last `pad` number of bits of the last byte
46 // is all bits cleared, and it was not used in the BitString data.
47 if bs.pad > 0 && (bs.data[bs.data.len - 1]) & ((1 << bs.pad) - 1) != 0 {
48 return error('BitString: bad args')
49 }
50}
51
52// tag returns the tag of BITSTRING type.
53pub fn (bs BitString) tag() Tag {
54 return default_bitstring_tag
55}
56
57// payload returns the payload of BITSTRING instance.
58pub fn (bs BitString) payload() ![]u8 {
59 bs.check()!
60 mut out := []u8{}
61 out << bs.pad
62 out << bs.data
63 return out
64}
65
66// str returns a string representation of the current state of bs.
67pub fn (bs BitString) str() string {
68 return 'BitString: ${bs.data.hex()} (${bs.pad})'
69}
70
71// parse BitString using the given Parser.
72fn BitString.parse(mut p Parser) !BitString {
73 tag := p.read_tag()!
74 if !tag.equal(default_bitstring_tag) {
75 return error('Get unexpected non bitstring tag')
76 }
77 length := p.read_length()!
78 bytes := if length == 0 {
79 []u8{}
80 } else {
81 p.read_bytes(length)!
82 }
83 bs := BitString.from_bytes(bytes)!
84
85 return bs
86}
87
88fn BitString.decode(bytes []u8) !(BitString, int) {
89 bs, next := BitString.decode_with_rule(bytes, .der)!
90 return bs, next
91}
92
93fn BitString.decode_with_rule(bytes []u8, rule EncodingRule) !(BitString, int) {
94 tag, length_pos := Tag.decode_with_rule(bytes, 0, rule)!
95 if !tag.equal(default_bitstring_tag) {
96 return error('Unexpected non-bitstring tag')
97 }
98 length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)!
99 if length < 1 {
100 return error('BitString: zero length bit string')
101 }
102 if content_pos >= bytes.len || content_pos + length > bytes.len {
103 return error('BitString: truncated payload bytes')
104 }
105 payload := unsafe { bytes[content_pos..content_pos + length] }
106 bs := BitString.new_with_pad(payload[1..], payload[0])!
107 next := content_pos + length
108
109 return bs, next
110}
111
112// from_binary_string creates a new BitString from binary bits arrays in s,
113// ie, arrays of 1 and 0. If s.len is not a multiple of 8, it will contain non-null pad,
114// otherwise, the pad is null.
115// The bit string '011010001' will need two content octets: 01101000 10000000 (hexadecimal 68 80);
116// seven bits of the last octet are not used and is interpreted as a pad value.
117// Example:
118// ```v
119// import x.encoding.asn1
120// bs := asn1.BitString.from_binary_string('011010001')!
121// assert bs.str() == 'BitString: 6880 (7)'
122// ```
123pub fn BitString.from_binary_string(s string) !BitString {
124 res, pad := parse_bits_string(s)!
125 return BitString.new_with_pad(res, u8(pad))!
126}
127
128// new creates a new BitString from regular string s.
129pub fn BitString.new(s string) !BitString {
130 return BitString.from_bytes(s.bytes())!
131}
132
133// from_bytes creates a new BitString from bytes array in src.
134// Note: Your first byte of the src as a pad bit.
135fn BitString.from_bytes(src []u8) !BitString {
136 if src.len < 1 {
137 return error('BitString error: need more bytes')
138 }
139 return BitString.new_with_pad(src[1..], src[0])!
140}
141
142// new_with_pad creates a new BitString from bytes array in bytes with
143// specific padding bits in pad
144fn BitString.new_with_pad(bytes []u8, pad u8) !BitString {
145 bs := BitString{
146 data: bytes
147 pad: pad
148 }
149 bs.check()!
150 return bs
151}
152
153fn (bs BitString) bytes_len() int {
154 return bs.data.len + 1
155}
156
157// Utility function
158
159// maximum allowed binary bits string length
160const max_bitstring_len = 65536
161
162// valid_bitstring checks whether this s string is a valid of arrays of binary string `0` and `1`.
163fn valid_bitstring(s string) bool {
164 return s.contains_only('01') && s.len <= max_bitstring_len
165}
166
167// parse_into_u8 parses arrays of binary bits of `0` and '1' with length == 8 into single byte (u8)
168// Example: parse_to_u8('01101000')! == u8(0x68) // => true
169fn parse_into_u8(s string) !u8 {
170 if s.len != 8 {
171 return error('not 8 length')
172 }
173 if !valid_bitstring(s) {
174 return error('not valid bit string: ${s}')
175 }
176 mut b := u8(0)
177
178 mut ctr := 0
179 bitmask := 0x01
180 for bit := 0; bit < s.len; bit++ {
181 v := u32(s[ctr] & bitmask) << (7 - bit)
182 b |= u8(v & 0x00ff)
183 ctr += 1
184 }
185 return b
186}
187
188// pad_into_octet pads string s by string `0` into new string with size 8
189fn pad_into_octet(s string) !string {
190 if valid_bitstring(s) && s.len > 0 && s.len < 8 {
191 len := if s.len % 8 == 0 { 0 } else { 8 - s.len % 8 }
192 pad := '0'.repeat(len)
193 res := s + pad
194 return res
195 }
196 return error('not valid bit string')
197}
198
199// parse_bits_string parses binary bits string s into arrays of byte and number of padding bits
200fn parse_bits_string(s string) !([]u8, int) {
201 if s.len == 0 {
202 return []u8{}, 0
203 }
204 if !valid_bitstring(s) {
205 return error('not valid bit string')
206 }
207 arr := arrays.chunk[u8](s.bytes(), 8)
208 mut res := []u8{}
209 pad_len := if s.len % 8 == 0 { 0 } else { 8 - s.len % 8 }
210 if pad_len > 7 {
211 return error('pad_len > 7')
212 }
213 for item in arr {
214 if item.len != 8 {
215 bts := pad_into_octet(item.bytestr())!
216 val := parse_into_u8(bts)!
217 res << val
218 }
219 if item.len == 8 {
220 b := parse_into_u8(item.bytestr())!
221 res << b
222 }
223 }
224 return res, pad_len
225}
226