1// Copyright 2016 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package ctfe
16
17import (
18	"bytes"
19	"errors"
20	"fmt"
21
22	"github.com/google/certificate-transparency-go/asn1"
23	"github.com/google/certificate-transparency-go/x509"
24)
25
26// OID of the non-critical extension used to mark pre-certificates, defined in RFC 6962
27var ctPoisonExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
28
29// Byte representation of ASN.1 NULL.
30var asn1NullBytes = []byte{0x05, 0x00}
31
32// IsPrecertificate tests if a certificate is a pre-certificate as defined in CT.
33// An error is returned if the CT extension is present but is not ASN.1 NULL as defined
34// by the spec.
35func IsPrecertificate(cert *x509.Certificate) (bool, error) {
36	for _, ext := range cert.Extensions {
37		if ctPoisonExtensionOID.Equal(ext.Id) {
38			if !ext.Critical || !bytes.Equal(asn1NullBytes, ext.Value) {
39				return false, fmt.Errorf("CT poison ext is not critical or invalid: %v", ext)
40			}
41
42			return true, nil
43		}
44	}
45
46	return false, nil
47}
48
49// ValidateChain takes the certificate chain as it was parsed from a JSON request. Ensures all
50// elements in the chain decode as X.509 certificates. Ensures that there is a valid path from the
51// end entity certificate in the chain to a trusted root cert, possibly using the intermediates
52// supplied in the chain. Then applies the RFC requirement that the path must involve all
53// the submitted chain in the order of submission.
54func ValidateChain(rawChain [][]byte, validationOpts CertValidationOpts) ([]*x509.Certificate, error) {
55	// First make sure the certs parse as X.509
56	chain := make([]*x509.Certificate, 0, len(rawChain))
57	intermediatePool := NewPEMCertPool()
58
59	for i, certBytes := range rawChain {
60		cert, err := x509.ParseCertificate(certBytes)
61		if x509.IsFatal(err) {
62			return nil, err
63		}
64
65		chain = append(chain, cert)
66
67		// All but the first cert form part of the intermediate pool
68		if i > 0 {
69			intermediatePool.AddCert(cert)
70		}
71	}
72
73	naStart := validationOpts.notAfterStart
74	naLimit := validationOpts.notAfterLimit
75
76	// Check whether the expiry date of this certificate is within the acceptable
77	// range.
78	if naStart != nil && chain[0].NotAfter.Before(*naStart) {
79		return nil, fmt.Errorf("certificate NotAfter (%v) < %v", chain[0].NotAfter, *naStart)
80	}
81	if naLimit != nil && !chain[0].NotAfter.Before(*naLimit) {
82		return nil, fmt.Errorf("certificate NotAfter (%v) >= %v", chain[0].NotAfter, *naLimit)
83	}
84
85	if validationOpts.acceptOnlyCA && !chain[0].IsCA {
86		return nil, errors.New("only certificates with CA bit set are accepted")
87	}
88
89	// We can now do the verification.  Use fairly lax options for verification, as
90	// CT is intended to observe certificates rather than police them.
91	verifyOpts := x509.VerifyOptions{
92		Roots:             validationOpts.trustedRoots.CertPool(),
93		Intermediates:     intermediatePool.CertPool(),
94		DisableTimeChecks: !validationOpts.rejectExpired,
95		// Precertificates have the poison extension; also the Go library code does not
96		// support the standard PolicyConstraints extension (which is required to be marked
97		// critical, RFC 5280 s4.2.1.11), so never check unhandled critical extensions.
98		DisableCriticalExtensionChecks: true,
99		// Pre-issued precertificates have the Certificate Transparency EKU; also some
100		// leaves have unknown EKUs that should not be bounced just because the intermediate
101		// does not also have them (cf. https://github.com/golang/go/issues/24590) so
102		// disable EKU checks.
103		DisableEKUChecks: true,
104		// Path length checks get confused by the presence of an additional
105		// pre-issuer intermediate, so disable them.
106		DisablePathLenChecks:        true,
107		DisableNameConstraintChecks: true,
108		DisableNameChecks:           false,
109		KeyUsages:                   validationOpts.extKeyUsages,
110	}
111
112	verifiedChains, err := chain[0].Verify(verifyOpts)
113	if err != nil {
114		return nil, err
115	}
116
117	if len(verifiedChains) == 0 {
118		return nil, errors.New("no path to root found when trying to validate chains")
119	}
120
121	// Verify might have found multiple paths to roots. Now we check that we have a path that
122	// uses all the certs in the order they were submitted so as to comply with RFC 6962
123	// requirements detailed in Section 3.1.
124	for _, verifiedChain := range verifiedChains {
125		if chainsEquivalent(chain, verifiedChain) {
126			return verifiedChain, nil
127		}
128	}
129
130	return nil, errors.New("no RFC compliant path to root found when trying to validate chain")
131}
132
133func chainsEquivalent(inChain []*x509.Certificate, verifiedChain []*x509.Certificate) bool {
134	// The verified chain includes a root, but the input chain may or may not include a
135	// root (RFC 6962 s4.1/ s4.2 "the last [certificate] is either the root certificate
136	// or a certificate that chains to a known root certificate").
137	if len(inChain) != len(verifiedChain) && len(inChain) != (len(verifiedChain)-1) {
138		return false
139	}
140
141	for i, certInChain := range inChain {
142		if !certInChain.Equal(verifiedChain[i]) {
143			return false
144		}
145	}
146	return true
147}
148