v2 / vlib / x / encoding / asn1 / integer.v
447 lines · 396 sloc · 11.83 KB · 1957162c2a4545ec88c75f59d5c27c689d974a57
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 math.big
7import crypto.internal.subtle
8
9// default_integer_tag is the default of ASN.1 INTEGER type.
10pub const default_integer_tag = Tag{.universal, false, int(TagType.integer)}
11
12// ASN.1 INTEGER.
13//
14// The INTEGER type value can be a positive or negative number.
15// There are no limits imposed on the magnitude of INTEGER values in the ASN.1 standard.
16// Its handles number arbitrary length of number with support of `math.big` module.
17// But, for sake of safety, we limit the INTEGER limit to follow allowed length in
18// definite form of Length part, ie, 1008 bit, or 126 bytes
19// The encoding of an integer number shall be primitive.
20
21// Limit of length of INTEGER type, in bytes
22// Known big RSA keys is 4096 bits, ie, 512 bytes
23const max_integer_length = 2048
24
25// Integer represent Universal class of arbitrary length type of ASN.1 INTEGER.
26// The encoding of an integer value shall be primitive.
27// If the contents octets of an integer value encoding consist of more than one octet,
28// then the bits of the first octet and bit 8 of the second octet.
29// a) shall not all be ones; and.
30// b) shall not all be zero.
31// NOTE – These rules ensure that an integer value is always encoded in
32// the smallest possible number of octets.
33pub struct Integer {
34pub:
35 // underlying integer value with support from `i64` and `big.Integer`
36 value IntValue
37}
38
39// hex returns Integer value as a hex string.
40pub fn (v Integer) hex() string {
41 match v.value {
42 i64 {
43 val := v.value as i64
44 return val.hex_full()
45 }
46 big.Integer {
47 val := v.value as big.Integer
48 return val.hex()
49 }
50 }
51}
52
53fn (v Integer) str() string {
54 return 'Integer ${v.value.str()}'
55}
56
57// IntValue represents arbitrary integer value, currently we support
58// through primitive 164 type for integer value below < max_i64, and
59// use `big.Integer` for support arbitrary length of integer values.
60type IntValue = big.Integer | i64
61
62fn (v IntValue) str() string {
63 match v {
64 i64 {
65 val := v as i64
66 return val.str()
67 }
68 big.Integer {
69 val := v as big.Integer
70 return val.str()
71 }
72 }
73}
74
75// bytes get the bytes representation from underlying IntValue.
76fn (v IntValue) bytes() []u8 {
77 match v {
78 i64 {
79 return i64_to_bytes(v)
80 }
81 big.Integer {
82 // if v == big.zero_int or similar big.Integer values that produces empty bytes,
83 // returns v.bytes() directly can lead to undesired behavior thats doesn't aligned with
84 // ASN.1 INTEGER requirement. See the discussion on the discord about the issues
85 // at https://discord.com/channels/592103645835821068/592294828432424960/1230460279733620777
86 // so, we do some hack to get the correct value
87 // TODO: find the correct way to tackle this
88 if v == big.zero_int {
89 return [u8(0x00)]
90 }
91 // todo: proper check of 0 bytes length
92 if v.bit_len() == 0 {
93 return [u8(0x00)]
94 }
95 // otherwise, we use v.bytes() directly
96 b, _ := v.bytes()
97 return b
98 }
99 }
100}
101
102// from_i64 creates new a ASN.1 Integer from i64 v.
103pub fn Integer.from_i64(v i64) Integer {
104 return Integer{
105 value: IntValue(v)
106 }
107}
108
109// from_int creates a new ASN.1 Integer from int v.
110pub fn Integer.from_int(v int) Integer {
111 return Integer{
112 value: IntValue(i64(v))
113 }
114}
115
116// from_bigint creates a new ASN.1 Integer from big.Integer b
117pub fn Integer.from_bigint(b big.Integer) Integer {
118 return Integer{
119 value: IntValue(b)
120 }
121}
122
123// from_string creates a new ASN.1 Integer from decimal string s.
124// If your string value is below max_i64, use from_i64 instead
125pub fn Integer.from_string(s string) !Integer {
126 v := big.integer_from_string(s)!
127 return Integer{
128 value: IntValue(v)
129 }
130}
131
132// from_hex creates a new ASN.1 Integer from hex string in x
133// where x is a valid hex string without `0x` prefix.
134// If your string value is below max_i64, use from_i64 instead
135pub fn Integer.from_hex(x string) !Integer {
136 s := big.integer_from_radix(x, 16)!
137 return Integer{
138 value: IntValue(s)
139 }
140}
141
142// from_bytes creates a new ASN.1 Integer from bytes array in b.
143// Its try to parse bytes as in two's complement form.
144fn Integer.from_bytes(b []u8) !Integer {
145 return Integer.unpack_from_twoscomplement_bytes(b)!
146}
147
148// unpack_from_twoscomplement_bytes parses the bytes in b into the Integer
149// value in the big-endian two's complement way. If b[0]&80 != 0, the number
150// is negative. If b is empty it would be error.
151fn Integer.unpack_from_twoscomplement_bytes(b []u8) !Integer {
152 // FIXME: should we return error instead ?
153 if b.len == 0 {
154 return error('Integer: null bytes')
155 }
156 if b.len > 7 {
157 mut num := big.integer_from_bytes(b)
158 // negative number
159 if b.len > 0 && b[0] & 0x80 > 0 {
160 sub := big.one_int.left_shift(u32(b.len) * 8)
161 num -= sub
162 }
163
164 return Integer{
165 value: IntValue(num)
166 }
167 }
168 // use i64
169 val := read_i64(b)!
170 res := Integer.from_i64(val)
171 return res
172}
173
174// bytes return underlying bytes array
175fn (v Integer) bytes() []u8 {
176 return v.value.bytes()
177}
178
179// bytes_len returns underlying bytes length
180fn (v Integer) bytes_len() int {
181 b := v.value.bytes()
182 return b.len
183}
184
185// tag returns the tag of Integer type element
186pub fn (v Integer) tag() Tag {
187 return default_integer_tag
188}
189
190// payload returns the payload of Integer type element.
191pub fn (v Integer) payload() ![]u8 {
192 bytes, _ := v.pack_into_twoscomplement_form()!
193 return bytes
194}
195
196// pack_into_twoscomplement_form serialize Integer in two's-complement rules.
197// - The integer value contains the encoded integer if it is positive, or its two's complement if it is negative.
198// - If the integer is positive but the high order bit is set to 1, a leading 0x00 is added to the content
199// to indicate that the number is not negative.
200// - If the number is negative after applying two's-complement rules, and the most-significant-bit of the
201// the high order bit of the bytes results isn't set, pad it with 0xff in order to keep the number negative.
202fn (v Integer) pack_into_twoscomplement_form() !([]u8, int) {
203 match v.value {
204 i64 {
205 val := v.value as i64
206 mut bytes := i64_to_bytes(val)
207 return bytes, bytes.len
208 }
209 big.Integer {
210 match v.value.signum {
211 0 {
212 return [u8(0x00)], 1
213 }
214 1 {
215 mut b := v.bytes()
216 // handle the zero issues
217 if b.len == 0 {
218 return [u8(0x00)], 1
219 }
220 // If the integer is positive but the high order bit is set to 1, a leading 0x00 is added
221 // to the content to indicate that the number is not negative
222 if b[0] & 0x80 > 0 {
223 b.prepend(u8(0x00))
224 }
225 return b, b.len
226 }
227 -1 {
228 // A negative number has to be converted to two's-complement form.
229 // by invert the number and then subtract it with big(1), or with other mean
230 // Flip all of the bits in the value and then add one to the resulting value.
231 // If the most-significant-bit isn't set then we'll need to pad the
232 // beginning with 0xff in order to keep the number negative.
233 negv := v.value.neg()
234 negvminus1 := negv - big.one_int
235 mut bytes, _ := negvminus1.bytes()
236 for i, _ in bytes {
237 bytes[i] ^= 0xff
238 }
239 if bytes.len == 0 || bytes[0] & 0x80 == 0 {
240 bytes.prepend(u8(0xff))
241 }
242 return bytes, bytes.len
243 }
244 else {
245 return error('should unreachable')
246 }
247 }
248 }
249 }
250}
251
252// equal do checking if integer n was equal to integer m.
253// ISSUE?: There are some issues when compared n == m directly,
254// its fails even internally its a same, so we provide and use equality check
255pub fn (n Integer) equal(m Integer) bool {
256 nbytes := n.bytes()
257 mbytes := m.bytes()
258 // todo: check sign equality
259 // m.tag() == n.tag() by definition, no need to check
260 return subtle.constant_time_compare(nbytes, mbytes) == 1
261}
262
263// Integer.unpack_and_validate deserializes bytes in b into Integer
264// in two's complement way and perform validation on this bytes to
265// meet der requirement.
266fn Integer.unpack_and_validate(b []u8) !Integer {
267 if !valid_bytes(b, true) {
268 return error('Integer: check return false')
269 }
270 ret := Integer.unpack_from_twoscomplement_bytes(b)!
271 return ret
272}
273
274// as_bigint casts Integer value to big.Integer or error on fails.
275pub fn (v Integer) as_bigint() !big.Integer {
276 if v.value is big.Integer {
277 val := v.value as big.Integer
278 return val
279 }
280 return error('Integer not hold big.Integer type')
281}
282
283// as_i64 casts Integer value to i64 value or error on fails.
284pub fn (v Integer) as_i64() !i64 {
285 if v.value is i64 {
286 val := v.value as i64
287 return val
288 }
289 return error('Integer not hold i64 type')
290}
291
292// parse tries to read and parse into Integer type or return error on fails.
293fn Integer.parse(mut p Parser) !Integer {
294 tag := p.read_tag()!
295 if !tag.equal(default_integer_tag) {
296 return error('Get unexected non Integer tag')
297 }
298 length := p.read_length()!
299 if length < 1 {
300 return error('Get length < 1 for Integer length')
301 }
302 bytes := p.read_bytes(length)!
303 ret := Integer.from_bytes(bytes)!
304
305 return ret
306}
307
308// decode tries to decode bytes array into Integer type or error on fails
309fn Integer.decode(bytes []u8) !(Integer, int) {
310 return Integer.decode_with_rule(bytes, 0, .der)!
311}
312
313// decode_with_rule tries to decode bytes back into ASN.1 Integer.
314// Its accepts `loc` params, the location (offset) within bytes where the unpack start from.
315// If not sure set to 0 to drive unpacking and rule of `Encodingrule`, currently only support`.der`.
316fn Integer.decode_with_rule(bytes []u8, loc int, rule EncodingRule) !(Integer, int) {
317 if bytes.len < 3 {
318 return error('Integer: bad bytes length')
319 }
320 tag, length_pos := Tag.decode_with_rule(bytes, loc, rule)!
321 if !tag.equal(default_integer_tag) {
322 return error('Get unexpected Integer tag')
323 }
324 length, content_pos := Length.decode_with_rule(bytes, length_pos, rule)!
325 payload := if length == 0 {
326 []u8{}
327 } else {
328 if content_pos + length > bytes.len {
329 return error('Not enought bytes to read on')
330 }
331 unsafe { bytes[content_pos..content_pos + length] }
332 }
333
334 // buf := trim_bytes(payload)!
335 next := content_pos + length
336 result := Integer.from_bytes(payload)!
337
338 return result, next
339}
340
341// Utility function
342//
343fn is_highest_bit_set(src []u8) bool {
344 if src.len > 0 {
345 return src[0] & 0x80 == 0
346 }
347 return false
348}
349
350fn trim_bytes(src []u8) ![]u8 {
351 if src.len == 0 {
352 return error('bad src')
353 }
354 // TODO: removes prepended bytes when its meet criteria
355 // positive value but its prepended with 0x00
356 if src.len > 1 && src[0] == 0x00 && src[1] & 0x80 > 0 {
357 bytes := src[1..]
358 return bytes
359 }
360 // TODO: how to do with multiples 0xff
361 if src.len > 1 && src[0] == 0xff && src[1] & 0x80 == 0 {
362 bytes := src[1..]
363 return bytes
364 }
365 return src
366}
367
368// length_i64 gets bytes length needed to reperesent this i64 value
369fn length_i64(val i64) int {
370 mut i := val
371 mut n := 1
372
373 for i > 127 {
374 n++
375 i >>= 8
376 }
377
378 for i < -128 {
379 n++
380 i >>= 8
381 }
382
383 return n
384}
385
386// i64_to_bytes transforms i64 value into bytes representation
387fn i64_to_bytes(i i64) []u8 {
388 mut n := length_i64(i)
389 mut dst := []u8{len: n}
390 for j := 0; j < n; j++ {
391 dst[j] = u8(i >> u32((n - 1 - j) * 8))
392 }
393 return dst
394}
395
396// read_i64 read src as signed i64
397fn read_i64(src []u8) !i64 {
398 if !valid_bytes(src, true) {
399 return error('i64 check return false')
400 }
401 mut ret := i64(0)
402
403 if src.len > 8 {
404 return error('too large integer')
405 }
406 for i := 0; i < src.len; i++ {
407 ret <<= 8
408 ret |= i64(src[i])
409 }
410
411 ret <<= 64 - u8(src.len) * 8
412 ret >>= 64 - u8(src.len) * 8
413
414 // try to serialize back, and check its matching original one
415 // and gives a warning when its not match.
416
417 dst := i64_to_bytes(ret)
418 if dst != src {
419 eprintln('maybe integer bytes not in shortest form')
420 }
421
422 return ret
423}
424
425// i32 handling
426//
427// read_i32 read from bytes
428fn read_i32(src []u8) !int {
429 if !valid_bytes(src, true) {
430 return error('i32 check return false')
431 }
432
433 ret := read_i64(src)!
434 if ret != i64(int(ret)) {
435 return error('integer too large')
436 }
437
438 return int(ret)
439}
440
441fn length_i32(v i32) int {
442 return length_i64(i64(v))
443}
444
445fn i32_to_bytes(v i32) []u8 {
446 return i64_to_bytes(i64(v))
447}
448