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