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