Skip to content

Commit 722d594

Browse files
mateusz834gopherbot
authored andcommitted
crypto/x509: add text and binary marshal methods to OID
Fixes #66249 Change-Id: I5973a19a087a35ad951e8a220d3e6e4456c7577f GitHub-Last-Rev: 921ca8b GitHub-Pull-Request: #66599 Reviewed-on: https://go-review.googlesource.com/c/go/+/575295 Reviewed-by: Rob Pike <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]>
1 parent 8ce2fed commit 722d594

File tree

6 files changed

+329
-66
lines changed

6 files changed

+329
-66
lines changed

api/next/66249.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pkg crypto/x509, func ParseOID(string) (OID, error) #66249
2+
pkg crypto/x509, method (*OID) UnmarshalBinary([]uint8) error #66249
3+
pkg crypto/x509, method (*OID) UnmarshalText([]uint8) error #66249
4+
pkg crypto/x509, method (OID) MarshalBinary() ([]uint8, error) #66249
5+
pkg crypto/x509, method (OID) MarshalText() ([]uint8, error) #66249
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The new [`ParseOID`](/pkg/crypto/x509#ParseOID) function parses a dot-encoded ASN.1 Object Identifier string.
2+
The [`OID`](/pkg/crypto/x509#OID) type now implements the [`BinaryMarshaler`](/pkg/encoding#BinaryMarshaler), [`BinaryUnmarshaler`](/pkg/encoding#BinaryUnmarshaler),
3+
[`TextMarshaler`](/pkg/encoding#TextMarshaler), [`TextUnmarshaler`](/pkg/encoding#TextUnmarshaler) interfaces.

src/crypto/x509/oid.go

+112
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ type OID struct {
2424
der []byte
2525
}
2626

27+
// ParseOID parses a Object Identifier string, represented by ASCII numbers separated by dots.
28+
func ParseOID(oid string) (OID, error) {
29+
var o OID
30+
return o, o.unmarshalOIDText(oid)
31+
}
32+
2733
func newOIDFromDER(der []byte) (OID, bool) {
2834
if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
2935
return OID{}, false
@@ -83,6 +89,112 @@ func appendBase128Int(dst []byte, n uint64) []byte {
8389
return dst
8490
}
8591

92+
func base128BigIntLength(n *big.Int) int {
93+
if n.Cmp(big.NewInt(0)) == 0 {
94+
return 1
95+
}
96+
return (n.BitLen() + 6) / 7
97+
}
98+
99+
func appendBase128BigInt(dst []byte, n *big.Int) []byte {
100+
if n.Cmp(big.NewInt(0)) == 0 {
101+
return append(dst, 0)
102+
}
103+
104+
for i := base128BigIntLength(n) - 1; i >= 0; i-- {
105+
o := byte(big.NewInt(0).Rsh(n, uint(i)*7).Bits()[0])
106+
o &= 0x7f
107+
if i != 0 {
108+
o |= 0x80
109+
}
110+
dst = append(dst, o)
111+
}
112+
return dst
113+
}
114+
115+
// MarshalText implements [encoding.TextMarshaler]
116+
func (o OID) MarshalText() ([]byte, error) {
117+
return []byte(o.String()), nil
118+
}
119+
120+
// UnmarshalText implements [encoding.TextUnmarshaler]
121+
func (o *OID) UnmarshalText(text []byte) error {
122+
return o.unmarshalOIDText(string(text))
123+
}
124+
125+
func (o *OID) unmarshalOIDText(oid string) error {
126+
// (*big.Int).SetString allows +/- signs, but we don't want
127+
// to allow them in the string representation of Object Identifier, so
128+
// reject such encodings.
129+
for _, c := range oid {
130+
isDigit := c >= '0' && c <= '9'
131+
if !isDigit && c != '.' {
132+
return errInvalidOID
133+
}
134+
}
135+
136+
var (
137+
firstNum string
138+
secondNum string
139+
)
140+
141+
var nextComponentExists bool
142+
firstNum, oid, nextComponentExists = strings.Cut(oid, ".")
143+
if !nextComponentExists {
144+
return errInvalidOID
145+
}
146+
secondNum, oid, nextComponentExists = strings.Cut(oid, ".")
147+
148+
var (
149+
first = big.NewInt(0)
150+
second = big.NewInt(0)
151+
)
152+
153+
if _, ok := first.SetString(firstNum, 10); !ok {
154+
return errInvalidOID
155+
}
156+
if _, ok := second.SetString(secondNum, 10); !ok {
157+
return errInvalidOID
158+
}
159+
160+
if first.Cmp(big.NewInt(2)) > 0 || (first.Cmp(big.NewInt(2)) < 0 && second.Cmp(big.NewInt(40)) >= 0) {
161+
return errInvalidOID
162+
}
163+
164+
firstComponent := first.Mul(first, big.NewInt(40))
165+
firstComponent.Add(firstComponent, second)
166+
167+
der := appendBase128BigInt(make([]byte, 0, 32), firstComponent)
168+
169+
for nextComponentExists {
170+
var strNum string
171+
strNum, oid, nextComponentExists = strings.Cut(oid, ".")
172+
b, ok := big.NewInt(0).SetString(strNum, 10)
173+
if !ok {
174+
return errInvalidOID
175+
}
176+
der = appendBase128BigInt(der, b)
177+
}
178+
179+
o.der = der
180+
return nil
181+
}
182+
183+
// MarshalBinary implements [encoding.BinaryMarshaler]
184+
func (o OID) MarshalBinary() ([]byte, error) {
185+
return bytes.Clone(o.der), nil
186+
}
187+
188+
// UnmarshalBinary implements [encoding.BinaryUnmarshaler]
189+
func (o *OID) UnmarshalBinary(b []byte) error {
190+
oid, ok := newOIDFromDER(bytes.Clone(b))
191+
if !ok {
192+
return errInvalidOID
193+
}
194+
*o = oid
195+
return nil
196+
}
197+
86198
// Equal returns true when oid and other represents the same Object Identifier.
87199
func (oid OID) Equal(other OID) bool {
88200
// There is only one possible DER encoding of

0 commit comments

Comments
 (0)