...

Source file src/github.com/google/certificate-transparency-go/x509/revoked.go

Documentation: github.com/google/certificate-transparency-go/x509

     1  // Copyright 2017 Google LLC. All Rights Reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  package x509
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/pem"
    11  	"time"
    12  
    13  	"github.com/google/certificate-transparency-go/asn1"
    14  	"github.com/google/certificate-transparency-go/x509/pkix"
    15  )
    16  
    17  // OID values for CRL extensions (TBSCertList.Extensions), RFC 5280 s5.2.
    18  var (
    19  	OIDExtensionCRLNumber                = asn1.ObjectIdentifier{2, 5, 29, 20}
    20  	OIDExtensionDeltaCRLIndicator        = asn1.ObjectIdentifier{2, 5, 29, 27}
    21  	OIDExtensionIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28}
    22  )
    23  
    24  // OID values for CRL entry extensions (RevokedCertificate.Extensions), RFC 5280 s5.3
    25  var (
    26  	OIDExtensionCRLReasons        = asn1.ObjectIdentifier{2, 5, 29, 21}
    27  	OIDExtensionInvalidityDate    = asn1.ObjectIdentifier{2, 5, 29, 24}
    28  	OIDExtensionCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29}
    29  )
    30  
    31  // RevocationReasonCode represents the reason for a certificate revocation; see RFC 5280 s5.3.1.
    32  type RevocationReasonCode asn1.Enumerated
    33  
    34  // RevocationReasonCode values.
    35  var (
    36  	Unspecified          = RevocationReasonCode(0)
    37  	KeyCompromise        = RevocationReasonCode(1)
    38  	CACompromise         = RevocationReasonCode(2)
    39  	AffiliationChanged   = RevocationReasonCode(3)
    40  	Superseded           = RevocationReasonCode(4)
    41  	CessationOfOperation = RevocationReasonCode(5)
    42  	CertificateHold      = RevocationReasonCode(6)
    43  	RemoveFromCRL        = RevocationReasonCode(8)
    44  	PrivilegeWithdrawn   = RevocationReasonCode(9)
    45  	AACompromise         = RevocationReasonCode(10)
    46  )
    47  
    48  // ReasonFlag holds a bitmask of applicable revocation reasons, from RFC 5280 s4.2.1.13
    49  type ReasonFlag int
    50  
    51  // ReasonFlag values.
    52  const (
    53  	UnusedFlag ReasonFlag = 1 << iota
    54  	KeyCompromiseFlag
    55  	CACompromiseFlag
    56  	AffiliationChangedFlag
    57  	SupersededFlag
    58  	CessationOfOperationFlag
    59  	CertificateHoldFlag
    60  	PrivilegeWithdrawnFlag
    61  	AACompromiseFlag
    62  )
    63  
    64  // CertificateList represents the ASN.1 structure of the same name from RFC 5280, s5.1.
    65  // It has the same content as pkix.CertificateList, but the contents include parsed versions
    66  // of any extensions.
    67  type CertificateList struct {
    68  	Raw                asn1.RawContent
    69  	TBSCertList        TBSCertList
    70  	SignatureAlgorithm pkix.AlgorithmIdentifier
    71  	SignatureValue     asn1.BitString
    72  }
    73  
    74  // ExpiredAt reports whether now is past the expiry time of certList.
    75  func (certList *CertificateList) ExpiredAt(now time.Time) bool {
    76  	return now.After(certList.TBSCertList.NextUpdate)
    77  }
    78  
    79  // Indication of whether extensions need to be critical or non-critical. Extensions that
    80  // can be either are omitted from the map.
    81  var listExtCritical = map[string]bool{
    82  	// From RFC 5280...
    83  	OIDExtensionAuthorityKeyId.String():           false, // s5.2.1
    84  	OIDExtensionIssuerAltName.String():            false, // s5.2.2
    85  	OIDExtensionCRLNumber.String():                false, // s5.2.3
    86  	OIDExtensionDeltaCRLIndicator.String():        true,  // s5.2.4
    87  	OIDExtensionIssuingDistributionPoint.String(): true,  // s5.2.5
    88  	OIDExtensionFreshestCRL.String():              false, // s5.2.6
    89  	OIDExtensionAuthorityInfoAccess.String():      false, // s5.2.7
    90  }
    91  
    92  var certExtCritical = map[string]bool{
    93  	// From RFC 5280...
    94  	OIDExtensionCRLReasons.String():        false, // s5.3.1
    95  	OIDExtensionInvalidityDate.String():    false, // s5.3.2
    96  	OIDExtensionCertificateIssuer.String(): true,  // s5.3.3
    97  }
    98  
    99  // IssuingDistributionPoint represents the ASN.1 structure of the same
   100  // name
   101  type IssuingDistributionPoint struct {
   102  	DistributionPoint          distributionPointName `asn1:"optional,tag:0"`
   103  	OnlyContainsUserCerts      bool                  `asn1:"optional,tag:1"`
   104  	OnlyContainsCACerts        bool                  `asn1:"optional,tag:2"`
   105  	OnlySomeReasons            asn1.BitString        `asn1:"optional,tag:3"`
   106  	IndirectCRL                bool                  `asn1:"optional,tag:4"`
   107  	OnlyContainsAttributeCerts bool                  `asn1:"optional,tag:5"`
   108  }
   109  
   110  // TBSCertList represents the ASN.1 structure of the same name from RFC
   111  // 5280, section 5.1.  It has the same content as pkix.TBSCertificateList
   112  // but the extensions are included in a parsed format.
   113  type TBSCertList struct {
   114  	Raw                 asn1.RawContent
   115  	Version             int
   116  	Signature           pkix.AlgorithmIdentifier
   117  	Issuer              pkix.RDNSequence
   118  	ThisUpdate          time.Time
   119  	NextUpdate          time.Time
   120  	RevokedCertificates []*RevokedCertificate
   121  	Extensions          []pkix.Extension
   122  	// Cracked out extensions:
   123  	AuthorityKeyID               []byte
   124  	IssuerAltNames               GeneralNames
   125  	CRLNumber                    int
   126  	BaseCRLNumber                int // -1 if no delta CRL present
   127  	IssuingDistributionPoint     IssuingDistributionPoint
   128  	IssuingDPFullNames           GeneralNames
   129  	FreshestCRLDistributionPoint []string
   130  	OCSPServer                   []string
   131  	IssuingCertificateURL        []string
   132  }
   133  
   134  // ParseCertificateList parses a CertificateList (e.g. a CRL) from the given
   135  // bytes. It's often the case that PEM encoded CRLs will appear where they
   136  // should be DER encoded, so this function will transparently handle PEM
   137  // encoding as long as there isn't any leading garbage.
   138  func ParseCertificateList(clBytes []byte) (*CertificateList, error) {
   139  	if bytes.HasPrefix(clBytes, pemCRLPrefix) {
   140  		block, _ := pem.Decode(clBytes)
   141  		if block != nil && block.Type == pemType {
   142  			clBytes = block.Bytes
   143  		}
   144  	}
   145  	return ParseCertificateListDER(clBytes)
   146  }
   147  
   148  // ParseCertificateListDER parses a DER encoded CertificateList from the given bytes.
   149  // For non-fatal errors, this function returns both an error and a CertificateList
   150  // object.
   151  func ParseCertificateListDER(derBytes []byte) (*CertificateList, error) {
   152  	var errs Errors
   153  	// First parse the DER into the pkix structures.
   154  	pkixList := new(pkix.CertificateList)
   155  	if rest, err := asn1.Unmarshal(derBytes, pkixList); err != nil {
   156  		errs.AddID(ErrInvalidCertList, err)
   157  		return nil, &errs
   158  	} else if len(rest) != 0 {
   159  		errs.AddID(ErrTrailingCertList)
   160  		return nil, &errs
   161  	}
   162  
   163  	// Transcribe the revoked certs but crack out extensions.
   164  	revokedCerts := make([]*RevokedCertificate, len(pkixList.TBSCertList.RevokedCertificates))
   165  	for i, pkixRevoked := range pkixList.TBSCertList.RevokedCertificates {
   166  		revokedCerts[i] = parseRevokedCertificate(pkixRevoked, &errs)
   167  		if revokedCerts[i] == nil {
   168  			return nil, &errs
   169  		}
   170  	}
   171  
   172  	certList := CertificateList{
   173  		Raw: derBytes,
   174  		TBSCertList: TBSCertList{
   175  			Raw:                 pkixList.TBSCertList.Raw,
   176  			Version:             pkixList.TBSCertList.Version,
   177  			Signature:           pkixList.TBSCertList.Signature,
   178  			Issuer:              pkixList.TBSCertList.Issuer,
   179  			ThisUpdate:          pkixList.TBSCertList.ThisUpdate,
   180  			NextUpdate:          pkixList.TBSCertList.NextUpdate,
   181  			RevokedCertificates: revokedCerts,
   182  			Extensions:          pkixList.TBSCertList.Extensions,
   183  			CRLNumber:           -1,
   184  			BaseCRLNumber:       -1,
   185  		},
   186  		SignatureAlgorithm: pkixList.SignatureAlgorithm,
   187  		SignatureValue:     pkixList.SignatureValue,
   188  	}
   189  
   190  	// Now crack out extensions.
   191  	for _, e := range certList.TBSCertList.Extensions {
   192  		if expectCritical, present := listExtCritical[e.Id.String()]; present {
   193  			if e.Critical && !expectCritical {
   194  				errs.AddID(ErrUnexpectedlyCriticalCertListExtension, e.Id)
   195  			} else if !e.Critical && expectCritical {
   196  				errs.AddID(ErrUnexpectedlyNonCriticalCertListExtension, e.Id)
   197  			}
   198  		}
   199  		switch {
   200  		case e.Id.Equal(OIDExtensionAuthorityKeyId):
   201  			// RFC 5280 s5.2.1
   202  			var a authKeyId
   203  			if rest, err := asn1.Unmarshal(e.Value, &a); err != nil {
   204  				errs.AddID(ErrInvalidCertListAuthKeyID, err)
   205  			} else if len(rest) != 0 {
   206  				errs.AddID(ErrTrailingCertListAuthKeyID)
   207  			}
   208  			certList.TBSCertList.AuthorityKeyID = a.Id
   209  		case e.Id.Equal(OIDExtensionIssuerAltName):
   210  			// RFC 5280 s5.2.2
   211  			if err := parseGeneralNames(e.Value, &certList.TBSCertList.IssuerAltNames); err != nil {
   212  				errs.AddID(ErrInvalidCertListIssuerAltName, err)
   213  			}
   214  		case e.Id.Equal(OIDExtensionCRLNumber):
   215  			// RFC 5280 s5.2.3
   216  			if rest, err := asn1.Unmarshal(e.Value, &certList.TBSCertList.CRLNumber); err != nil {
   217  				errs.AddID(ErrInvalidCertListCRLNumber, err)
   218  			} else if len(rest) != 0 {
   219  				errs.AddID(ErrTrailingCertListCRLNumber)
   220  			}
   221  			if certList.TBSCertList.CRLNumber < 0 {
   222  				errs.AddID(ErrNegativeCertListCRLNumber, certList.TBSCertList.CRLNumber)
   223  			}
   224  		case e.Id.Equal(OIDExtensionDeltaCRLIndicator):
   225  			// RFC 5280 s5.2.4
   226  			if rest, err := asn1.Unmarshal(e.Value, &certList.TBSCertList.BaseCRLNumber); err != nil {
   227  				errs.AddID(ErrInvalidCertListDeltaCRL, err)
   228  			} else if len(rest) != 0 {
   229  				errs.AddID(ErrTrailingCertListDeltaCRL)
   230  			}
   231  			if certList.TBSCertList.BaseCRLNumber < 0 {
   232  				errs.AddID(ErrNegativeCertListDeltaCRL, certList.TBSCertList.BaseCRLNumber)
   233  			}
   234  		case e.Id.Equal(OIDExtensionIssuingDistributionPoint):
   235  			parseIssuingDistributionPoint(e.Value, &certList.TBSCertList.IssuingDistributionPoint, &certList.TBSCertList.IssuingDPFullNames, &errs)
   236  		case e.Id.Equal(OIDExtensionFreshestCRL):
   237  			// RFC 5280 s5.2.6
   238  			if err := parseDistributionPoints(e.Value, &certList.TBSCertList.FreshestCRLDistributionPoint); err != nil {
   239  				errs.AddID(ErrInvalidCertListFreshestCRL, err)
   240  				return nil, err
   241  			}
   242  		case e.Id.Equal(OIDExtensionAuthorityInfoAccess):
   243  			// RFC 5280 s5.2.7
   244  			var aia []accessDescription
   245  			if rest, err := asn1.Unmarshal(e.Value, &aia); err != nil {
   246  				errs.AddID(ErrInvalidCertListAuthInfoAccess, err)
   247  			} else if len(rest) != 0 {
   248  				errs.AddID(ErrTrailingCertListAuthInfoAccess)
   249  			}
   250  
   251  			for _, v := range aia {
   252  				// GeneralName: uniformResourceIdentifier [6] IA5String
   253  				if v.Location.Tag != tagURI {
   254  					continue
   255  				}
   256  				switch {
   257  				case v.Method.Equal(OIDAuthorityInfoAccessOCSP):
   258  					certList.TBSCertList.OCSPServer = append(certList.TBSCertList.OCSPServer, string(v.Location.Bytes))
   259  				case v.Method.Equal(OIDAuthorityInfoAccessIssuers):
   260  					certList.TBSCertList.IssuingCertificateURL = append(certList.TBSCertList.IssuingCertificateURL, string(v.Location.Bytes))
   261  				}
   262  				// TODO(drysdale): cope with more possibilities
   263  			}
   264  		default:
   265  			if e.Critical {
   266  				errs.AddID(ErrUnhandledCriticalCertListExtension, e.Id)
   267  			}
   268  		}
   269  	}
   270  
   271  	if errs.Fatal() {
   272  		return nil, &errs
   273  	}
   274  	if errs.Empty() {
   275  		return &certList, nil
   276  	}
   277  	return &certList, &errs
   278  }
   279  
   280  func parseIssuingDistributionPoint(data []byte, idp *IssuingDistributionPoint, name *GeneralNames, errs *Errors) {
   281  	// RFC 5280 s5.2.5
   282  	if rest, err := asn1.Unmarshal(data, idp); err != nil {
   283  		errs.AddID(ErrInvalidCertListIssuingDP, err)
   284  	} else if len(rest) != 0 {
   285  		errs.AddID(ErrTrailingCertListIssuingDP)
   286  	}
   287  
   288  	typeCount := 0
   289  	if idp.OnlyContainsUserCerts {
   290  		typeCount++
   291  	}
   292  	if idp.OnlyContainsCACerts {
   293  		typeCount++
   294  	}
   295  	if idp.OnlyContainsAttributeCerts {
   296  		typeCount++
   297  	}
   298  	if typeCount > 1 {
   299  		errs.AddID(ErrCertListIssuingDPMultipleTypes, idp.OnlyContainsUserCerts, idp.OnlyContainsCACerts, idp.OnlyContainsAttributeCerts)
   300  	}
   301  	for _, fn := range idp.DistributionPoint.FullName {
   302  		if _, err := parseGeneralName(fn.FullBytes, name, false); err != nil {
   303  			errs.AddID(ErrCertListIssuingDPInvalidFullName, err)
   304  		}
   305  	}
   306  }
   307  
   308  // RevokedCertificate represents the unnamed ASN.1 structure that makes up the
   309  // revokedCertificates member of the TBSCertList structure from RFC 5280, s5.1.
   310  // It has the same content as pkix.RevokedCertificate but the extensions are
   311  // included in a parsed format.
   312  type RevokedCertificate struct {
   313  	pkix.RevokedCertificate
   314  	// Cracked out extensions:
   315  	RevocationReason RevocationReasonCode
   316  	InvalidityDate   time.Time
   317  	Issuer           GeneralNames
   318  }
   319  
   320  func parseRevokedCertificate(pkixRevoked pkix.RevokedCertificate, errs *Errors) *RevokedCertificate {
   321  	result := RevokedCertificate{RevokedCertificate: pkixRevoked}
   322  	for _, e := range pkixRevoked.Extensions {
   323  		if expectCritical, present := certExtCritical[e.Id.String()]; present {
   324  			if e.Critical && !expectCritical {
   325  				errs.AddID(ErrUnexpectedlyCriticalRevokedCertExtension, e.Id)
   326  			} else if !e.Critical && expectCritical {
   327  				errs.AddID(ErrUnexpectedlyNonCriticalRevokedCertExtension, e.Id)
   328  			}
   329  		}
   330  		switch {
   331  		case e.Id.Equal(OIDExtensionCRLReasons):
   332  			// RFC 5280, s5.3.1
   333  			var reason asn1.Enumerated
   334  			if rest, err := asn1.Unmarshal(e.Value, &reason); err != nil {
   335  				errs.AddID(ErrInvalidRevocationReason, err)
   336  			} else if len(rest) != 0 {
   337  				errs.AddID(ErrTrailingRevocationReason)
   338  			}
   339  			result.RevocationReason = RevocationReasonCode(reason)
   340  		case e.Id.Equal(OIDExtensionInvalidityDate):
   341  			// RFC 5280, s5.3.2
   342  			if rest, err := asn1.Unmarshal(e.Value, &result.InvalidityDate); err != nil {
   343  				errs.AddID(ErrInvalidRevocationInvalidityDate, err)
   344  			} else if len(rest) != 0 {
   345  				errs.AddID(ErrTrailingRevocationInvalidityDate)
   346  			}
   347  		case e.Id.Equal(OIDExtensionCertificateIssuer):
   348  			// RFC 5280, s5.3.3
   349  			if err := parseGeneralNames(e.Value, &result.Issuer); err != nil {
   350  				errs.AddID(ErrInvalidRevocationIssuer, err)
   351  			}
   352  		default:
   353  			if e.Critical {
   354  				errs.AddID(ErrUnhandledCriticalRevokedCertExtension, e.Id)
   355  			}
   356  		}
   357  	}
   358  	return &result
   359  }
   360  
   361  // CheckCertificateListSignature checks that the signature in crl is from c.
   362  func (c *Certificate) CheckCertificateListSignature(crl *CertificateList) error {
   363  	algo := SignatureAlgorithmFromAI(crl.SignatureAlgorithm)
   364  	return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
   365  }
   366  

View as plain text