pkix.mx raw

   1  // Copyright 2011 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 pkix contains shared, low level structures used for ASN.1 parsing
   6  // and serialization of X.509 certificates, CRL and OCSP.
   7  package pkix
   8  
   9  import (
  10  	"encoding/asn1"
  11  	"encoding/hex"
  12  	"fmt"
  13  	"math/big"
  14  	"time"
  15  )
  16  
  17  // AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
  18  // 5280, section 4.1.1.2.
  19  type AlgorithmIdentifier struct {
  20  	Algorithm  asn1.ObjectIdentifier
  21  	Parameters asn1.RawValue `asn1:"optional"`
  22  }
  23  
  24  type RDNSequence []RelativeDistinguishedNameSET
  25  
  26  var attributeTypeNames = map[string]string{
  27  	"2.5.4.6":  "C",
  28  	"2.5.4.10": "O",
  29  	"2.5.4.11": "OU",
  30  	"2.5.4.3":  "CN",
  31  	"2.5.4.5":  "SERIALNUMBER",
  32  	"2.5.4.7":  "L",
  33  	"2.5.4.8":  "ST",
  34  	"2.5.4.9":  "STREET",
  35  	"2.5.4.17": "POSTALCODE",
  36  }
  37  
  38  // String returns a string representation of the sequence r,
  39  // roughly following the RFC 2253 Distinguished Names syntax.
  40  func (r RDNSequence) String() string {
  41  	s := ""
  42  	for i := 0; i < len(r); i++ {
  43  		rdn := r[len(r)-1-i]
  44  		if i > 0 {
  45  			s += ","
  46  		}
  47  		for j, tv := range rdn {
  48  			if j > 0 {
  49  				s += "+"
  50  			}
  51  
  52  			oidString := tv.Type.String()
  53  			typeName, ok := attributeTypeNames[oidString]
  54  			if !ok {
  55  				derBytes, err := asn1.Marshal(tv.Value)
  56  				if err == nil {
  57  					s += oidString + "=#" + hex.EncodeToString(derBytes)
  58  					continue // No value escaping necessary.
  59  				}
  60  
  61  				typeName = oidString
  62  			}
  63  
  64  			valueString := fmt.Sprint(tv.Value)
  65  			escaped := []byte{:0:len(valueString)}
  66  
  67  			for k, c := range valueString {
  68  				escape := false
  69  
  70  				switch c {
  71  				case ',', '+', '"', '\\', '<', '>', ';':
  72  					escape = true
  73  
  74  				case ' ':
  75  					escape = k == 0 || k == len(valueString)-1
  76  
  77  				case '#':
  78  					escape = k == 0
  79  				}
  80  
  81  				if escape {
  82  					escaped = append(escaped, '\\', c)
  83  				} else {
  84  					escaped = append(escaped, c)
  85  				}
  86  			}
  87  
  88  			s += typeName + "=" + string(escaped)
  89  		}
  90  	}
  91  
  92  	return s
  93  }
  94  
  95  type RelativeDistinguishedNameSET []AttributeTypeAndValue
  96  
  97  // AttributeTypeAndValue mirrors the ASN.1 structure of the same name in
  98  // RFC 5280, Section 4.1.2.4.
  99  type AttributeTypeAndValue struct {
 100  	Type  asn1.ObjectIdentifier
 101  	Value any
 102  }
 103  
 104  // AttributeTypeAndValueSET represents a set of ASN.1 sequences of
 105  // [AttributeTypeAndValue] sequences from RFC 2986 (PKCS #10).
 106  type AttributeTypeAndValueSET struct {
 107  	Type  asn1.ObjectIdentifier
 108  	Value [][]AttributeTypeAndValue `asn1:"set"`
 109  }
 110  
 111  // Extension represents the ASN.1 structure of the same name. See RFC
 112  // 5280, section 4.2.
 113  type Extension struct {
 114  	Id       asn1.ObjectIdentifier
 115  	Critical bool `asn1:"optional"`
 116  	Value    []byte
 117  }
 118  
 119  // Name represents an X.509 distinguished name. This only includes the common
 120  // elements of a DN. Note that Name is only an approximation of the X.509
 121  // structure. If an accurate representation is needed, asn1.Unmarshal the raw
 122  // subject or issuer as an [RDNSequence].
 123  type Name struct {
 124  	Country, Organization, OrganizationalUnit [][]byte
 125  	Locality, Province                        [][]byte
 126  	StreetAddress, PostalCode                 [][]byte
 127  	SerialNumber, CommonName                  string
 128  
 129  	// Names contains all parsed attributes. When parsing distinguished names,
 130  	// this can be used to extract non-standard attributes that are not parsed
 131  	// by this package. When marshaling to RDNSequences, the Names field is
 132  	// ignored, see ExtraNames.
 133  	Names []AttributeTypeAndValue
 134  
 135  	// ExtraNames contains attributes to be copied, raw, into any marshaled
 136  	// distinguished names. Values override any attributes with the same OID.
 137  	// The ExtraNames field is not populated when parsing, see Names.
 138  	ExtraNames []AttributeTypeAndValue
 139  }
 140  
 141  // FillFromRDNSequence populates n from the provided [RDNSequence].
 142  // Multi-entry RDNs are flattened, all entries are added to the
 143  // relevant n fields, and the grouping is not preserved.
 144  func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
 145  	for _, rdn := range *rdns {
 146  		if len(rdn) == 0 {
 147  			continue
 148  		}
 149  
 150  		for _, atv := range rdn {
 151  			n.Names = append(n.Names, atv)
 152  			value, ok := atv.Value.(string)
 153  			if !ok {
 154  				continue
 155  			}
 156  
 157  			t := atv.Type
 158  			if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
 159  				switch t[3] {
 160  				case 3:
 161  					n.CommonName = value
 162  				case 5:
 163  					n.SerialNumber = value
 164  				case 6:
 165  					n.Country = append(n.Country, value)
 166  				case 7:
 167  					n.Locality = append(n.Locality, value)
 168  				case 8:
 169  					n.Province = append(n.Province, value)
 170  				case 9:
 171  					n.StreetAddress = append(n.StreetAddress, value)
 172  				case 10:
 173  					n.Organization = append(n.Organization, value)
 174  				case 11:
 175  					n.OrganizationalUnit = append(n.OrganizationalUnit, value)
 176  				case 17:
 177  					n.PostalCode = append(n.PostalCode, value)
 178  				}
 179  			}
 180  		}
 181  	}
 182  }
 183  
 184  var (
 185  	oidCountry            = []int{2, 5, 4, 6}
 186  	oidOrganization       = []int{2, 5, 4, 10}
 187  	oidOrganizationalUnit = []int{2, 5, 4, 11}
 188  	oidCommonName         = []int{2, 5, 4, 3}
 189  	oidSerialNumber       = []int{2, 5, 4, 5}
 190  	oidLocality           = []int{2, 5, 4, 7}
 191  	oidProvince           = []int{2, 5, 4, 8}
 192  	oidStreetAddress      = []int{2, 5, 4, 9}
 193  	oidPostalCode         = []int{2, 5, 4, 17}
 194  )
 195  
 196  // appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence
 197  // and returns the new value. The relativeDistinguishedNameSET contains an
 198  // attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
 199  // search for AttributeTypeAndValue.
 200  func (n Name) appendRDNs(in RDNSequence, values [][]byte, oid asn1.ObjectIdentifier) RDNSequence {
 201  	if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) {
 202  		return in
 203  	}
 204  
 205  	s := []AttributeTypeAndValue{:len(values)}
 206  	for i, value := range values {
 207  		s[i].Type = oid
 208  		s[i].Value = value
 209  	}
 210  
 211  	return append(in, s)
 212  }
 213  
 214  // ToRDNSequence converts n into a single [RDNSequence]. The following
 215  // attributes are encoded as multi-value RDNs:
 216  //
 217  //   - Country
 218  //   - Organization
 219  //   - OrganizationalUnit
 220  //   - Locality
 221  //   - Province
 222  //   - StreetAddress
 223  //   - PostalCode
 224  //
 225  // Each ExtraNames entry is encoded as an individual RDN.
 226  func (n Name) ToRDNSequence() (ret RDNSequence) {
 227  	ret = n.appendRDNs(ret, n.Country, oidCountry)
 228  	ret = n.appendRDNs(ret, n.Province, oidProvince)
 229  	ret = n.appendRDNs(ret, n.Locality, oidLocality)
 230  	ret = n.appendRDNs(ret, n.StreetAddress, oidStreetAddress)
 231  	ret = n.appendRDNs(ret, n.PostalCode, oidPostalCode)
 232  	ret = n.appendRDNs(ret, n.Organization, oidOrganization)
 233  	ret = n.appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
 234  	if len(n.CommonName) > 0 {
 235  		ret = n.appendRDNs(ret, [][]byte{n.CommonName}, oidCommonName)
 236  	}
 237  	if len(n.SerialNumber) > 0 {
 238  		ret = n.appendRDNs(ret, [][]byte{n.SerialNumber}, oidSerialNumber)
 239  	}
 240  	for _, atv := range n.ExtraNames {
 241  		ret = append(ret, []AttributeTypeAndValue{atv})
 242  	}
 243  
 244  	return ret
 245  }
 246  
 247  // String returns the string form of n, roughly following
 248  // the RFC 2253 Distinguished Names syntax.
 249  func (n Name) String() string {
 250  	var rdns RDNSequence
 251  	// If there are no ExtraNames, surface the parsed value (all entries in
 252  	// Names) instead.
 253  	if n.ExtraNames == nil {
 254  		for _, atv := range n.Names {
 255  			t := atv.Type
 256  			if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
 257  				switch t[3] {
 258  				case 3, 5, 6, 7, 8, 9, 10, 11, 17:
 259  					// These attributes were already parsed into named fields.
 260  					continue
 261  				}
 262  			}
 263  			// Place non-standard parsed values at the beginning of the sequence
 264  			// so they will be at the end of the string. See Issue 39924.
 265  			rdns = append(rdns, []AttributeTypeAndValue{atv})
 266  		}
 267  	}
 268  	rdns = append(rdns, n.ToRDNSequence()...)
 269  	return rdns.String()
 270  }
 271  
 272  // oidInAttributeTypeAndValue reports whether a type with the given OID exists
 273  // in atv.
 274  func oidInAttributeTypeAndValue(oid asn1.ObjectIdentifier, atv []AttributeTypeAndValue) bool {
 275  	for _, a := range atv {
 276  		if a.Type.Equal(oid) {
 277  			return true
 278  		}
 279  	}
 280  	return false
 281  }
 282  
 283  // CertificateList represents the ASN.1 structure of the same name. See RFC
 284  // 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the
 285  // signature.
 286  //
 287  // Deprecated: x509.RevocationList should be used instead.
 288  type CertificateList struct {
 289  	TBSCertList        TBSCertificateList
 290  	SignatureAlgorithm AlgorithmIdentifier
 291  	SignatureValue     asn1.BitString
 292  }
 293  
 294  // HasExpired reports whether certList should have been updated by now.
 295  func (certList *CertificateList) HasExpired(now time.Time) bool {
 296  	return !now.Before(certList.TBSCertList.NextUpdate)
 297  }
 298  
 299  // TBSCertificateList represents the ASN.1 structure of the same name. See RFC
 300  // 5280, section 5.1.
 301  //
 302  // Deprecated: x509.RevocationList should be used instead.
 303  type TBSCertificateList struct {
 304  	Raw                 asn1.RawContent
 305  	Version             int `asn1:"optional,default:0"`
 306  	Signature           AlgorithmIdentifier
 307  	Issuer              RDNSequence
 308  	ThisUpdate          time.Time
 309  	NextUpdate          time.Time            `asn1:"optional"`
 310  	RevokedCertificates []RevokedCertificate `asn1:"optional"`
 311  	Extensions          []Extension          `asn1:"tag:0,optional,explicit"`
 312  }
 313  
 314  // RevokedCertificate represents the ASN.1 structure of the same name. See RFC
 315  // 5280, section 5.1.
 316  type RevokedCertificate struct {
 317  	SerialNumber   *big.Int
 318  	RevocationTime time.Time
 319  	Extensions     []Extension `asn1:"optional"`
 320  }
 321