2022-01-21 11:24:10 +08:00
package smx509
import (
"bytes"
2022-04-11 15:55:23 +08:00
"crypto"
2022-01-21 11:24:10 +08:00
"crypto/x509"
2022-05-09 14:47:23 +08:00
"crypto/x509/pkix"
2022-01-21 11:24:10 +08:00
"errors"
"fmt"
"net"
2025-01-17 08:30:27 +08:00
"net/netip"
2022-01-21 11:24:10 +08:00
"net/url"
"reflect"
"runtime"
"strings"
"time"
"unicode/utf8"
)
const (
NotAuthorizedToSign = x509 . NotAuthorizedToSign
Expired = x509 . Expired
CANotAuthorizedForThisName = x509 . CANotAuthorizedForThisName
TooManyIntermediates = x509 . TooManyIntermediates
IncompatibleUsage = x509 . IncompatibleUsage
NameMismatch = x509 . NameMismatch
NameConstraintsWithoutSANs = x509 . NameConstraintsWithoutSANs
UnconstrainedName = x509 . UnconstrainedName
TooManyConstraints = x509 . TooManyConstraints
CANotAuthorizedForExtKeyUsage = x509 . CANotAuthorizedForExtKeyUsage
)
type CertificateInvalidError = x509 . CertificateInvalidError
// UnknownAuthorityError results when the certificate issuer is unknown
type UnknownAuthorityError struct {
Cert * Certificate
// hintErr contains an error that may be helpful in determining why an
// authority wasn't found.
hintErr error
// hintCert contains a possible authority certificate that was rejected
// because of the error in hintErr.
hintCert * Certificate
}
func ( e UnknownAuthorityError ) Error ( ) string {
s := "x509: certificate signed by unknown authority"
if e . hintErr != nil {
certName := e . hintCert . Subject . CommonName
if len ( certName ) == 0 {
if len ( e . hintCert . Subject . Organization ) > 0 {
certName = e . hintCert . Subject . Organization [ 0 ]
} else {
certName = "serial:" + e . hintCert . SerialNumber . String ( )
}
}
s += fmt . Sprintf ( " (possibly because of %q while trying to verify candidate authority certificate %q)" , e . hintErr , certName )
}
return s
}
// errNotParsed is returned when a certificate without ASN.1 contents is
// verified. Platform-specific verification needs the ASN.1 contents.
var errNotParsed = errors . New ( "x509: missing ASN.1 contents; use ParseCertificate" )
// VerifyOptions contains parameters for Certificate.Verify.
type VerifyOptions struct {
// DNSName, if set, is checked against the leaf certificate with
// Certificate.VerifyHostname or the platform verifier.
DNSName string
// Intermediates is an optional pool of certificates that are not trust
// anchors, but can be used to form a chain from the leaf certificate to a
// root certificate.
Intermediates * CertPool
// Roots is the set of trusted root certificates the leaf certificate needs
// to chain up to. If nil, the system roots or the platform verifier are used.
Roots * CertPool
// CurrentTime is used to check the validity of all certificates in the
// chain. If zero, the current time is used.
CurrentTime time . Time
// KeyUsages specifies which Extended Key Usage values are acceptable. A
// chain is accepted if it allows any of the listed values. An empty list
// means ExtKeyUsageServerAuth. To accept any key usage, include ExtKeyUsageAny.
KeyUsages [ ] ExtKeyUsage
// MaxConstraintComparisions is the maximum number of comparisons to
// perform when checking a given certificate's name constraints. If
// zero, a sensible default is used. This limit prevents pathological
// certificates from consuming excessive amounts of CPU time when
// validating. It does not apply to the platform verifier.
MaxConstraintComparisions int
}
const (
leafCertificate = iota
intermediateCertificate
rootCertificate
)
// rfc2821Mailbox represents a “mailbox” (which is an email address to most
// people) by breaking it into the “local” (i.e. before the '@') and “domain”
// parts.
type rfc2821Mailbox struct {
local , domain string
}
// parseRFC2821Mailbox parses an email address into local and domain parts,
// based on the ABNF for a “Mailbox” from RFC 2821. According to RFC 5280,
// Section 4.2.1.6 that's correct for an rfc822Name from a certificate: “The
// format of an rfc822Name is a "Mailbox" as defined in RFC 2821, Section 4.1.2”.
func parseRFC2821Mailbox ( in string ) ( mailbox rfc2821Mailbox , ok bool ) {
if len ( in ) == 0 {
return mailbox , false
}
localPartBytes := make ( [ ] byte , 0 , len ( in ) / 2 )
if in [ 0 ] == '"' {
// Quoted-string = DQUOTE *qcontent DQUOTE
// non-whitespace-control = %d1-8 / %d11 / %d12 / %d14-31 / %d127
// qcontent = qtext / quoted-pair
// qtext = non-whitespace-control /
// %d33 / %d35-91 / %d93-126
// quoted-pair = ("\" text) / obs-qp
// text = %d1-9 / %d11 / %d12 / %d14-127 / obs-text
//
// (Names beginning with “obs-” are the obsolete syntax from RFC 2822,
// Section 4. Since it has been 16 years, we no longer accept that.)
in = in [ 1 : ]
QuotedString :
for {
if len ( in ) == 0 {
return mailbox , false
}
c := in [ 0 ]
in = in [ 1 : ]
switch {
case c == '"' :
break QuotedString
case c == '\\' :
// quoted-pair
if len ( in ) == 0 {
return mailbox , false
}
if in [ 0 ] == 11 ||
in [ 0 ] == 12 ||
( 1 <= in [ 0 ] && in [ 0 ] <= 9 ) ||
( 14 <= in [ 0 ] && in [ 0 ] <= 127 ) {
localPartBytes = append ( localPartBytes , in [ 0 ] )
in = in [ 1 : ]
} else {
return mailbox , false
}
case c == 11 ||
c == 12 ||
// Space (char 32) is not allowed based on the
// BNF, but RFC 3696 gives an example that
// assumes that it is. Several “verified”
// errata continue to argue about this point.
// We choose to accept it.
c == 32 ||
c == 33 ||
c == 127 ||
( 1 <= c && c <= 8 ) ||
( 14 <= c && c <= 31 ) ||
( 35 <= c && c <= 91 ) ||
( 93 <= c && c <= 126 ) :
// qtext
localPartBytes = append ( localPartBytes , c )
default :
return mailbox , false
}
}
} else {
// Atom ("." Atom)*
NextChar :
for len ( in ) > 0 {
// atext from RFC 2822, Section 3.2.4
c := in [ 0 ]
switch {
case c == '\\' :
// Examples given in RFC 3696 suggest that
// escaped characters can appear outside of a
// quoted string. Several “verified” errata
// continue to argue the point. We choose to
// accept it.
in = in [ 1 : ]
if len ( in ) == 0 {
return mailbox , false
}
fallthrough
case ( '0' <= c && c <= '9' ) ||
( 'a' <= c && c <= 'z' ) ||
( 'A' <= c && c <= 'Z' ) ||
c == '!' || c == '#' || c == '$' || c == '%' ||
c == '&' || c == '\'' || c == '*' || c == '+' ||
c == '-' || c == '/' || c == '=' || c == '?' ||
c == '^' || c == '_' || c == '`' || c == '{' ||
c == '|' || c == '}' || c == '~' || c == '.' :
localPartBytes = append ( localPartBytes , in [ 0 ] )
in = in [ 1 : ]
default :
break NextChar
}
}
if len ( localPartBytes ) == 0 {
return mailbox , false
}
// From RFC 3696, Section 3:
// “period (".") may also appear, but may not be used to start
// or end the local part, nor may two or more consecutive
// periods appear.”
twoDots := [ ] byte { '.' , '.' }
if localPartBytes [ 0 ] == '.' ||
localPartBytes [ len ( localPartBytes ) - 1 ] == '.' ||
bytes . Contains ( localPartBytes , twoDots ) {
return mailbox , false
}
}
if len ( in ) == 0 || in [ 0 ] != '@' {
return mailbox , false
}
in = in [ 1 : ]
// The RFC species a format for domains, but that's known to be
// violated in practice so we accept that anything after an '@' is the
// domain part.
if _ , ok := domainToReverseLabels ( in ) ; ! ok {
return mailbox , false
}
mailbox . local = string ( localPartBytes )
mailbox . domain = in
return mailbox , true
}
2022-02-08 16:30:28 +08:00
// domainToReverseLabels converts a textual domain name like foo.example.com to
// the list of labels in reverse order, e.g. ["com", "example", "foo"].
func domainToReverseLabels ( domain string ) ( reverseLabels [ ] string , ok bool ) {
for len ( domain ) > 0 {
if i := strings . LastIndexByte ( domain , '.' ) ; i == - 1 {
reverseLabels = append ( reverseLabels , domain )
domain = ""
} else {
reverseLabels = append ( reverseLabels , domain [ i + 1 : ] )
domain = domain [ : i ]
2024-05-23 17:35:56 +08:00
if i == 0 { // domain == ""
// domain is prefixed with an empty label, append an empty
// string to reverseLabels to indicate this.
reverseLabels = append ( reverseLabels , "" )
2025-01-17 08:30:27 +08:00
}
2022-02-08 16:30:28 +08:00
}
}
if len ( reverseLabels ) > 0 && len ( reverseLabels [ 0 ] ) == 0 {
// An empty label at the end indicates an absolute value.
return nil , false
}
for _ , label := range reverseLabels {
if len ( label ) == 0 {
// Empty labels are otherwise invalid.
return nil , false
}
for _ , c := range label {
if c < 33 || c > 126 {
// Invalid character.
return nil , false
}
}
}
return reverseLabels , true
}
func matchEmailConstraint ( mailbox rfc2821Mailbox , constraint string ) ( bool , error ) {
// If the constraint contains an @, then it specifies an exact mailbox
// name.
if strings . Contains ( constraint , "@" ) {
constraintMailbox , ok := parseRFC2821Mailbox ( constraint )
if ! ok {
return false , fmt . Errorf ( "x509: internal error: cannot parse constraint %q" , constraint )
}
return mailbox . local == constraintMailbox . local && strings . EqualFold ( mailbox . domain , constraintMailbox . domain ) , nil
}
// Otherwise the constraint is like a DNS constraint of the domain part
// of the mailbox.
return matchDomainConstraint ( mailbox . domain , constraint )
}
func matchURIConstraint ( uri * url . URL , constraint string ) ( bool , error ) {
// From RFC 5280, Section 4.2.1.10:
// “a uniformResourceIdentifier that does not include an authority
// component with a host name specified as a fully qualified domain
// name (e.g., if the URI either does not include an authority
// component or includes an authority component in which the host name
// is specified as an IP address), then the application MUST reject the
// certificate.”
host := uri . Host
if len ( host ) == 0 {
return false , fmt . Errorf ( "URI with empty host (%q) cannot be matched against constraints" , uri . String ( ) )
}
if strings . Contains ( host , ":" ) && ! strings . HasSuffix ( host , "]" ) {
var err error
host , _ , err = net . SplitHostPort ( uri . Host )
if err != nil {
return false , err
}
}
2025-01-17 08:30:27 +08:00
// netip.ParseAddr will reject the URI IPv6 literal form "[...]", so we
// check if _either_ the string parses as an IP, or if it is enclosed in
// square brackets.
if _ , err := netip . ParseAddr ( host ) ; err == nil || ( strings . HasPrefix ( host , "[" ) && strings . HasSuffix ( host , "]" ) ) {
2022-02-08 16:30:28 +08:00
return false , fmt . Errorf ( "URI with IP (%q) cannot be matched against constraints" , uri . String ( ) )
}
return matchDomainConstraint ( host , constraint )
}
func matchIPConstraint ( ip net . IP , constraint * net . IPNet ) ( bool , error ) {
if len ( ip ) != len ( constraint . IP ) {
return false , nil
}
for i := range ip {
if mask := constraint . Mask [ i ] ; ip [ i ] & mask != constraint . IP [ i ] & mask {
return false , nil
}
}
return true , nil
}
func matchDomainConstraint ( domain , constraint string ) ( bool , error ) {
// The meaning of zero length constraints is not specified, but this
// code follows NSS and accepts them as matching everything.
if len ( constraint ) == 0 {
return true , nil
}
domainLabels , ok := domainToReverseLabels ( domain )
if ! ok {
return false , fmt . Errorf ( "x509: internal error: cannot parse domain %q" , domain )
}
// RFC 5280 says that a leading period in a domain name means that at
// least one label must be prepended, but only for URI and email
// constraints, not DNS constraints. The code also supports that
// behaviour for DNS constraints.
mustHaveSubdomains := false
if constraint [ 0 ] == '.' {
mustHaveSubdomains = true
constraint = constraint [ 1 : ]
}
constraintLabels , ok := domainToReverseLabels ( constraint )
if ! ok {
return false , fmt . Errorf ( "x509: internal error: cannot parse domain %q" , constraint )
}
if len ( domainLabels ) < len ( constraintLabels ) ||
( mustHaveSubdomains && len ( domainLabels ) == len ( constraintLabels ) ) {
return false , nil
}
for i , constraintLabel := range constraintLabels {
if ! strings . EqualFold ( constraintLabel , domainLabels [ i ] ) {
return false , nil
}
}
return true , nil
}
2022-01-21 11:24:10 +08:00
// checkNameConstraints checks that c permits a child certificate to claim the
// given name, of type nameType. The argument parsedName contains the parsed
// form of name, suitable for passing to the match function. The total number
// of comparisons is tracked in the given count and should not exceed the given
// limit.
func ( c * Certificate ) checkNameConstraints ( count * int ,
maxConstraintComparisons int ,
nameType string ,
name string ,
2023-11-09 08:34:50 +08:00
parsedName any ,
match func ( parsedName , constraint any ) ( match bool , err error ) ,
permitted , excluded any ) error {
2022-01-21 11:24:10 +08:00
excludedValue := reflect . ValueOf ( excluded )
* count += excludedValue . Len ( )
if * count > maxConstraintComparisons {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : TooManyConstraints , Detail : "" }
2022-01-21 11:24:10 +08:00
}
for i := 0 ; i < excludedValue . Len ( ) ; i ++ {
constraint := excludedValue . Index ( i ) . Interface ( )
match , err := match ( parsedName , constraint )
if err != nil {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : CANotAuthorizedForThisName , Detail : err . Error ( ) }
2022-01-21 11:24:10 +08:00
}
if match {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : CANotAuthorizedForThisName , Detail : fmt . Sprintf ( "%s %q is excluded by constraint %q" , nameType , name , constraint ) }
2022-01-21 11:24:10 +08:00
}
}
permittedValue := reflect . ValueOf ( permitted )
* count += permittedValue . Len ( )
if * count > maxConstraintComparisons {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : TooManyConstraints , Detail : "" }
2022-01-21 11:24:10 +08:00
}
ok := true
for i := 0 ; i < permittedValue . Len ( ) ; i ++ {
constraint := permittedValue . Index ( i ) . Interface ( )
var err error
if ok , err = match ( parsedName , constraint ) ; err != nil {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : CANotAuthorizedForThisName , Detail : err . Error ( ) }
2022-01-21 11:24:10 +08:00
}
if ok {
break
}
}
if ! ok {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : CANotAuthorizedForThisName , Detail : fmt . Sprintf ( "%s %q is not permitted by any constraint" , nameType , name ) }
2022-01-21 11:24:10 +08:00
}
return nil
}
// isValid performs validity checks on c given that it is a candidate to append
// to the chain in currentChain.
func ( c * Certificate ) isValid ( certType int , currentChain [ ] * Certificate , opts * VerifyOptions ) error {
if len ( c . UnhandledCriticalExtensions ) > 0 {
return x509 . UnhandledCriticalExtension { }
}
if len ( currentChain ) > 0 {
child := currentChain [ len ( currentChain ) - 1 ]
if ! bytes . Equal ( child . RawIssuer , c . RawSubject ) {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : NameMismatch , Detail : "" }
2022-01-21 11:24:10 +08:00
}
}
now := opts . CurrentTime
if now . IsZero ( ) {
now = time . Now ( )
}
if now . Before ( c . NotBefore ) {
return CertificateInvalidError {
Cert : c . asX509 ( ) ,
Reason : Expired ,
Detail : fmt . Sprintf ( "current time %s is before %s" , now . Format ( time . RFC3339 ) , c . NotBefore . Format ( time . RFC3339 ) ) ,
}
} else if now . After ( c . NotAfter ) {
return CertificateInvalidError {
Cert : c . asX509 ( ) ,
Reason : Expired ,
Detail : fmt . Sprintf ( "current time %s is after %s" , now . Format ( time . RFC3339 ) , c . NotAfter . Format ( time . RFC3339 ) ) ,
}
}
maxConstraintComparisons := opts . MaxConstraintComparisions
if maxConstraintComparisons == 0 {
maxConstraintComparisons = 250000
}
comparisonCount := 0
if certType == intermediateCertificate || certType == rootCertificate {
if len ( currentChain ) == 0 {
return errors . New ( "x509: internal error: empty chain when appending CA cert" )
}
}
2022-04-11 15:55:23 +08:00
if ( certType == intermediateCertificate || certType == rootCertificate ) &&
c . hasNameConstraints ( ) {
toCheck := [ ] * Certificate { }
2023-06-01 13:10:54 +08:00
for _ , c := range currentChain {
if c . hasSANExtension ( ) {
toCheck = append ( toCheck , c )
}
2022-04-11 15:55:23 +08:00
}
for _ , sanCert := range toCheck {
err := forEachSAN ( sanCert . getSANExtension ( ) , func ( tag int , data [ ] byte ) error {
switch tag {
case nameTypeEmail :
name := string ( data )
mailbox , ok := parseRFC2821Mailbox ( name )
if ! ok {
return fmt . Errorf ( "x509: cannot parse rfc822Name %q" , mailbox )
}
if err := c . checkNameConstraints ( & comparisonCount , maxConstraintComparisons , "email address" , name , mailbox ,
2023-11-09 08:34:50 +08:00
func ( parsedName , constraint any ) ( bool , error ) {
2022-04-11 15:55:23 +08:00
return matchEmailConstraint ( parsedName . ( rfc2821Mailbox ) , constraint . ( string ) )
} , c . PermittedEmailAddresses , c . ExcludedEmailAddresses ) ; err != nil {
return err
}
case nameTypeDNS :
name := string ( data )
if _ , ok := domainToReverseLabels ( name ) ; ! ok {
return fmt . Errorf ( "x509: cannot parse dnsName %q" , name )
}
if err := c . checkNameConstraints ( & comparisonCount , maxConstraintComparisons , "DNS name" , name , name ,
2023-11-09 08:34:50 +08:00
func ( parsedName , constraint any ) ( bool , error ) {
2022-04-11 15:55:23 +08:00
return matchDomainConstraint ( parsedName . ( string ) , constraint . ( string ) )
} , c . PermittedDNSDomains , c . ExcludedDNSDomains ) ; err != nil {
return err
}
case nameTypeURI :
name := string ( data )
uri , err := url . Parse ( name )
if err != nil {
return fmt . Errorf ( "x509: internal error: URI SAN %q failed to parse" , name )
}
if err := c . checkNameConstraints ( & comparisonCount , maxConstraintComparisons , "URI" , name , uri ,
2023-11-09 08:34:50 +08:00
func ( parsedName , constraint any ) ( bool , error ) {
2022-04-11 15:55:23 +08:00
return matchURIConstraint ( parsedName . ( * url . URL ) , constraint . ( string ) )
} , c . PermittedURIDomains , c . ExcludedURIDomains ) ; err != nil {
return err
}
case nameTypeIP :
ip := net . IP ( data )
if l := len ( ip ) ; l != net . IPv4len && l != net . IPv6len {
return fmt . Errorf ( "x509: internal error: IP SAN %x failed to parse" , data )
}
if err := c . checkNameConstraints ( & comparisonCount , maxConstraintComparisons , "IP address" , ip . String ( ) , ip ,
2023-11-09 08:34:50 +08:00
func ( parsedName , constraint any ) ( bool , error ) {
2022-04-11 15:55:23 +08:00
return matchIPConstraint ( parsedName . ( net . IP ) , constraint . ( * net . IPNet ) )
} , c . PermittedIPRanges , c . ExcludedIPRanges ) ; err != nil {
return err
}
default :
// Unknown SAN types are ignored.
2022-01-21 11:24:10 +08:00
}
2022-04-11 15:55:23 +08:00
return nil
} )
2022-01-21 11:24:10 +08:00
2022-04-11 15:55:23 +08:00
if err != nil {
return err
2022-01-21 11:24:10 +08:00
}
}
}
// KeyUsage status flags are ignored. From Engineering Security, Peter
// Gutmann: A European government CA marked its signing certificates as
// being valid for encryption only, but no-one noticed. Another
// European CA marked its signature keys as not being valid for
// signatures. A different CA marked its own trusted root certificate
// as being invalid for certificate signing. Another national CA
// distributed a certificate to be used to encrypt data for the
// country’ s tax authority that was marked as only being usable for
// digital signatures but not for encryption. Yet another CA reversed
// the order of the bit flags in the keyUsage due to confusion over
// encoding endianness, essentially setting a random keyUsage in
// certificates that it issued. Another CA created a self-invalidating
// certificate by adding a certificate policy statement stipulating
// that the certificate had to be used strictly as specified in the
// keyUsage, and a keyUsage containing a flag indicating that the RSA
// encryption key could only be used for Diffie-Hellman key agreement.
if certType == intermediateCertificate && ( ! c . BasicConstraintsValid || ! c . IsCA ) {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : NotAuthorizedToSign , Detail : "" }
2022-01-21 11:24:10 +08:00
}
if c . BasicConstraintsValid && c . MaxPathLen >= 0 {
numIntermediates := len ( currentChain ) - 1
if numIntermediates > c . MaxPathLen {
2022-01-28 13:13:26 +08:00
return CertificateInvalidError { Cert : c . asX509 ( ) , Reason : TooManyIntermediates , Detail : "" }
2022-01-21 11:24:10 +08:00
}
}
return nil
}
// Verify attempts to verify c by building one or more chains from c to a
// certificate in opts.Roots, using certificates in opts.Intermediates if
// needed. If successful, it returns one or more chains where the first
// element of the chain is c and the last element is from opts.Roots.
//
// If opts.Roots is nil, the platform verifier might be used, and
// verification details might differ from what is described below. If system
// roots are unavailable the returned error will be of type SystemRootsError.
//
// Name constraints in the intermediates will be applied to all names claimed
// in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim
// example.com if an intermediate doesn't permit it, even if example.com is not
// the name being validated. Note that DirectoryName constraints are not
// supported.
//
2022-02-08 16:30:28 +08:00
// Name constraint validation follows the rules from RFC 5280, with the
// addition that DNS name constraints may use the leading period format
// defined for emails and URIs. When a constraint has a leading period
// it indicates that at least one additional label must be prepended to
// the constrained name to be considered valid.
//
2022-01-21 11:24:10 +08:00
// Extended Key Usage values are enforced nested down a chain, so an intermediate
// or root that enumerates EKUs prevents a leaf from asserting an EKU not in that
// list. (While this is not specified, it is common practice in order to limit
// the types of certificates a CA can issue.)
//
2022-11-02 16:42:50 +08:00
// Certificates other than c in the returned chains should not be modified.
//
2022-01-21 11:24:10 +08:00
// WARNING: this function doesn't do any revocation checking.
func ( c * Certificate ) Verify ( opts VerifyOptions ) ( chains [ ] [ ] * Certificate , err error ) {
// Platform-specific verification needs the ASN.1 contents so
// this makes the behavior consistent across platforms.
if len ( c . Raw ) == 0 {
return nil , errNotParsed
}
for i := 0 ; i < opts . Intermediates . len ( ) ; i ++ {
2024-03-06 08:35:14 +08:00
c , _ , err := opts . Intermediates . cert ( i )
2022-01-21 11:24:10 +08:00
if err != nil {
2022-11-21 09:31:30 +08:00
return nil , fmt . Errorf ( "x509: error fetching intermediate: %w" , err )
2022-01-21 11:24:10 +08:00
}
if len ( c . Raw ) == 0 {
return nil , errNotParsed
}
}
2022-02-08 16:30:28 +08:00
// Use platform verifiers, where available, if Roots is from SystemCertPool.
if runtime . GOOS == "windows" {
2024-03-06 13:02:56 +08:00
// Don't use the system verifier if the system pool was replaced with a non-system pool,
// i.e. if SetFallbackRoots was called with x509usefallbackroots=1.
systemPool := systemRootsPool ( )
if opts . Roots == nil && ( systemPool == nil || systemPool . systemPool ) {
2022-01-21 11:24:10 +08:00
return c . systemVerify ( & opts )
}
if opts . Roots != nil && opts . Roots . systemPool {
platformChains , err := c . systemVerify ( & opts )
// If the platform verifier succeeded, or there are no additional
// roots, return the platform verifier result. Otherwise, continue
// with the Go verifier.
if err == nil || opts . Roots . len ( ) == 0 {
return platformChains , err
}
}
}
if opts . Roots == nil {
opts . Roots = systemRootsPool ( )
if opts . Roots == nil {
return nil , x509 . SystemRootsError { Err : systemRootsErr }
}
}
err = c . isValid ( leafCertificate , nil , & opts )
if err != nil {
return
}
2022-11-02 16:42:50 +08:00
if len ( opts . DNSName ) > 0 {
err = c . VerifyHostname ( opts . DNSName )
if err != nil {
return
}
}
2022-05-09 14:35:51 +08:00
var candidateChains [ ] [ ] * Certificate
if opts . Roots . contains ( c ) {
candidateChains = [ ] [ ] * Certificate { { c } }
} else {
candidateChains , err = c . buildChains ( [ ] * Certificate { c } , nil , & opts )
2022-01-21 11:24:10 +08:00
if err != nil {
2022-05-09 14:35:51 +08:00
return nil , err
2022-01-21 11:24:10 +08:00
}
}
2022-05-09 14:35:51 +08:00
if len ( opts . KeyUsages ) == 0 {
opts . KeyUsages = [ ] ExtKeyUsage { ExtKeyUsageServerAuth }
}
for _ , eku := range opts . KeyUsages {
if eku == ExtKeyUsageAny {
// If any key usage is acceptable, no need to check the chain for
// key usages.
return candidateChains , nil
}
2022-01-21 11:24:10 +08:00
}
2022-05-09 14:35:51 +08:00
chains = make ( [ ] [ ] * Certificate , 0 , len ( candidateChains ) )
for _ , candidate := range candidateChains {
if checkChainForKeyUsage ( candidate , opts . KeyUsages ) {
chains = append ( chains , candidate )
}
}
if len ( chains ) == 0 {
2022-11-02 16:42:50 +08:00
return nil , CertificateInvalidError { Cert : c . asX509 ( ) , Reason : IncompatibleUsage , Detail : "" }
2022-05-09 14:35:51 +08:00
}
return chains , nil
2022-01-21 11:24:10 +08:00
}
func appendToFreshChain ( chain [ ] * Certificate , cert * Certificate ) [ ] * Certificate {
n := make ( [ ] * Certificate , len ( chain ) + 1 )
copy ( n , chain )
n [ len ( chain ) ] = cert
return n
}
2022-05-09 14:47:23 +08:00
// alreadyInChain checks whether a candidate certificate is present in a chain.
// Rather than doing a direct byte for byte equivalency check, we check if the
// subject, public key, and SAN, if present, are equal. This prevents loops that
// are created by mutual cross-signatures, or other cross-signature bridge
// oddities.
func alreadyInChain ( candidate * Certificate , chain [ ] * Certificate ) bool {
type pubKeyEqual interface {
Equal ( crypto . PublicKey ) bool
}
var candidateSAN * pkix . Extension
for _ , ext := range candidate . Extensions {
if ext . Id . Equal ( oidExtensionSubjectAltName ) {
candidateSAN = & ext
break
}
}
for _ , cert := range chain {
if ! bytes . Equal ( candidate . RawSubject , cert . RawSubject ) {
continue
}
if ! candidate . PublicKey . ( pubKeyEqual ) . Equal ( cert . PublicKey ) {
continue
}
var certSAN * pkix . Extension
for _ , ext := range cert . Extensions {
if ext . Id . Equal ( oidExtensionSubjectAltName ) {
certSAN = & ext
break
}
}
if candidateSAN == nil && certSAN == nil {
return true
} else if candidateSAN == nil || certSAN == nil {
return false
}
if bytes . Equal ( candidateSAN . Value , certSAN . Value ) {
return true
}
}
return false
}
2022-01-21 11:24:10 +08:00
// maxChainSignatureChecks is the maximum number of CheckSignatureFrom calls
2022-02-08 16:30:28 +08:00
// that an invocation of buildChains will (transitively) make. Most chains are
2022-01-21 11:24:10 +08:00
// less than 15 certificates long, so this leaves space for multiple chains and
// for failed checks due to different intermediates having the same Subject.
const maxChainSignatureChecks = 100
2022-04-11 15:55:23 +08:00
func ( c * Certificate ) buildChains ( currentChain [ ] * Certificate , sigChecks * int , opts * VerifyOptions ) ( chains [ ] [ ] * Certificate , err error ) {
2022-01-21 11:24:10 +08:00
var (
hintErr error
hintCert * Certificate
)
2024-03-06 08:35:14 +08:00
considerCandidate := func ( certType int , candidate potentialParent ) {
2025-01-17 08:30:27 +08:00
if candidate . cert . PublicKey == nil || alreadyInChain ( candidate . cert , currentChain ) {
2022-05-09 14:47:23 +08:00
return
2022-01-21 11:24:10 +08:00
}
if sigChecks == nil {
sigChecks = new ( int )
}
* sigChecks ++
if * sigChecks > maxChainSignatureChecks {
err = errors . New ( "x509: signature check attempts limit reached while verifying certificate chain" )
return
}
2024-03-06 08:35:14 +08:00
if err := c . CheckSignatureFrom ( candidate . cert ) ; err != nil {
2022-01-21 11:24:10 +08:00
if hintErr == nil {
hintErr = err
2024-03-06 08:35:14 +08:00
hintCert = candidate . cert
2022-01-21 11:24:10 +08:00
}
return
}
2024-03-06 08:35:14 +08:00
err = candidate . cert . isValid ( certType , currentChain , opts )
2022-01-21 11:24:10 +08:00
if err != nil {
2022-12-02 08:47:39 +08:00
if hintErr == nil {
hintErr = err
2024-03-06 08:35:14 +08:00
hintCert = candidate . cert
2022-12-02 08:47:39 +08:00
}
2022-01-21 11:24:10 +08:00
return
}
2024-03-06 08:35:14 +08:00
if candidate . constraint != nil {
if err := candidate . constraint ( currentChain ) ; err != nil {
if hintErr == nil {
hintErr = err
hintCert = candidate . cert
}
return
}
}
2025-01-17 08:30:27 +08:00
2022-01-21 11:24:10 +08:00
switch certType {
case rootCertificate :
2024-03-06 08:35:14 +08:00
chains = append ( chains , appendToFreshChain ( currentChain , candidate . cert ) )
2022-01-21 11:24:10 +08:00
case intermediateCertificate :
2022-04-11 15:55:23 +08:00
var childChains [ ] [ ] * Certificate
2024-03-06 08:35:14 +08:00
childChains , err = candidate . cert . buildChains ( appendToFreshChain ( currentChain , candidate . cert ) , sigChecks , opts )
2022-01-21 11:24:10 +08:00
chains = append ( chains , childChains ... )
}
}
for _ , root := range opts . Roots . findPotentialParents ( c ) {
considerCandidate ( rootCertificate , root )
}
for _ , intermediate := range opts . Intermediates . findPotentialParents ( c ) {
considerCandidate ( intermediateCertificate , intermediate )
}
if len ( chains ) > 0 {
err = nil
}
if len ( chains ) == 0 && err == nil {
err = UnknownAuthorityError { c , hintErr , hintCert }
}
return
}
func validHostnamePattern ( host string ) bool { return validHostname ( host , true ) }
func validHostnameInput ( host string ) bool { return validHostname ( host , false ) }
// validHostname reports whether host is a valid hostname that can be matched or
// matched against according to RFC 6125 2.2, with some leniency to accommodate
// legacy values.
func validHostname ( host string , isPattern bool ) bool {
if ! isPattern {
host = strings . TrimSuffix ( host , "." )
}
if len ( host ) == 0 {
return false
}
2024-05-23 17:35:56 +08:00
if host == "*" {
// Bare wildcards are not allowed, they are not valid DNS names,
// nor are they allowed per RFC 6125.
return false
}
2022-01-21 11:24:10 +08:00
for i , part := range strings . Split ( host , "." ) {
if part == "" {
// Empty label.
return false
}
if isPattern && i == 0 && part == "*" {
// Only allow full left-most wildcards, as those are the only ones
// we match, and matching literal '*' characters is probably never
// the expected behavior.
continue
}
for j , c := range part {
if 'a' <= c && c <= 'z' {
continue
}
if '0' <= c && c <= '9' {
continue
}
if 'A' <= c && c <= 'Z' {
continue
}
if c == '-' && j != 0 {
continue
}
if c == '_' {
2022-02-08 16:30:28 +08:00
// Not a valid character in hostnames, but commonly
2022-01-21 11:24:10 +08:00
// found in deployments outside the WebPKI.
continue
}
return false
}
}
return true
}
func matchExactly ( hostA , hostB string ) bool {
if hostA == "" || hostA == "." || hostB == "" || hostB == "." {
return false
}
return toLowerCaseASCII ( hostA ) == toLowerCaseASCII ( hostB )
}
func matchHostnames ( pattern , host string ) bool {
pattern = toLowerCaseASCII ( pattern )
host = toLowerCaseASCII ( strings . TrimSuffix ( host , "." ) )
if len ( pattern ) == 0 || len ( host ) == 0 {
return false
}
patternParts := strings . Split ( pattern , "." )
hostParts := strings . Split ( host , "." )
if len ( patternParts ) != len ( hostParts ) {
return false
}
for i , patternPart := range patternParts {
if i == 0 && patternPart == "*" {
continue
}
if patternPart != hostParts [ i ] {
return false
}
}
return true
}
// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
// an explicitly ASCII function to avoid any sharp corners resulting from
// performing Unicode operations on DNS labels.
func toLowerCaseASCII ( in string ) string {
// If the string is already lower-case then there's nothing to do.
isAlreadyLowerCase := true
for _ , c := range in {
if c == utf8 . RuneError {
// If we get a UTF-8 error then there might be
// upper-case ASCII bytes in the invalid sequence.
isAlreadyLowerCase = false
break
}
if 'A' <= c && c <= 'Z' {
isAlreadyLowerCase = false
break
}
}
if isAlreadyLowerCase {
return in
}
out := [ ] byte ( in )
for i , c := range out {
if 'A' <= c && c <= 'Z' {
out [ i ] += 'a' - 'A'
}
}
return string ( out )
}
// VerifyHostname returns nil if c is a valid certificate for the named host.
// Otherwise it returns an error describing the mismatch.
//
// IP addresses can be optionally enclosed in square brackets and are checked
// against the IPAddresses field. Other names are checked case insensitively
// against the DNSNames field. If the names are valid hostnames, the certificate
2023-04-24 09:43:37 +08:00
// fields can have a wildcard as the complete left-most label (e.g. *.example.com).
2022-01-21 11:24:10 +08:00
//
// Note that the legacy Common Name field is ignored.
func ( c * Certificate ) VerifyHostname ( h string ) error {
// IP addresses may be written in [ ].
candidateIP := h
if len ( h ) >= 3 && h [ 0 ] == '[' && h [ len ( h ) - 1 ] == ']' {
candidateIP = h [ 1 : len ( h ) - 1 ]
}
if ip := net . ParseIP ( candidateIP ) ; ip != nil {
// We only match IP addresses against IP SANs.
// See RFC 6125, Appendix B.2.
for _ , candidate := range c . IPAddresses {
if ip . Equal ( candidate ) {
return nil
}
}
2022-01-28 13:13:26 +08:00
return x509 . HostnameError { Certificate : c . asX509 ( ) , Host : candidateIP }
2022-01-21 11:24:10 +08:00
}
candidateName := toLowerCaseASCII ( h ) // Save allocations inside the loop.
validCandidateName := validHostnameInput ( candidateName )
for _ , match := range c . DNSNames {
// Ideally, we'd only match valid hostnames according to RFC 6125 like
// browsers (more or less) do, but in practice Go is used in a wider
// array of contexts and can't even assume DNS resolution. Instead,
// always allow perfect matches, and only apply wildcard and trailing
// dot processing to valid hostnames.
if validCandidateName && validHostnamePattern ( match ) {
if matchHostnames ( match , candidateName ) {
return nil
}
} else {
if matchExactly ( match , candidateName ) {
return nil
}
}
}
2022-02-08 16:30:28 +08:00
2022-01-28 13:13:26 +08:00
return x509 . HostnameError { Certificate : c . asX509 ( ) , Host : h }
2022-01-21 11:24:10 +08:00
}
func checkChainForKeyUsage ( chain [ ] * Certificate , keyUsages [ ] ExtKeyUsage ) bool {
usages := make ( [ ] ExtKeyUsage , len ( keyUsages ) )
copy ( usages , keyUsages )
if len ( chain ) == 0 {
return false
}
usagesRemaining := len ( usages )
// We walk down the list and cross out any usages that aren't supported
// by each certificate. If we cross out all the usages, then the chain
// is unacceptable.
NextCert :
for i := len ( chain ) - 1 ; i >= 0 ; i -- {
cert := chain [ i ]
if len ( cert . ExtKeyUsage ) == 0 && len ( cert . UnknownExtKeyUsage ) == 0 {
// The certificate doesn't have any extended key usage specified.
continue
}
for _ , usage := range cert . ExtKeyUsage {
if usage == ExtKeyUsageAny {
// The certificate is explicitly good for any usage.
continue NextCert
}
}
const invalidUsage ExtKeyUsage = - 1
NextRequestedUsage :
for i , requestedUsage := range usages {
if requestedUsage == invalidUsage {
continue
}
for _ , usage := range cert . ExtKeyUsage {
if requestedUsage == usage {
continue NextRequestedUsage
}
}
usages [ i ] = invalidUsage
usagesRemaining --
if usagesRemaining == 0 {
return false
}
}
}
return true
}