crypto/x509: introduce new robust OID type & use it for certificate policies #60665
Description
X509 certificates use OIDs in the certificatePolicies extension to signal specific policies that apply to a certificate. These certificates may contain OIDs which have components that do not fit within an int32. Due to the design of asn1.ObjectIdentifier, these OIDs cannot be parsed, since we cap the size of a component to the max int32 in order to maintain consistent behavior across platforms.
asn1.ObjectIdentifier is simply a alias for []int, and as such it cannot be reasonable retrofitted to support these extremely (perhaps too) large components. Introducing a new type is not particularly complicated, the most straight forward approach is to simply store the DER representation in memory, rather than parsing out an integer representation, but the complexity comes with how common asn1.ObjectIdentifier usage is. Ideally we'd replace all usages with the new type, deprecating the old fields, but this would result in significant bloat all over the place.
Since (as far as I'm aware at the moment, see #58821, #30757, #38737, and #36467. #36467 relates to RDN components, but that is significantly more complicated) the only place we're currently seeing parsing errors because of giant OIDs is certificate policies, I propose that we implement this new type, and first use it to replace only the Certificate.PolicyIdentifiers field. If down the road we start encountering these large OIDs elsewhere we can take a piecemeal approach to replacing them (with the rule that any new usage of OIDs should also use the new type).
I'm open to disagreements about where the new type should be implemented. OIDs are used, a lot, for X509, but they are also all over the place in various protocols, so it might make sense to put it somewhere else. I think their original home in asn1 is slightly confusing, since it's not inherently an asn1 type, but 🤷 (as always its easier to argue where it shouldn't be, rather than where it should be).
type OID struct {
// unexported
der []byte
str string
}
func (oid *OID) Equal(other *OID) bool
func (oid *OID) String() string
// Simple way to switch between the old/new formats, and for parsing
// OIDs for testing, but perhaps not strictly necessary.
func (oid *OID) ToInts() ([]int, bool)
func FromInts([]int) *OID
type Certificate {
...
// DEPRECATED: PolicyIdentifiers contains asn1.ObjectIdentifiers, the components
// of which are limited to int32. If a certificate contains a policy which
// cannot be represented by asn1.ObjectIdentifier, it will not be included in
// PolicyIdentifiers, but will be present in Policies, which contains all parsed
// policy OIDs.
PolicyIdentifiers []asn1.ObjectIdentifier
// Policies contains policy OIDs included in any certificatePolicies extensions
// in the certificate.
Policies []*OID
}
cc @golang/security @FiloSottile