-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbitstr.go
216 lines (174 loc) · 4.42 KB
/
bitstr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package dirsyn
/*
bitstring.go implements the ASN.1 BIT STRING type and methods.
*/
import (
"encoding/asn1"
)
/*
BitString is a type alias of [asn1.BitString], which can conform to [§ 3.3.2
of RFC 4517]:
BitString = SQUOTE *binary-digit SQUOTE "B"
binary-digit = "0" / "1"
From [§ 1.4 of RFC 4512]:
SQUOTE = %x27 ; single quote ("'")
[§ 1.4 of RFC 4512]: https://datatracker.ietf.org/doc/html/rfc4512#section-1.4
[§ 3.3.2 of RFC 4517]: https://datatracker.ietf.org/doc/html/rfc4517#section-3.3.2
*/
type BitString asn1.BitString
/*
String returns the string representation of the receiver instance.
*/
func (r BitString) String() (bs string) {
if len(r.Bytes)*8 == r.BitLength {
for _, b := range r.Bytes {
bs += fuint(uint64(b), 2)
}
bs = string(rune('\'')) + bs +
string(rune('\'')) +
string(rune('B'))
}
return
}
/*
IsZero returns a Boolean value indicative of a nil receiver state.
*/
func (r BitString) IsZero() bool { return &r == nil }
/*
BitString returns an error following an analysis of x in the context of
an ASN.1 BIT STRING.
*/
func (r RFC4517) BitString(x any) (bs BitString, err error) {
bs, err = marshalBitString(x)
return
}
func bitString(x any) (result Boolean) {
result.Set(false)
if _, err := marshalBitString(x); err == nil {
result.Set(true)
}
return
}
func marshalBitString(x any) (bs BitString, err error) {
var raw []byte
if raw, err = assertBitString(x); err == nil {
if raw, err = verifyBitStringContents(raw); err == nil {
var tx string
var bss asn1.BitString
for i := len(raw); i > 0 && err == nil; i -= 8 {
tx = string(raw[:i])
if i-8 >= 0 {
tx = string(raw[i-8 : i])
}
var bd uint64
bd, err = puint(tx, 2, 8)
bss.Bytes = append(bss.Bytes, []byte{byte(bd)}...)
}
if err == nil {
if _, err = asn1m(bss); err == nil {
bss.BitLength = len(bss.Bytes) * 8
bs = BitString(bss)
}
}
}
}
return
}
func assertBitString(x any) (raw []byte, err error) {
switch tv := x.(type) {
case []byte:
if len(tv) == 0 {
err = errorBadLength("BitString", 0)
break
}
raw = tv
case string:
raw, err = assertBitString([]byte(tv))
default:
err = errorBadType("BitString")
}
return
}
func verifyBitStringContents(raw []byte) ([]byte, error) {
var err error
// Last char MUST be 'B' rune, else die.
if term := raw[len(raw)-1]; term != 'B' {
err = errorTxt("Incompatible terminating character for BitString: " + string(term))
return raw, err
}
// Trim terminating char
raw = raw[:len(raw)-1]
// Make sure there are enough remaining
// characters to actually do something.
if len(raw) < 3 {
err = errorTxt("Incompatible remaining length for BitString: " + fmtInt(int64(len(raw)), 10))
return raw, err
}
// Verify (and then remove) single quotes
L := raw[0]
R := raw[len(raw)-1]
if L != '\'' || R != '\'' {
err = errorTxt("Incompatible encapsulating characters BitString: " + string(L) + "/" + string(R))
return raw, err
}
raw = raw[1 : len(raw)-1]
for i := 0; i < len(raw); i++ {
if !isBinChar(rune(raw[i])) {
err = errorTxt("Incompatible non-binary character for BitString" + string(raw[i]))
break
}
}
return raw, err
}
/*
bitStringMatch returns a Boolean value indicative of a BitStringMatch
as described in [§ 4.2.1 of RFC 4517].
OID: 2.5.13.16
[§ 4.2.1 of RFC 4517]: https://www.rfc-editor.org/rfc/rfc4517#section-4.2.1
*/
func bitStringMatch(a, b any) (result Boolean, err error) {
var abs, bbs BitString
if abs, err = marshalBitString(a); err != nil {
return
}
abytes := abs.Bytes
abits := abs.BitLength
if bbs, err = marshalBitString(b); err != nil {
return
}
bbytes := bbs.Bytes
bbits := bbs.BitLength
// TODO
//if namedBitList {
// // Remove trailing zero bits
// abits = stripTrailingZeros(abytes, abits)
// bbits = stripTrailingZeros(bbytes, bbits)
//}
// Check if both bit strings have the same number of bits
if abits != bbits {
result.Set(false)
return
}
// Compare bit strings bitwise
for i := 0; i < len(abytes); i++ {
if abytes[i] != bbytes[i] {
result.Set(false)
return
}
}
result.Set(true)
return
}
// stripTrailingZeros removes trailing zero bits and returns the new bit length
func stripTrailingZeros(bytes []byte, bitLength int) (blen int) {
blen = bitLength
for i := len(bytes) - 1; i >= 0; i-- {
for bit := 0; bit < 8; bit++ {
if (bytes[i] & (1 << bit)) != 0 {
return blen
}
blen--
}
}
return
}