mirror of
https://github.com/golang/go
synced 2024-11-26 06:38:00 -07:00
crypto/x509 add new OID type and use it in Certificate
This commit is contained in:
parent
68e52bc03c
commit
aaaf27a65e
6
api/next/60665.txt
Normal file
6
api/next/60665.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pkg crypto/x509, type Certificate struct, Policies []OID #60665
|
||||||
|
pkg crypto/x509, type OID struct #60665
|
||||||
|
pkg crypto/x509, method (OID) Equal(OID) bool #60665
|
||||||
|
pkg crypto/x509, method (OID) EqualASN1OID(asn1.ObjectIdentifier) bool #60665
|
||||||
|
pkg crypto/x509, method (OID) String() string #60665
|
||||||
|
pkg crypto/x509, func OIDFromInts([]uint64) (OID, error) #60665
|
253
src/crypto/x509/oid.go
Normal file
253
src/crypto/x509/oid.go
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidOID = errors.New("invalid oid")
|
||||||
|
)
|
||||||
|
|
||||||
|
// An OID represents an ASN.1 OBJECT IDENTIFIER.
|
||||||
|
type OID struct {
|
||||||
|
der []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOIDFromDER(der []byte) (OID, bool) {
|
||||||
|
if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
|
||||||
|
return OID{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
start := 0
|
||||||
|
for i, v := range der {
|
||||||
|
// ITU-T X.690, section 8.19.2:
|
||||||
|
// The subidentifier shall be encoded in the fewest possible octets,
|
||||||
|
// that is, the leading octet of the subidentifier shall not have the value 0x80.
|
||||||
|
if i == start && v == 0x80 {
|
||||||
|
return OID{}, false
|
||||||
|
}
|
||||||
|
if v&0x80 == 0 {
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OID{der}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDFromInts creates a new OID using ints, each integer is a separate component.
|
||||||
|
func OIDFromInts(oid []uint64) (OID, error) {
|
||||||
|
if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
|
||||||
|
return OID{}, errInvalidOID
|
||||||
|
}
|
||||||
|
|
||||||
|
length := base128IntLength(oid[0]*40 + oid[1])
|
||||||
|
for _, v := range oid[2:] {
|
||||||
|
length += base128IntLength(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
der := make([]byte, 0, length)
|
||||||
|
der = appendBase128Int(der, oid[0]*40+oid[1])
|
||||||
|
for _, v := range oid[2:] {
|
||||||
|
der = appendBase128Int(der, v)
|
||||||
|
}
|
||||||
|
return OID{der}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func base128IntLength(n uint64) int {
|
||||||
|
if n == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
l := 0
|
||||||
|
for i := n; i > 0; i >>= 7 {
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendBase128Int(dst []byte, n uint64) []byte {
|
||||||
|
for i := base128IntLength(n) - 1; i >= 0; i-- {
|
||||||
|
o := byte(n >> uint(i*7))
|
||||||
|
o &= 0x7f
|
||||||
|
if i != 0 {
|
||||||
|
o |= 0x80
|
||||||
|
}
|
||||||
|
dst = append(dst, o)
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true when oid and other represents the same Object Identifier.
|
||||||
|
func (oid OID) Equal(other OID) bool {
|
||||||
|
// There is only one possible DER encoding of
|
||||||
|
// each unique Object Identifier.
|
||||||
|
return bytes.Equal(oid.der, other.der)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualASN1OID returns whether an OID equals an asn1.ObjectIdentifier. If
|
||||||
|
// asn1.ObjectIdentifier cannot represent the OID specified by oid, because
|
||||||
|
// a component of OID requires more than 31 bits, it returns false.
|
||||||
|
func (oid OID) EqualASN1OID(other asn1.ObjectIdentifier) bool {
|
||||||
|
const (
|
||||||
|
valSize = 31 // amount of usable bits of val for OIDs.
|
||||||
|
bitsPerByte = 7
|
||||||
|
maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
|
||||||
|
)
|
||||||
|
var (
|
||||||
|
val = 0
|
||||||
|
first = true
|
||||||
|
)
|
||||||
|
for _, v := range oid.der {
|
||||||
|
if val > maxValSafeShift {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val <<= bitsPerByte
|
||||||
|
val |= int(v & 0x7F)
|
||||||
|
if v&0x80 == 0 {
|
||||||
|
if first {
|
||||||
|
if len(other) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var val1, val2 int
|
||||||
|
if val < 80 {
|
||||||
|
val1 = val / 40
|
||||||
|
val2 = val % 40
|
||||||
|
} else {
|
||||||
|
val1 = 2
|
||||||
|
val2 = val - 80
|
||||||
|
}
|
||||||
|
if val1 != other[0] || val2 != other[1] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val = 0
|
||||||
|
first = false
|
||||||
|
other = other[2:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(other) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if val != other[0] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val = 0
|
||||||
|
other = other[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings returns the string representation of the Object Identifier.
|
||||||
|
func (oid OID) String() string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.Grow(32)
|
||||||
|
const (
|
||||||
|
valSize = 64 // size in bits of val.
|
||||||
|
bitsPerByte = 7
|
||||||
|
maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
|
||||||
|
)
|
||||||
|
var (
|
||||||
|
start = 0
|
||||||
|
val = uint64(0)
|
||||||
|
numBuf = make([]byte, 0, 21)
|
||||||
|
bigVal *big.Int
|
||||||
|
overflow bool
|
||||||
|
)
|
||||||
|
for i, v := range oid.der {
|
||||||
|
curVal := v & 0x7F
|
||||||
|
valEnd := v&0x80 == 0
|
||||||
|
if valEnd {
|
||||||
|
if start != 0 {
|
||||||
|
b.WriteByte('.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !overflow && val > maxValSafeShift {
|
||||||
|
if bigVal == nil {
|
||||||
|
bigVal = new(big.Int)
|
||||||
|
}
|
||||||
|
bigVal = bigVal.SetUint64(val)
|
||||||
|
overflow = true
|
||||||
|
}
|
||||||
|
if overflow {
|
||||||
|
bigVal = bigVal.Lsh(bigVal, bitsPerByte).Or(bigVal, big.NewInt(int64(curVal)))
|
||||||
|
if valEnd {
|
||||||
|
if start == 0 {
|
||||||
|
b.WriteString("2.")
|
||||||
|
bigVal = bigVal.Sub(bigVal, big.NewInt(80))
|
||||||
|
}
|
||||||
|
numBuf = bigVal.Append(numBuf, 10)
|
||||||
|
b.Write(numBuf)
|
||||||
|
numBuf = numBuf[:0]
|
||||||
|
val = 0
|
||||||
|
start = i + 1
|
||||||
|
overflow = false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val <<= bitsPerByte
|
||||||
|
val |= uint64(curVal)
|
||||||
|
if valEnd {
|
||||||
|
if start == 0 {
|
||||||
|
var val1, val2 uint64
|
||||||
|
if val < 80 {
|
||||||
|
val1 = val / 40
|
||||||
|
val2 = val % 40
|
||||||
|
} else {
|
||||||
|
val1 = 2
|
||||||
|
val2 = val - 80
|
||||||
|
}
|
||||||
|
b.Write(strconv.AppendUint(numBuf, val1, 10))
|
||||||
|
b.WriteByte('.')
|
||||||
|
b.Write(strconv.AppendUint(numBuf, val2, 10))
|
||||||
|
} else {
|
||||||
|
b.Write(strconv.AppendUint(numBuf, val, 10))
|
||||||
|
}
|
||||||
|
val = 0
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oid OID) toASN1OID() (asn1.ObjectIdentifier, bool) {
|
||||||
|
out := make([]int, 0, len(oid.der)+1)
|
||||||
|
|
||||||
|
const (
|
||||||
|
valSize = 31 // amount of usable bits of val for OIDs.
|
||||||
|
bitsPerByte = 7
|
||||||
|
maxValSafeShift = (1 << (valSize - bitsPerByte)) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
val := 0
|
||||||
|
|
||||||
|
for _, v := range oid.der {
|
||||||
|
if val > maxValSafeShift {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
val <<= bitsPerByte
|
||||||
|
val |= int(v & 0x7F)
|
||||||
|
|
||||||
|
if v&0x80 == 0 {
|
||||||
|
if len(out) == 0 {
|
||||||
|
if val < 80 {
|
||||||
|
out = append(out, val/40)
|
||||||
|
out = append(out, val%40)
|
||||||
|
} else {
|
||||||
|
out = append(out, 2)
|
||||||
|
out = append(out, val-80)
|
||||||
|
}
|
||||||
|
val = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, val)
|
||||||
|
val = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, true
|
||||||
|
}
|
98
src/crypto/x509/oid_test.go
Normal file
98
src/crypto/x509/oid_test.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package x509
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/asn1"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOID(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
raw []byte
|
||||||
|
valid bool
|
||||||
|
str string
|
||||||
|
ints []uint64
|
||||||
|
}{
|
||||||
|
{[]byte{}, false, "", nil},
|
||||||
|
{[]byte{0x80, 0x01}, false, "", nil},
|
||||||
|
{[]byte{0x01, 0x80, 0x01}, false, "", nil},
|
||||||
|
|
||||||
|
{[]byte{1, 2, 3}, true, "0.1.2.3", []uint64{0, 1, 2, 3}},
|
||||||
|
{[]byte{41, 2, 3}, true, "1.1.2.3", []uint64{1, 1, 2, 3}},
|
||||||
|
{[]byte{86, 2, 3}, true, "2.6.2.3", []uint64{2, 6, 2, 3}},
|
||||||
|
|
||||||
|
{[]byte{41, 255, 255, 255, 127}, true, "1.1.268435455", []uint64{1, 1, 268435455}},
|
||||||
|
{[]byte{41, 0x87, 255, 255, 255, 127}, true, "1.1.2147483647", []uint64{1, 1, 2147483647}},
|
||||||
|
{[]byte{41, 255, 255, 255, 255, 127}, true, "1.1.34359738367", []uint64{1, 1, 34359738367}},
|
||||||
|
{[]byte{42, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.2.9223372036854775807", []uint64{1, 2, 9223372036854775807}},
|
||||||
|
{[]byte{43, 0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.3.18446744073709551615", []uint64{1, 3, 18446744073709551615}},
|
||||||
|
{[]byte{44, 0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "1.4.36893488147419103231", nil},
|
||||||
|
{[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.1180591620717411303423", nil},
|
||||||
|
{[]byte{85, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.5.19342813113834066795298815", nil},
|
||||||
|
|
||||||
|
{[]byte{255, 255, 255, 127}, true, "2.268435375", []uint64{2, 268435375}},
|
||||||
|
{[]byte{0x87, 255, 255, 255, 127}, true, "2.2147483567", []uint64{2, 2147483567}},
|
||||||
|
{[]byte{255, 127}, true, "2.16303", []uint64{2, 16303}},
|
||||||
|
{[]byte{255, 255, 255, 255, 127}, true, "2.34359738287", []uint64{2, 34359738287}},
|
||||||
|
{[]byte{255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.9223372036854775727", []uint64{2, 9223372036854775727}},
|
||||||
|
{[]byte{0x81, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.18446744073709551535", []uint64{2, 18446744073709551535}},
|
||||||
|
{[]byte{0x83, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.36893488147419103151", nil},
|
||||||
|
{[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.1180591620717411303343", nil},
|
||||||
|
{[]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127}, true, "2.19342813113834066795298735", nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range tests {
|
||||||
|
oid, ok := newOIDFromDER(v.raw)
|
||||||
|
if ok != v.valid {
|
||||||
|
if ok {
|
||||||
|
t.Errorf("%v: unexpected success while parsing: %v", v.raw, oid)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%v: unexpected failure while parsing", v.raw)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if str := oid.String(); str != v.str {
|
||||||
|
t.Errorf("%v: oid.String() = %v, want; %v", v.raw, str, v.str)
|
||||||
|
}
|
||||||
|
|
||||||
|
var asn1OID asn1.ObjectIdentifier
|
||||||
|
for _, v := range v.ints {
|
||||||
|
if v > math.MaxInt32 {
|
||||||
|
asn1OID = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
asn1OID = append(asn1OID, int(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
o, ok := oid.toASN1OID()
|
||||||
|
if shouldOk := asn1OID != nil; shouldOk != ok {
|
||||||
|
if ok {
|
||||||
|
t.Errorf("%v: oid.toASN1OID() unexpected success", v.raw)
|
||||||
|
} else {
|
||||||
|
t.Errorf("%v: oid.toASN1OID() unexpected fauilure", v.raw)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if asn1OID != nil {
|
||||||
|
if !o.Equal(asn1OID) {
|
||||||
|
t.Errorf("%v: oid.toASN1OID(asn1OID).Equal(oid) = false, want: true", v.raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.ints != nil {
|
||||||
|
oid2, err := OIDFromInts(v.ints)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: OIDFromInts() unexpected error: %v", v.raw, err)
|
||||||
|
}
|
||||||
|
if !oid2.Equal(oid) {
|
||||||
|
t.Errorf("%v: %#v.Equal(%#v) = false, want: true", v.raw, oid2, oid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -435,23 +435,23 @@ func parseExtKeyUsageExtension(der cryptobyte.String) ([]ExtKeyUsage, []asn1.Obj
|
|||||||
return extKeyUsages, unknownUsages, nil
|
return extKeyUsages, unknownUsages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCertificatePoliciesExtension(der cryptobyte.String) ([]asn1.ObjectIdentifier, error) {
|
func parseCertificatePoliciesExtension(der cryptobyte.String) ([]OID, error) {
|
||||||
var oids []asn1.ObjectIdentifier
|
var oids []OID
|
||||||
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
|
if !der.ReadASN1(&der, cryptobyte_asn1.SEQUENCE) {
|
||||||
return nil, errors.New("x509: invalid certificate policies")
|
return nil, errors.New("x509: invalid certificate policies")
|
||||||
}
|
}
|
||||||
for !der.Empty() {
|
for !der.Empty() {
|
||||||
var cp cryptobyte.String
|
var cp cryptobyte.String
|
||||||
if !der.ReadASN1(&cp, cryptobyte_asn1.SEQUENCE) {
|
var OIDBytes cryptobyte.String
|
||||||
|
if !der.ReadASN1(&cp, cryptobyte_asn1.SEQUENCE) || !cp.ReadASN1(&OIDBytes, cryptobyte_asn1.OBJECT_IDENTIFIER) {
|
||||||
return nil, errors.New("x509: invalid certificate policies")
|
return nil, errors.New("x509: invalid certificate policies")
|
||||||
}
|
}
|
||||||
var oid asn1.ObjectIdentifier
|
oid, ok := newOIDFromDER(OIDBytes)
|
||||||
if !cp.ReadASN1ObjectIdentifier(&oid) {
|
if !ok {
|
||||||
return nil, errors.New("x509: invalid certificate policies")
|
return nil, errors.New("x509: invalid certificate policies")
|
||||||
}
|
}
|
||||||
oids = append(oids, oid)
|
oids = append(oids, oid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return oids, nil
|
return oids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,10 +748,16 @@ func processExtensions(out *Certificate) error {
|
|||||||
}
|
}
|
||||||
out.SubjectKeyId = skid
|
out.SubjectKeyId = skid
|
||||||
case 32:
|
case 32:
|
||||||
out.PolicyIdentifiers, err = parseCertificatePoliciesExtension(e.Value)
|
out.Policies, err = parseCertificatePoliciesExtension(e.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
out.PolicyIdentifiers = make([]asn1.ObjectIdentifier, 0, len(out.Policies))
|
||||||
|
for _, oid := range out.Policies {
|
||||||
|
if oid, ok := oid.toASN1OID(); ok {
|
||||||
|
out.PolicyIdentifiers = append(out.PolicyIdentifiers, oid)
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// Unknown extensions are recorded if critical.
|
// Unknown extensions are recorded if critical.
|
||||||
unhandled = true
|
unhandled = true
|
||||||
|
@ -773,6 +773,10 @@ type Certificate struct {
|
|||||||
CRLDistributionPoints []string
|
CRLDistributionPoints []string
|
||||||
|
|
||||||
PolicyIdentifiers []asn1.ObjectIdentifier
|
PolicyIdentifiers []asn1.ObjectIdentifier
|
||||||
|
|
||||||
|
// Policies contains policy OIDs included in any certificatePolicies extensions
|
||||||
|
// in the certificate.
|
||||||
|
Policies []OID
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUnsupportedAlgorithm results from attempting to perform an operation that
|
// ErrUnsupportedAlgorithm results from attempting to perform an operation that
|
||||||
|
Loading…
Reference in New Issue
Block a user