github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/x509/crl_parser.go (about)

     1  package x509
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"math/big"
     8  	"time"
     9  	"unicode/utf16"
    10  	"unicode/utf8"
    11  
    12  	"github.com/zmap/zcrypto/cryptobyte"
    13  	cryptobyte_asn1 "github.com/zmap/zcrypto/cryptobyte/asn1"
    14  	"github.com/zmap/zcrypto/encoding/asn1"
    15  	"github.com/zmap/zcrypto/x509/pkix"
    16  )
    17  
    18  const x509v2Version = 1
    19  
    20  // RevokedCertificate represents an entry in the revokedCertificates sequence of
    21  // a CRL.
    22  // STARTBLOCK: This type does not exist in upstream.
    23  type RevokedCertificate struct {
    24  	// Raw contains the raw bytes of the revokedCertificates entry. It is set when
    25  	// parsing a CRL; it is ignored when generating a CRL.
    26  	Raw []byte
    27  
    28  	// SerialNumber represents the serial number of a revoked certificate. It is
    29  	// both used when creating a CRL and populated when parsing a CRL. It MUST NOT
    30  	// be nil.
    31  	SerialNumber *big.Int
    32  	// RevocationTime represents the time at which the certificate was revoked. It
    33  	// is both used when creating a CRL and populated when parsing a CRL. It MUST
    34  	// NOT be nil.
    35  	RevocationTime time.Time
    36  	// ReasonCode represents the reason for revocation, using the integer enum
    37  	// values specified in RFC 5280 Section 5.3.1. When creating a CRL, a value of
    38  	// nil or zero will result in the reasonCode extension being omitted. When
    39  	// parsing a CRL, a value of nil represents a no reasonCode extension, while a
    40  	// value of 0 represents a reasonCode extension containing enum value 0 (this
    41  	// SHOULD NOT happen, but can and does).
    42  	ReasonCode *int
    43  
    44  	// Extensions contains raw X.509 extensions. When creating a CRL, the
    45  	// Extensions field is ignored, see ExtraExtensions.
    46  	Extensions []pkix.Extension
    47  	// ExtraExtensions contains any additional extensions to add directly to the
    48  	// revokedCertificate entry. It is up to the caller to ensure that this field
    49  	// does not contain any extensions which duplicate extensions created by this
    50  	// package (currently, the reasonCode extension). The ExtraExtensions field is
    51  	// not populated when parsing a CRL, see Extensions.
    52  	ExtraExtensions []pkix.Extension
    53  }
    54  
    55  // ENDBLOCK
    56  
    57  // ParseRevocationList parses a X509 v2 Certificate Revocation List from the given
    58  // ASN.1 DER data.
    59  func ParseRevocationList(der []byte) (*RevocationList, error) {
    60  	rl := &RevocationList{}
    61  
    62  	input := cryptobyte.String(der)
    63  	// we read the SEQUENCE including length and tag bytes so that
    64  	// we can populate RevocationList.Raw, before unwrapping the
    65  	// SEQUENCE so it can be operated on
    66  	if !input.ReadASN1Element(&input, cryptobyte_asn1.SEQUENCE) {
    67  		return nil, errors.New("x509: malformed crl")
    68  	}
    69  	rl.Raw = input
    70  	if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
    71  		return nil, errors.New("x509: malformed crl")
    72  	}
    73  
    74  	var tbs cryptobyte.String
    75  	// do the same trick again as above to extract the raw
    76  	// bytes for Certificate.RawTBSCertificate
    77  	if !input.ReadASN1Element(&tbs, cryptobyte_asn1.SEQUENCE) {
    78  		return nil, errors.New("x509: malformed tbs crl")
    79  	}
    80  	rl.RawTBSRevocationList = tbs
    81  	if !tbs.ReadASN1(&tbs, cryptobyte_asn1.SEQUENCE) {
    82  		return nil, errors.New("x509: malformed tbs crl")
    83  	}
    84  
    85  	var version int
    86  	if !tbs.PeekASN1Tag(cryptobyte_asn1.INTEGER) {
    87  		return nil, errors.New("x509: unsupported crl version")
    88  	}
    89  	if !tbs.ReadASN1Integer(&version) {
    90  		return nil, errors.New("x509: malformed crl")
    91  	}
    92  	if version != x509v2Version {
    93  		return nil, fmt.Errorf("x509: unsupported crl version: %d", version)
    94  	}
    95  
    96  	var sigAISeq cryptobyte.String
    97  	if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
    98  		return nil, errors.New("x509: malformed signature algorithm identifier")
    99  	}
   100  	// Before parsing the inner algorithm identifier, extract
   101  	// the outer algorithm identifier and make sure that they
   102  	// match.
   103  	var outerSigAISeq cryptobyte.String
   104  	if !input.ReadASN1(&outerSigAISeq, cryptobyte_asn1.SEQUENCE) {
   105  		return nil, errors.New("x509: malformed algorithm identifier")
   106  	}
   107  	if !bytes.Equal(outerSigAISeq, sigAISeq) {
   108  		return nil, errors.New("x509: inner and outer signature algorithm identifiers don't match")
   109  	}
   110  	sigAI, err := parseAI(sigAISeq)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	rl.SignatureAlgorithm = getSignatureAlgorithmFromAI(sigAI)
   115  
   116  	var signature asn1.BitString
   117  	if !input.ReadASN1BitString(&signature) {
   118  		return nil, errors.New("x509: malformed signature")
   119  	}
   120  	rl.Signature = signature.RightAlign()
   121  
   122  	var issuerSeq cryptobyte.String
   123  	if !tbs.ReadASN1Element(&issuerSeq, cryptobyte_asn1.SEQUENCE) {
   124  		return nil, errors.New("x509: malformed issuer")
   125  	}
   126  	rl.RawIssuer = issuerSeq
   127  	issuerRDNs, err := parseName(issuerSeq)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	rl.Issuer.FillFromRDNSequence(issuerRDNs)
   132  
   133  	rl.ThisUpdate, err = parseTime(&tbs)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	if tbs.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime) || tbs.PeekASN1Tag(cryptobyte_asn1.UTCTime) {
   138  		rl.NextUpdate, err = parseTime(&tbs)
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  	}
   143  
   144  	if tbs.PeekASN1Tag(cryptobyte_asn1.SEQUENCE) {
   145  		// NOTE: The block does not exist in upstream.
   146  		rcs := make([]RevokedCertificate, 0)
   147  		// ENDBLOCK
   148  		var revokedSeq cryptobyte.String
   149  		if !tbs.ReadASN1(&revokedSeq, cryptobyte_asn1.SEQUENCE) {
   150  			return nil, errors.New("x509: malformed crl")
   151  		}
   152  		for !revokedSeq.Empty() {
   153  			var certSeq cryptobyte.String
   154  			// NOTE: The block is different from upstream. Upstream: ReadASN1
   155  			if !revokedSeq.ReadASN1Element(&certSeq, cryptobyte_asn1.SEQUENCE) {
   156  				// ENDBLOCK
   157  				return nil, errors.New("x509: malformed crl")
   158  			}
   159  			rc := RevokedCertificate{Raw: certSeq}
   160  			if !certSeq.ReadASN1(&certSeq, cryptobyte_asn1.SEQUENCE) {
   161  				return nil, errors.New("x509: malformed crl")
   162  			}
   163  			rc.SerialNumber = new(big.Int)
   164  			if !certSeq.ReadASN1Integer(rc.SerialNumber) {
   165  				return nil, errors.New("x509: malformed serial number")
   166  			}
   167  			rc.RevocationTime, err = parseTime(&certSeq)
   168  			if err != nil {
   169  				return nil, err
   170  			}
   171  			var extensions cryptobyte.String
   172  			var present bool
   173  			if !certSeq.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.SEQUENCE) {
   174  				return nil, errors.New("x509: malformed extensions")
   175  			}
   176  			if present {
   177  				for !extensions.Empty() {
   178  					var extension cryptobyte.String
   179  					if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) {
   180  						return nil, errors.New("x509: malformed extension")
   181  					}
   182  					ext, err := parseExtension(extension)
   183  					if err != nil {
   184  						return nil, err
   185  					}
   186  					// STARTBLOCK: This block does not exist in upstream.
   187  					if ext.Id.Equal(oidExtensionReasonCode) {
   188  						val := cryptobyte.String(ext.Value)
   189  						rc.ReasonCode = new(int)
   190  						if !val.ReadASN1Enum(rc.ReasonCode) {
   191  							return nil, fmt.Errorf("x509: malformed reasonCode extension")
   192  						}
   193  					}
   194  					// ENDBLOCK
   195  					rc.Extensions = append(rc.Extensions, ext)
   196  				}
   197  			}
   198  			// STARTBLOCK: The block does not exist in upstream.
   199  			rcs = append(rcs, rc)
   200  			// ENDBLOCK
   201  		}
   202  		rl.RevokedCertificates = rcs
   203  	}
   204  
   205  	var extensions cryptobyte.String
   206  	var present bool
   207  	if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) {
   208  		return nil, errors.New("x509: malformed extensions")
   209  	}
   210  	if present {
   211  		if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) {
   212  			return nil, errors.New("x509: malformed extensions")
   213  		}
   214  		for !extensions.Empty() {
   215  			var extension cryptobyte.String
   216  			if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) {
   217  				return nil, errors.New("x509: malformed extension")
   218  			}
   219  			ext, err := parseExtension(extension)
   220  			if err != nil {
   221  				return nil, err
   222  			}
   223  			if ext.Id.Equal(oidExtensionAuthorityKeyId) {
   224  				rl.AuthorityKeyId = ext.Value
   225  			} else if ext.Id.Equal(oidExtensionCRLNumber) {
   226  				value := cryptobyte.String(ext.Value)
   227  				rl.Number = new(big.Int)
   228  				if !value.ReadASN1Integer(rl.Number) {
   229  					return nil, errors.New("x509: malformed crl number")
   230  				}
   231  			}
   232  			rl.Extensions = append(rl.Extensions, ext)
   233  		}
   234  	}
   235  
   236  	return rl, nil
   237  }
   238  
   239  // isPrintable reports whether the given b is in the ASN.1 PrintableString set.
   240  // This is a simplified version of encoding/asn1.isPrintable.
   241  func isPrintable(b byte) bool {
   242  	return 'a' <= b && b <= 'z' ||
   243  		'A' <= b && b <= 'Z' ||
   244  		'0' <= b && b <= '9' ||
   245  		'\'' <= b && b <= ')' ||
   246  		'+' <= b && b <= '/' ||
   247  		b == ' ' ||
   248  		b == ':' ||
   249  		b == '=' ||
   250  		b == '?' ||
   251  		// This is technically not allowed in a PrintableString.
   252  		// However, x509 certificates with wildcard strings don't
   253  		// always use the correct string type so we permit it.
   254  		b == '*' ||
   255  		// This is not technically allowed either. However, not
   256  		// only is it relatively common, but there are also a
   257  		// handful of CA certificates that contain it. At least
   258  		// one of which will not expire until 2027.
   259  		b == '&'
   260  }
   261  
   262  // parseASN1String parses the ASN.1 string types T61String, PrintableString,
   263  // UTF8String, BMPString, IA5String, and NumericString. This is mostly copied
   264  // from the respective encoding/asn1.parse... methods, rather than just
   265  // increasing the API surface of that package.
   266  func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) {
   267  	switch tag {
   268  	case cryptobyte_asn1.T61String:
   269  		return string(value), nil
   270  	case cryptobyte_asn1.PrintableString:
   271  		for _, b := range value {
   272  			if !isPrintable(b) {
   273  				return "", errors.New("invalid PrintableString")
   274  			}
   275  		}
   276  		return string(value), nil
   277  	case cryptobyte_asn1.UTF8String:
   278  		if !utf8.Valid(value) {
   279  			return "", errors.New("invalid UTF-8 string")
   280  		}
   281  		return string(value), nil
   282  	case cryptobyte_asn1.Tag(asn1.TagBMPString):
   283  		if len(value)%2 != 0 {
   284  			return "", errors.New("invalid BMPString")
   285  		}
   286  
   287  		// Strip terminator if present.
   288  		if l := len(value); l >= 2 && value[l-1] == 0 && value[l-2] == 0 {
   289  			value = value[:l-2]
   290  		}
   291  
   292  		s := make([]uint16, 0, len(value)/2)
   293  		for len(value) > 0 {
   294  			s = append(s, uint16(value[0])<<8+uint16(value[1]))
   295  			value = value[2:]
   296  		}
   297  
   298  		return string(utf16.Decode(s)), nil
   299  	case cryptobyte_asn1.IA5String:
   300  		s := string(value)
   301  		if isIA5String(s) != nil {
   302  			return "", errors.New("invalid IA5String")
   303  		}
   304  		return s, nil
   305  	case cryptobyte_asn1.Tag(asn1.TagNumericString):
   306  		for _, b := range value {
   307  			if !('0' <= b && b <= '9' || b == ' ') {
   308  				return "", errors.New("invalid NumericString")
   309  			}
   310  		}
   311  		return string(value), nil
   312  	}
   313  	return "", fmt.Errorf("unsupported string type: %v", tag)
   314  }
   315  
   316  // parseName parses a DER encoded Name as defined in RFC 5280. We may
   317  // want to export this function in the future for use in crypto/tls.
   318  func parseName(raw cryptobyte.String) (*pkix.RDNSequence, error) {
   319  	if !raw.ReadASN1(&raw, cryptobyte_asn1.SEQUENCE) {
   320  		return nil, errors.New("x509: invalid RDNSequence")
   321  	}
   322  
   323  	var rdnSeq pkix.RDNSequence
   324  	for !raw.Empty() {
   325  		var rdnSet pkix.RelativeDistinguishedNameSET
   326  		var set cryptobyte.String
   327  		if !raw.ReadASN1(&set, cryptobyte_asn1.SET) {
   328  			return nil, errors.New("x509: invalid RDNSequence")
   329  		}
   330  		for !set.Empty() {
   331  			var atav cryptobyte.String
   332  			if !set.ReadASN1(&atav, cryptobyte_asn1.SEQUENCE) {
   333  				return nil, errors.New("x509: invalid RDNSequence: invalid attribute")
   334  			}
   335  			var attr pkix.AttributeTypeAndValue
   336  			if !atav.ReadASN1ObjectIdentifier(&attr.Type) {
   337  				return nil, errors.New("x509: invalid RDNSequence: invalid attribute type")
   338  			}
   339  			var rawValue cryptobyte.String
   340  			var valueTag cryptobyte_asn1.Tag
   341  			if !atav.ReadAnyASN1(&rawValue, &valueTag) {
   342  				return nil, errors.New("x509: invalid RDNSequence: invalid attribute value")
   343  			}
   344  			var err error
   345  			attr.Value, err = parseASN1String(valueTag, rawValue)
   346  			if err != nil {
   347  				return nil, fmt.Errorf("x509: invalid RDNSequence: invalid attribute value: %s", err)
   348  			}
   349  			rdnSet = append(rdnSet, attr)
   350  		}
   351  
   352  		rdnSeq = append(rdnSeq, rdnSet)
   353  	}
   354  
   355  	return &rdnSeq, nil
   356  }
   357  
   358  func parseAI(der cryptobyte.String) (pkix.AlgorithmIdentifier, error) {
   359  	ai := pkix.AlgorithmIdentifier{}
   360  	if !der.ReadASN1ObjectIdentifier(&ai.Algorithm) {
   361  		return ai, errors.New("x509: malformed OID")
   362  	}
   363  	if der.Empty() {
   364  		return ai, nil
   365  	}
   366  	var params cryptobyte.String
   367  	var tag cryptobyte_asn1.Tag
   368  	if !der.ReadAnyASN1Element(&params, &tag) {
   369  		return ai, errors.New("x509: malformed parameters")
   370  	}
   371  	ai.Parameters.Tag = int(tag)
   372  	ai.Parameters.FullBytes = params
   373  	return ai, nil
   374  }
   375  
   376  func parseTime(der *cryptobyte.String) (time.Time, error) {
   377  	var t time.Time
   378  	switch {
   379  	case der.PeekASN1Tag(cryptobyte_asn1.UTCTime):
   380  		if !der.ReadASN1UTCTime(&t) {
   381  			return t, errors.New("x509: malformed UTCTime")
   382  		}
   383  	case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime):
   384  		if !der.ReadASN1GeneralizedTime(&t) {
   385  			return t, errors.New("x509: malformed GeneralizedTime")
   386  		}
   387  	default:
   388  		return t, errors.New("x509: unsupported time format")
   389  	}
   390  	return t, nil
   391  }
   392  
   393  func parseExtension(der cryptobyte.String) (pkix.Extension, error) {
   394  	var ext pkix.Extension
   395  	if !der.ReadASN1ObjectIdentifier(&ext.Id) {
   396  		return ext, errors.New("x509: malformed extension OID field")
   397  	}
   398  	if der.PeekASN1Tag(cryptobyte_asn1.BOOLEAN) {
   399  		if !der.ReadASN1Boolean(&ext.Critical) {
   400  			return ext, errors.New("x509: malformed extension critical field")
   401  		}
   402  	}
   403  	var val cryptobyte.String
   404  	if !der.ReadASN1(&val, cryptobyte_asn1.OCTET_STRING) {
   405  		return ext, errors.New("x509: malformed extension value field")
   406  	}
   407  	ext.Value = val
   408  	return ext, nil
   409  }