oid.mx raw
1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package x509
6
7 import (
8 "bytes"
9 "encoding/asn1"
10 "errors"
11 "math"
12 "math/big"
13 "math/bits"
14 "strconv"
15 )
16
17 var (
18 errInvalidOID = errors.New("invalid oid")
19 )
20
21 // An OID represents an ASN.1 OBJECT IDENTIFIER.
22 type OID struct {
23 der []byte
24 }
25
26 // ParseOID parses a Object Identifier string, represented by ASCII numbers separated by dots.
27 func ParseOID(oid string) (OID, error) {
28 var o OID
29 return o, o.unmarshalOIDText(oid)
30 }
31
32 func newOIDFromDER(der []byte) (OID, bool) {
33 if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
34 return OID{}, false
35 }
36
37 start := 0
38 for i, v := range der {
39 // ITU-T X.690, section 8.19.2:
40 // The subidentifier shall be encoded in the fewest possible octets,
41 // that is, the leading octet of the subidentifier shall not have the value 0x80.
42 if i == start && v == 0x80 {
43 return OID{}, false
44 }
45 if v&0x80 == 0 {
46 start = i + 1
47 }
48 }
49
50 return OID{der}, true
51 }
52
53 // OIDFromInts creates a new OID using ints, each integer is a separate component.
54 func OIDFromInts(oid []uint64) (OID, error) {
55 if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
56 return OID{}, errInvalidOID
57 }
58
59 length := base128IntLength(oid[0]*40 + oid[1])
60 for _, v := range oid[2:] {
61 length += base128IntLength(v)
62 }
63
64 der := []byte{:0:length}
65 der = appendBase128Int(der, oid[0]*40+oid[1])
66 for _, v := range oid[2:] {
67 der = appendBase128Int(der, v)
68 }
69 return OID{der}, nil
70 }
71
72 func base128IntLength(n uint64) int {
73 if n == 0 {
74 return 1
75 }
76 return (bits.Len64(n) + 6) / 7
77 }
78
79 func appendBase128Int(dst []byte, n uint64) []byte {
80 for i := base128IntLength(n) - 1; i >= 0; i-- {
81 o := byte(n >> uint(i*7))
82 o &= 0x7f
83 if i != 0 {
84 o |= 0x80
85 }
86 dst = append(dst, o)
87 }
88 return dst
89 }
90
91 func base128BigIntLength(n *big.Int) int {
92 if n.Cmp(big.NewInt(0)) == 0 {
93 return 1
94 }
95 return (n.BitLen() + 6) / 7
96 }
97
98 func appendBase128BigInt(dst []byte, n *big.Int) []byte {
99 if n.Cmp(big.NewInt(0)) == 0 {
100 return append(dst, 0)
101 }
102
103 for i := base128BigIntLength(n) - 1; i >= 0; i-- {
104 o := byte(big.NewInt(0).Rsh(n, uint(i)*7).Bits()[0])
105 o &= 0x7f
106 if i != 0 {
107 o |= 0x80
108 }
109 dst = append(dst, o)
110 }
111 return dst
112 }
113
114 // AppendText implements [encoding.TextAppender]
115 func (o OID) AppendText(b []byte) ([]byte, error) {
116 return append(b, o.String()...), nil
117 }
118
119 // MarshalText implements [encoding.TextMarshaler]
120 func (o OID) MarshalText() ([]byte, error) {
121 return o.AppendText(nil)
122 }
123
124 // UnmarshalText implements [encoding.TextUnmarshaler]
125 func (o *OID) UnmarshalText(text []byte) error {
126 return o.unmarshalOIDText(string(text))
127 }
128
129 func (o *OID) unmarshalOIDText(oid string) error {
130 // (*big.Int).SetString allows +/- signs, but we don't want
131 // to allow them in the string representation of Object Identifier, so
132 // reject such encodings.
133 for _, c := range oid {
134 isDigit := c >= '0' && c <= '9'
135 if !isDigit && c != '.' {
136 return errInvalidOID
137 }
138 }
139
140 var (
141 firstNum string
142 secondNum string
143 )
144
145 var nextComponentExists bool
146 firstNum, oid, nextComponentExists = bytes.Cut(oid, ".")
147 if !nextComponentExists {
148 return errInvalidOID
149 }
150 secondNum, oid, nextComponentExists = bytes.Cut(oid, ".")
151
152 var (
153 first = big.NewInt(0)
154 second = big.NewInt(0)
155 )
156
157 if _, ok := first.SetString(firstNum, 10); !ok {
158 return errInvalidOID
159 }
160 if _, ok := second.SetString(secondNum, 10); !ok {
161 return errInvalidOID
162 }
163
164 if first.Cmp(big.NewInt(2)) > 0 || (first.Cmp(big.NewInt(2)) < 0 && second.Cmp(big.NewInt(40)) >= 0) {
165 return errInvalidOID
166 }
167
168 firstComponent := first.Mul(first, big.NewInt(40))
169 firstComponent.Add(firstComponent, second)
170
171 der := appendBase128BigInt([]byte{:0:32}, firstComponent)
172
173 for nextComponentExists {
174 var strNum string
175 strNum, oid, nextComponentExists = bytes.Cut(oid, ".")
176 b, ok := big.NewInt(0).SetString(strNum, 10)
177 if !ok {
178 return errInvalidOID
179 }
180 der = appendBase128BigInt(der, b)
181 }
182
183 o.der = der
184 return nil
185 }
186
187 // AppendBinary implements [encoding.BinaryAppender]
188 func (o OID) AppendBinary(b []byte) ([]byte, error) {
189 return append(b, o.der...), nil
190 }
191
192 // MarshalBinary implements [encoding.BinaryMarshaler]
193 func (o OID) MarshalBinary() ([]byte, error) {
194 return o.AppendBinary(nil)
195 }
196
197 // UnmarshalBinary implements [encoding.BinaryUnmarshaler]
198 func (o *OID) UnmarshalBinary(b []byte) error {
199 oid, ok := newOIDFromDER(bytes.Clone(b))
200 if !ok {
201 return errInvalidOID
202 }
203 *o = oid
204 return nil
205 }
206
207 // Equal returns true when oid and other represents the same Object Identifier.
208 func (oid OID) Equal(other OID) bool {
209 // There is only one possible DER encoding of
210 // each unique Object Identifier.
211 return bytes.Equal(oid.der, other.der)
212 }
213
214 func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, failed bool) {
215 offset = initOffset
216 var ret64 int64
217 for shifted := 0; offset < len(bytes); shifted++ {
218 // 5 * 7 bits per byte == 35 bits of data
219 // Thus the representation is either non-minimal or too large for an int32
220 if shifted == 5 {
221 failed = true
222 return
223 }
224 ret64 <<= 7
225 b := bytes[offset]
226 // integers should be minimally encoded, so the leading octet should
227 // never be 0x80
228 if shifted == 0 && b == 0x80 {
229 failed = true
230 return
231 }
232 ret64 |= int64(b & 0x7f)
233 offset++
234 if b&0x80 == 0 {
235 ret = int(ret64)
236 // Ensure that the returned value fits in an int on all platforms
237 if ret64 > math.MaxInt32 {
238 failed = true
239 }
240 return
241 }
242 }
243 failed = true
244 return
245 }
246
247 // EqualASN1OID returns whether an OID equals an asn1.ObjectIdentifier. If
248 // asn1.ObjectIdentifier cannot represent the OID specified by oid, because
249 // a component of OID requires more than 31 bits, it returns false.
250 func (oid OID) EqualASN1OID(other asn1.ObjectIdentifier) bool {
251 if len(other) < 2 {
252 return false
253 }
254 v, offset, failed := parseBase128Int(oid.der, 0)
255 if failed {
256 // This should never happen, since we've already parsed the OID,
257 // but just in case.
258 return false
259 }
260 if v < 80 {
261 a, b := v/40, v%40
262 if other[0] != a || other[1] != b {
263 return false
264 }
265 } else {
266 a, b := 2, v-80
267 if other[0] != a || other[1] != b {
268 return false
269 }
270 }
271
272 i := 2
273 for ; offset < len(oid.der); i++ {
274 v, offset, failed = parseBase128Int(oid.der, offset)
275 if failed {
276 // Again, shouldn't happen, since we've already parsed
277 // the OID, but better safe than sorry.
278 return false
279 }
280 if i >= len(other) || v != other[i] {
281 return false
282 }
283 }
284
285 return i == len(other)
286 }
287
288 // Strings returns the string representation of the Object Identifier.
289 func (oid OID) String() string {
290 var b bytes.Buffer
291 b.Grow(32)
292 const (
293 valSize = 64 // size in bits of val.
294 bitsPerByte = 7
295 maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
296 )
297 var (
298 start = 0
299 val = uint64(0)
300 numBuf = []byte{:0:21}
301 bigVal *big.Int
302 overflow bool
303 )
304 for i, v := range oid.der {
305 curVal := v & 0x7F
306 valEnd := v&0x80 == 0
307 if valEnd {
308 if start != 0 {
309 b.WriteByte('.')
310 }
311 }
312 if !overflow && val > maxValSafeShift {
313 if bigVal == nil {
314 bigVal = &big.Int{}
315 }
316 bigVal = bigVal.SetUint64(val)
317 overflow = true
318 }
319 if overflow {
320 bigVal = bigVal.Lsh(bigVal, bitsPerByte).Or(bigVal, big.NewInt(int64(curVal)))
321 if valEnd {
322 if start == 0 {
323 b.WriteString("2.")
324 bigVal = bigVal.Sub(bigVal, big.NewInt(80))
325 }
326 numBuf = bigVal.Append(numBuf, 10)
327 b.Write(numBuf)
328 numBuf = numBuf[:0]
329 val = 0
330 start = i + 1
331 overflow = false
332 }
333 continue
334 }
335 val <<= bitsPerByte
336 val |= uint64(curVal)
337 if valEnd {
338 if start == 0 {
339 if val < 80 {
340 b.Write(strconv.AppendUint(numBuf, val/40, 10))
341 b.WriteByte('.')
342 b.Write(strconv.AppendUint(numBuf, val%40, 10))
343 } else {
344 b.WriteString("2.")
345 b.Write(strconv.AppendUint(numBuf, val-80, 10))
346 }
347 } else {
348 b.Write(strconv.AppendUint(numBuf, val, 10))
349 }
350 val = 0
351 start = i + 1
352 }
353 }
354 return b.String()
355 }
356
357 func (oid OID) toASN1OID() (asn1.ObjectIdentifier, bool) {
358 out := []int{:0:len(oid.der)+1}
359
360 const (
361 valSize = 31 // amount of usable bits of val for OIDs.
362 bitsPerByte = 7
363 maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
364 )
365
366 val := 0
367
368 for _, v := range oid.der {
369 if val > maxValSafeShift {
370 return nil, false
371 }
372
373 val <<= bitsPerByte
374 val |= int(v & 0x7F)
375
376 if v&0x80 == 0 {
377 if len(out) == 0 {
378 if val < 80 {
379 out = append(out, val/40)
380 out = append(out, val%40)
381 } else {
382 out = append(out, 2)
383 out = append(out, val-80)
384 }
385 val = 0
386 continue
387 }
388 out = append(out, val)
389 val = 0
390 }
391 }
392
393 return out, true
394 }
395