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

     1  package crl
     2  
     3  import (
     4  	"encoding/json"
     5  	"time"
     6  
     7  	"github.com/zmap/zcrypto/encoding/asn1"
     8  	"github.com/zmap/zcrypto/x509"
     9  	"github.com/zmap/zcrypto/x509/pkix"
    10  )
    11  
    12  // RevocationReasonCode - status codes that explain revocation reason see RFC 5280, Section 5.3.1
    13  type RevocationReasonCode int
    14  
    15  var reasonCodeNames map[RevocationReasonCode]string
    16  
    17  func init() {
    18  	reasonCodeNames = make(map[RevocationReasonCode]string)
    19  	reasonCodeNames[0] = "unspecified"
    20  	reasonCodeNames[1] = "keyCompromise"
    21  	reasonCodeNames[2] = "cACompromise"
    22  	reasonCodeNames[3] = "affiliationChanged"
    23  	reasonCodeNames[4] = "superseded"
    24  	reasonCodeNames[5] = "cessationOfOperation"
    25  	reasonCodeNames[6] = "certificateHold"
    26  	// STATUS CODE 7 IS NOT USED
    27  	reasonCodeNames[8] = "removeFromCRL"
    28  	reasonCodeNames[9] = "privilegeWithdrawn"
    29  	reasonCodeNames[10] = "aACompromise"
    30  }
    31  
    32  // MarshalJSON implements the json.Marshler interface
    33  func (code *RevocationReasonCode) MarshalJSON() ([]byte, error) {
    34  	aux := struct {
    35  		Value int    `json:"value"`
    36  		Name  string `json:"name"`
    37  	}{
    38  		Value: int(*code),
    39  		Name:  code.String(),
    40  	}
    41  	return json.Marshal(&aux)
    42  }
    43  
    44  // UnmarshalJSON implements the json.Unmarshaler interface
    45  func (code *RevocationReasonCode) UnmarshalJSON(b []byte) error {
    46  	aux := struct {
    47  		Value int    `json:"value"`
    48  		Name  string `json:"name"`
    49  	}{}
    50  	if err := json.Unmarshal(b, &aux); err != nil {
    51  		return err
    52  	}
    53  	*code = RevocationReasonCode(aux.Value)
    54  	return nil
    55  }
    56  
    57  func (code *RevocationReasonCode) String() string {
    58  	return reasonCodeNames[*code]
    59  }
    60  
    61  var (
    62  	crlNumberExtensionOID        = asn1.ObjectIdentifier{2, 5, 29, 20}
    63  	revocationReasonExtensionOID = asn1.ObjectIdentifier{2, 5, 29, 21}
    64  	invalidityDateExtensionOID   = asn1.ObjectIdentifier{2, 5, 29, 24}
    65  )
    66  
    67  type crlNumberExtension struct {
    68  	ID        asn1.ObjectIdentifier
    69  	Critical  bool `asn1:"optional"`
    70  	CRLNumber int
    71  }
    72  
    73  // TODO: handle additional CRL Extensions
    74  // 2.5.29.21,cRLReason
    75  // id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 }
    76  // -- reasonCode ::= { CRLReasonCode }
    77  
    78  //2.5.29.24,invalidityDate
    79  // id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 }
    80  // -- InvalidityDate ::=  GeneralizedTime
    81  
    82  // RevocationData - information on whether a certificate has been
    83  // revoked by a specified CRL, and information on the CRL
    84  type RevocationData struct {
    85  	CRLSignatureAlgorithm         x509.SignatureAlgorithm
    86  	CRLSignatureValue             []byte
    87  	Version                       int `asn1:"optional,default:0"`
    88  	Issuer                        pkix.Name
    89  	ThisUpdate                    time.Time
    90  	NextUpdate                    time.Time `asn1:"optional"`
    91  	CRLExtensions                 ListExtensionData
    92  	UnknownCRLExtensions          []pkix.Extension `asn1:"tag:0,optional,explicit"`
    93  	UnknownCriticalCRLExtensions  []pkix.Extension `asn1:"tag:0,optional,explicit"`
    94  	IsRevoked                     bool
    95  	RevocationTime                time.Time
    96  	CertificateEntryExtensions    RevokedCertExtensionData
    97  	RawCertificateEntryExtensions []pkix.Extension `asn1:"optional"`
    98  }
    99  
   100  // ListExtensionData - Data from optional, non-critical pkix.CertificateList extensions
   101  type ListExtensionData struct {
   102  	CRLNumber int
   103  	AuthKeyID x509.SubjAuthKeyId `json:"authority_key_id,omitempty"`
   104  }
   105  
   106  // RevokedCertExtensionData - Data from optional, non-critical pkix.RevokedCertificate extensions
   107  type RevokedCertExtensionData struct {
   108  	Reason         *RevocationReasonCode
   109  	invalidityDate time.Time
   110  }
   111  
   112  func gatherListExtensionInfo(certList *pkix.CertificateList, ret *RevocationData) {
   113  	for _, extension := range certList.TBSCertList.Extensions {
   114  		if extension.Id.Equal(crlNumberExtensionOID) {
   115  			var ext crlNumberExtension
   116  			asn1.Unmarshal(extension.Value, &ext.CRLNumber)
   117  			ret.CRLExtensions.CRLNumber = ext.CRLNumber
   118  		} else if extension.Critical {
   119  			ret.UnknownCriticalCRLExtensions = append(ret.UnknownCriticalCRLExtensions, extension)
   120  		} else {
   121  			ret.UnknownCRLExtensions = append(ret.UnknownCRLExtensions, extension)
   122  		}
   123  	}
   124  }
   125  
   126  // CheckCRLForCert - parses through a given CRL and to see if a given certificate
   127  // is present, and returns data on the revocation and CRL in general
   128  func CheckCRLForCert(certList *pkix.CertificateList, cert *x509.Certificate, cache map[string]*pkix.RevokedCertificate) (*RevocationData, error) {
   129  	ret := &RevocationData{
   130  		CRLSignatureAlgorithm: x509.GetSignatureAlgorithmFromAI(certList.SignatureAlgorithm),
   131  		CRLSignatureValue:     certList.SignatureValue.Bytes,
   132  		Version:               certList.TBSCertList.Version,
   133  		ThisUpdate:            certList.TBSCertList.ThisUpdate,
   134  		NextUpdate:            certList.TBSCertList.NextUpdate,
   135  		IsRevoked:             false,
   136  	}
   137  	ret.Issuer.FillFromRDNSequence(&certList.TBSCertList.Issuer)
   138  
   139  	gatherListExtensionInfo(certList, ret)
   140  
   141  	if cache != nil {
   142  		if val, ok := cache[cert.SerialNumber.String()]; ok {
   143  			ret.IsRevoked = true
   144  			ret.RevocationTime = val.RevocationTime
   145  		}
   146  		return ret, nil
   147  	}
   148  
   149  	// else no cache was given, must linear search through
   150  	revokedCerts := certList.TBSCertList.RevokedCertificates
   151  	for i := range revokedCerts {
   152  		if revokedCerts[i].SerialNumber.Cmp(cert.SerialNumber) == 0 {
   153  			ret.IsRevoked = true
   154  			ret.RevocationTime = revokedCerts[i].RevocationTime
   155  			break
   156  		}
   157  	}
   158  	return ret, nil
   159  }