v2 / vlib / x / encoding / asn1 / boolean.v
167 lines · 149 sloc · 4.85 KB · 58fc4dead559901cad648fb695f31d0f6de9945a
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// default_boolean_tag is the default tag of ASN.1 BOOLEAN type.
7pub const default_boolean_tag = Tag{.universal, false, int(TagType.boolean)}
8
9// ASN.1 UNIVERSAL CLASS OF BOOLEAN TYPE.
10//
11// A Boolean value can take true or false.
12// ASN.1 DER encoding restricts encoding of boolean true value into 0xff
13// and otherwise, encodes into zero (0x00) for false value.
14// The encoding of a boolean value shall be primitive. The contents octets shall consist of a single octet.
15pub struct Boolean {
16mut:
17 // boolean value represented in single byte to allow stores multiple value represents
18 // true value others than 0xff, ie., non-null byte representing true value.
19 value u8
20}
21
22// new creates a new Boolean value from true or false value.
23// By default, when you pass true, its would store 0xff as underlying byte value.
24// If you want more to be relaxed, see from_u8 to creates with another byte value.
25pub fn Boolean.new(value bool) Boolean {
26 mut ret := Boolean{}
27 val := if value { u8(0xff) } else { u8(0x00) }
28 ret.value = val
29
30 return ret
31}
32
33// from_u8 creates a new Boolean value from single byte value.
34fn Boolean.from_u8(value u8) Boolean {
35 return Boolean{
36 value: value
37 }
38}
39
40// tag returns the tag of Boolean type.
41pub fn (v Boolean) tag() Tag {
42 return default_boolean_tag
43}
44
45fn (v Boolean) str() string {
46 res := if v.value() { 'Boolean (TRUE)' } else { 'Boolean (FALSE)' }
47 return res
48}
49
50// payload returns the payload of Boolean type in .der rule.
51pub fn (b Boolean) payload() ![]u8 {
52 return b.payload_with_rule(.der)!
53}
54
55fn (b Boolean) payload_with_rule(rule EncodingRule) ![]u8 {
56 // by default, true value is encoded to 0xff
57 if rule == .der {
58 if b.value != u8(0xff) && b.value != u8(0x00) {
59 return error('Boolean: in .der, only 0xff or 0x00 are allowed')
60 }
61 }
62 return [b.value]
63}
64
65// parse tries to read a Boolean type from parser or return error on fails
66pub fn Boolean.parse(mut p Parser) !Boolean {
67 tag := p.read_tag()!
68 if !tag.equal(default_boolean_tag) {
69 return error('Get unexpected non boolean tag')
70 }
71 length := p.read_length()!
72 if length != 1 {
73 return error('Bad boolean length')
74 }
75 bytes := p.read_bytes(length)!
76 value := Boolean.from_bytes(bytes)!
77
78 return value
79}
80
81// from_bytes creates a new ASN.1 BOOLEAN type from bytes b.
82// Boolean type should fit in one byte length, otherwise it would return error.
83// by default, p.rule == .der to follow DER restriction
84fn Boolean.from_bytes(bytes []u8) !Boolean {
85 return Boolean.from_bytes_with_rule(bytes, .der)
86}
87
88fn Boolean.from_bytes_with_rule(bytes []u8, rule EncodingRule) !Boolean {
89 if bytes.len != 1 {
90 return error('Boolean: bad bytes')
91 }
92 // for DER requirements that "If the encoding represents the boolean value TRUE,
93 // its single contents octet shall have all eight bits set to one."
94 // Thus only 0 and 255 are valid encoded values.
95 // But, we relaxed this requirement to allow other than non-null
96 // value to be treated as TRUE value, like in BER encoding.
97 match bytes[0] {
98 u8(0x00) {
99 return Boolean.from_u8(0x00)
100 }
101 u8(0xff) {
102 return Boolean.from_u8(0xff)
103 }
104 else {
105 // other non-null value is treated as TRUE boolean value
106 if rule == .der {
107 return error('Boolean: in DER, other than 0xff is not allowed for true value')
108 }
109 return Boolean.from_u8(bytes[0])
110 }
111 }
112}
113
114// value gets the boolean value represented by underlying byte value.
115// It returns FALSE if the byte == 0x00 and TRUE otherwise.
116pub fn (b Boolean) value() bool {
117 return b.value_with_rule(.der)
118}
119
120fn (b Boolean) value_with_rule(rule EncodingRule) bool {
121 match b.value {
122 u8(0xff) {
123 return true
124 }
125 u8(0x00) {
126 return false
127 }
128 else {
129 if rule == .der {
130 return false
131 }
132 // otherwise non-null is considered as true
133 return true
134 }
135 }
136}
137
138// decode tries to decode bytes array into Booelan type or error on fails.
139fn Boolean.decode(src []u8) !(Boolean, int) {
140 return Boolean.decode_with_rule(src, 0, .der)!
141}
142
143fn Boolean.decode_with_rule(src []u8, loc int, rule EncodingRule) !(Boolean, int) {
144 if src.len < 3 {
145 return error('Boolean: bad length bytes')
146 }
147 if rule != .der && rule != .ber {
148 return error('Boolean: not supported rule')
149 }
150 tag, length_pos := Tag.decode_with_rule(src, loc, rule)!
151 if !tag.equal(default_boolean_tag) {
152 return error('Unexpected non-boolean tag')
153 }
154 length, content_pos := Length.decode_with_rule(src, length_pos, rule)!
155 if length != 1 {
156 return error('Boolean: should have length 1')
157 }
158 if content_pos >= src.len || content_pos + length > src.len {
159 return error('Boolean: truncated payload bytes')
160 }
161 payload := unsafe { src[content_pos..content_pos + length] }
162
163 // boolean value should be encoded in single byte
164 res := Boolean.from_bytes_with_rule(payload, rule)!
165 next := content_pos + length
166 return res, next
167}
168