// Copyright 2011 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package x509import ()typeInvalidReasonintconst (// NotAuthorizedToSign results when a certificate is signed by another // which isn't marked as a CA certificate.NotAuthorizedToSignInvalidReason = iota// Expired results when a certificate has expired, based on the time // given in the VerifyOptions.Expired// CANotAuthorizedForThisName results when an intermediate or root // certificate has a name constraint which doesn't permit a DNS or // other name (including IP address) in the leaf certificate.CANotAuthorizedForThisName// TooManyIntermediates results when a path length constraint is // violated.TooManyIntermediates// IncompatibleUsage results when the certificate's key usage indicates // that it may only be used for a different purpose.IncompatibleUsage// NameMismatch results when the subject name of a parent certificate // does not match the issuer name in the child.NameMismatch// NameConstraintsWithoutSANs is a legacy error and is no longer returned.NameConstraintsWithoutSANs// UnconstrainedName results when a CA certificate contains permitted // name constraints, but leaf certificate contains a name of an // unsupported or unconstrained type.UnconstrainedName// TooManyConstraints results when the number of comparison operations // needed to check a certificate exceeds the limit set by // VerifyOptions.MaxConstraintComparisions. This limit exists to // prevent pathological certificates can consuming excessive amounts of // CPU time to verify.TooManyConstraints// CANotAuthorizedForExtKeyUsage results when an intermediate or root // certificate does not permit a requested extended key usage.CANotAuthorizedForExtKeyUsage// NoValidChains results when there are no valid chains to return.NoValidChains)// CertificateInvalidError results when an odd error occurs. Users of this// library probably want to handle all these errors uniformly.typeCertificateInvalidErrorstruct { Cert *Certificate Reason InvalidReason Detail string}func ( CertificateInvalidError) () string {switch .Reason {caseNotAuthorizedToSign:return"x509: certificate is not authorized to sign other certificates"caseExpired:return"x509: certificate has expired or is not yet valid: " + .DetailcaseCANotAuthorizedForThisName:return"x509: a root or intermediate certificate is not authorized to sign for this name: " + .DetailcaseCANotAuthorizedForExtKeyUsage:return"x509: a root or intermediate certificate is not authorized for an extended key usage: " + .DetailcaseTooManyIntermediates:return"x509: too many intermediates for path length constraint"caseIncompatibleUsage:return"x509: certificate specifies an incompatible key usage"caseNameMismatch:return"x509: issuer name does not match subject from issuing certificate"caseNameConstraintsWithoutSANs:return"x509: issuer has name constraints but leaf doesn't have a SAN extension"caseUnconstrainedName:return"x509: issuer has name constraints but leaf contains unknown or unconstrained name: " + .DetailcaseNoValidChains: := "x509: no valid chains built"if .Detail != "" { = fmt.Sprintf("%s: %s", , .Detail) }return }return"x509: unknown error"}// HostnameError results when the set of authorized names doesn't match the// requested name.typeHostnameErrorstruct { Certificate *Certificate Host string}func ( HostnameError) () string { := .Certificateif !.hasSANExtension() && matchHostnames(.Subject.CommonName, .Host) {return"x509: certificate relies on legacy Common Name field, use SANs instead" }varstringif := net.ParseIP(.Host); != nil {// Trying to validate an IPiflen(.IPAddresses) == 0 {return"x509: cannot validate certificate for " + .Host + " because it doesn't contain any IP SANs" }for , := range .IPAddresses {iflen() > 0 { += ", " } += .String() } } else { = strings.Join(.DNSNames, ", ") }iflen() == 0 {return"x509: certificate is not valid for any names, but wanted to match " + .Host }return"x509: certificate is valid for " + + ", not " + .Host}// UnknownAuthorityError results when the certificate issuer is unknowntypeUnknownAuthorityErrorstruct { 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 ( UnknownAuthorityError) () string { := "x509: certificate signed by unknown authority"if .hintErr != nil { := .hintCert.Subject.CommonNameiflen() == 0 {iflen(.hintCert.Subject.Organization) > 0 { = .hintCert.Subject.Organization[0] } else { = "serial:" + .hintCert.SerialNumber.String() } } += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", .hintErr, ) }return}// SystemRootsError results when we fail to load the system root certificates.typeSystemRootsErrorstruct { Err error}func ( SystemRootsError) () string { := "x509: failed to load system roots and no roots provided"if .Err != nil {return + "; " + .Err.Error() }return}func ( SystemRootsError) () error { return .Err }// 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.typeVerifyOptionsstruct {// 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// CertificatePolicies specifies which certificate policy OIDs are // acceptable during policy validation. An empty CertificatePolices // field implies any valid policy is acceptable. CertificatePolicies []OID// The following policy fields are unexported, because we do not expect // users to actually need to use them, but are useful for testing the // policy validation code.// inhibitPolicyMapping indicates if policy mapping should be allowed // during path validation. inhibitPolicyMapping bool// requireExplicitPolicy indidicates if explicit policies must be present // for each certificate being validated. requireExplicitPolicy bool// inhibitAnyPolicy indicates if the anyPolicy policy should be // processed if present in a certificate being validated. inhibitAnyPolicy bool}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( string) ( rfc2821Mailbox, bool) {iflen() == 0 {return , false } := make([]byte, 0, len()/2)if [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.) = [1:] :for {iflen() == 0 {return , false } := [0] = [1:]switch {case == '"':breakcase == '\\':// quoted-pairiflen() == 0 {return , false }if [0] == 11 || [0] == 12 || (1 <= [0] && [0] <= 9) || (14 <= [0] && [0] <= 127) { = append(, [0]) = [1:] } else {return , false }case == 11 || == 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. == 32 || == 33 || == 127 || (1 <= && <= 8) || (14 <= && <= 31) || (35 <= && <= 91) || (93 <= && <= 126):// qtext = append(, )default:return , false } } } else {// Atom ("." Atom)* :forlen() > 0 {// atext from RFC 2822, Section 3.2.4 := [0]switch {case == '\\':// 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. = [1:]iflen() == 0 {return , false }fallthroughcase ('0' <= && <= '9') || ('a' <= && <= 'z') || ('A' <= && <= 'Z') || == '!' || == '#' || == '$' || == '%' || == '&' || == '\'' || == '*' || == '+' || == '-' || == '/' || == '=' || == '?' || == '^' || == '_' || == '`' || == '{' || == '|' || == '}' || == '~' || == '.': = append(, [0]) = [1:]default:break } }iflen() == 0 {return , 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.” := []byte{'.', '.'}if [0] == '.' || [len()-1] == '.' ||bytes.Contains(, ) {return , false } }iflen() == 0 || [0] != '@' {return , false } = [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 , := domainToReverseLabels(); ! {return , false } .local = string() .domain = return , true}// 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( string) ( []string, bool) {forlen() > 0 {if := strings.LastIndexByte(, '.'); == -1 { = append(, ) = "" } else { = append(, [+1:]) = [:]if == 0 { // domain == ""// domain is prefixed with an empty label, append an empty // string to reverseLabels to indicate this. = append(, "") } } }iflen() > 0 && len([0]) == 0 {// An empty label at the end indicates an absolute value.returnnil, false }for , := range {iflen() == 0 {// Empty labels are otherwise invalid.returnnil, false }for , := range {if < 33 || > 126 {// Invalid character.returnnil, false } } }return , true}func matchEmailConstraint( rfc2821Mailbox, string) (bool, error) {// If the constraint contains an @, then it specifies an exact mailbox // name.ifstrings.Contains(, "@") { , := parseRFC2821Mailbox()if ! {returnfalse, fmt.Errorf("x509: internal error: cannot parse constraint %q", ) }return .local == .local && strings.EqualFold(.domain, .domain), nil }// Otherwise the constraint is like a DNS constraint of the domain part // of the mailbox.returnmatchDomainConstraint(.domain, )}func matchURIConstraint( *url.URL, 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.” := .Hostiflen() == 0 {returnfalse, fmt.Errorf("URI with empty host (%q) cannot be matched against constraints", .String()) }ifstrings.Contains(, ":") && !strings.HasSuffix(, "]") {varerror , _, = net.SplitHostPort(.Host)if != nil {returnfalse, } }ifstrings.HasPrefix(, "[") && strings.HasSuffix(, "]") ||net.ParseIP() != nil {returnfalse, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", .String()) }returnmatchDomainConstraint(, )}func matchIPConstraint( net.IP, *net.IPNet) (bool, error) {iflen() != len(.IP) {returnfalse, nil }for := range {if := .Mask[]; []& != .IP[]& {returnfalse, nil } }returntrue, nil}func matchDomainConstraint(, string) (bool, error) {// The meaning of zero length constraints is not specified, but this // code follows NSS and accepts them as matching everything.iflen() == 0 {returntrue, nil } , := domainToReverseLabels()if ! {returnfalse, fmt.Errorf("x509: internal error: cannot parse domain %q", ) }// 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. := falseif [0] == '.' { = true = [1:] } , := domainToReverseLabels()if ! {returnfalse, fmt.Errorf("x509: internal error: cannot parse domain %q", ) }iflen() < len() || ( && len() == len()) {returnfalse, nil }for , := range {if !strings.EqualFold(, []) {returnfalse, nil } }returntrue, nil}// 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 ( *Certificate) ( *int,int,string,string,any,func(, any) ( bool, error), , any) error { := reflect.ValueOf() * += .Len()if * > {returnCertificateInvalidError{, TooManyConstraints, ""} }for := 0; < .Len(); ++ { := .Index().Interface() , := (, )if != nil {returnCertificateInvalidError{, CANotAuthorizedForThisName, .Error()} }if {returnCertificateInvalidError{, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is excluded by constraint %q", , , )} } } := reflect.ValueOf() * += .Len()if * > {returnCertificateInvalidError{, TooManyConstraints, ""} } := truefor := 0; < .Len(); ++ { := .Index().Interface()varerrorif , = (, ); != nil {returnCertificateInvalidError{, CANotAuthorizedForThisName, .Error()} }if {break } }if ! {returnCertificateInvalidError{, CANotAuthorizedForThisName, fmt.Sprintf("%s %q is not permitted by any constraint", , )} }returnnil}// isValid performs validity checks on c given that it is a candidate to append// to the chain in currentChain.func ( *Certificate) ( int, []*Certificate, *VerifyOptions) error {iflen(.UnhandledCriticalExtensions) > 0 {returnUnhandledCriticalExtension{} }iflen() > 0 { := [len()-1]if !bytes.Equal(.RawIssuer, .RawSubject) {returnCertificateInvalidError{, NameMismatch, ""} } } := .CurrentTimeif .IsZero() { = time.Now() }if .Before(.NotBefore) {returnCertificateInvalidError{Cert: ,Reason: Expired,Detail: fmt.Sprintf("current time %s is before %s", .Format(time.RFC3339), .NotBefore.Format(time.RFC3339)), } } elseif .After(.NotAfter) {returnCertificateInvalidError{Cert: ,Reason: Expired,Detail: fmt.Sprintf("current time %s is after %s", .Format(time.RFC3339), .NotAfter.Format(time.RFC3339)), } } := .MaxConstraintComparisionsif == 0 { = 250000 } := 0if == intermediateCertificate || == rootCertificate {iflen() == 0 {returnerrors.New("x509: internal error: empty chain when appending CA cert") } }if ( == intermediateCertificate || == rootCertificate) && .hasNameConstraints() { := []*Certificate{}for , := range {if .hasSANExtension() { = append(, ) } }for , := range { := forEachSAN(.getSANExtension(), func( int, []byte) error {switch {casenameTypeEmail: := string() , := parseRFC2821Mailbox()if ! {returnfmt.Errorf("x509: cannot parse rfc822Name %q", ) }if := .checkNameConstraints(&, , "email address", , ,func(, any) (bool, error) {returnmatchEmailConstraint(.(rfc2821Mailbox), .(string)) }, .PermittedEmailAddresses, .ExcludedEmailAddresses); != nil {return }casenameTypeDNS: := string()if , := domainToReverseLabels(); ! {returnfmt.Errorf("x509: cannot parse dnsName %q", ) }if := .checkNameConstraints(&, , "DNS name", , ,func(, any) (bool, error) {returnmatchDomainConstraint(.(string), .(string)) }, .PermittedDNSDomains, .ExcludedDNSDomains); != nil {return }casenameTypeURI: := string() , := url.Parse()if != nil {returnfmt.Errorf("x509: internal error: URI SAN %q failed to parse", ) }if := .checkNameConstraints(&, , "URI", , ,func(, any) (bool, error) {returnmatchURIConstraint(.(*url.URL), .(string)) }, .PermittedURIDomains, .ExcludedURIDomains); != nil {return }casenameTypeIP: := net.IP()if := len(); != net.IPv4len && != net.IPv6len {returnfmt.Errorf("x509: internal error: IP SAN %x failed to parse", ) }if := .checkNameConstraints(&, , "IP address", .String(), ,func(, any) (bool, error) {returnmatchIPConstraint(.(net.IP), .(*net.IPNet)) }, .PermittedIPRanges, .ExcludedIPRanges); != nil {return }default:// Unknown SAN types are ignored. }returnnil })if != nil {return } } }// 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 == intermediateCertificate && (!.BasicConstraintsValid || !.IsCA) {returnCertificateInvalidError{, NotAuthorizedToSign, ""} }if .BasicConstraintsValid && .MaxPathLen >= 0 { := len() - 1if > .MaxPathLen {returnCertificateInvalidError{, TooManyIntermediates, ""} } }returnnil}// 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.//// 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.//// 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.)//// Certificates that use SHA1WithRSA and ECDSAWithSHA1 signatures are not supported,// and will not be used to build chains.//// Certificates other than c in the returned chains should not be modified.//// WARNING: this function doesn't do any revocation checking.func ( *Certificate) ( VerifyOptions) ( [][]*Certificate, error) {// Platform-specific verification needs the ASN.1 contents so // this makes the behavior consistent across platforms.iflen(.Raw) == 0 {returnnil, errNotParsed }for := 0; < .Intermediates.len(); ++ { , , := .Intermediates.cert()if != nil {returnnil, fmt.Errorf("crypto/x509: error fetching intermediate: %w", ) }iflen(.Raw) == 0 {returnnil, errNotParsed } }// Use platform verifiers, where available, if Roots is from SystemCertPool.ifruntime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {// 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. := systemRootsPool()if .Roots == nil && ( == nil || .systemPool) {return .systemVerify(&) }if .Roots != nil && .Roots.systemPool { , := .systemVerify(&)// If the platform verifier succeeded, or there are no additional // roots, return the platform verifier result. Otherwise, continue // with the Go verifier.if == nil || .Roots.len() == 0 {return , } } }if .Roots == nil { .Roots = systemRootsPool()if .Roots == nil {returnnil, SystemRootsError{systemRootsErr} } } = .isValid(leafCertificate, nil, &)if != nil {return }iflen(.DNSName) > 0 { = .VerifyHostname(.DNSName)if != nil {return } }var [][]*Certificateif .Roots.contains() { = [][]*Certificate{{}} } else { , = .buildChains([]*Certificate{}, nil, &)if != nil {returnnil, } }iflen(.KeyUsages) == 0 { .KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} }for , := range .KeyUsages {if == ExtKeyUsageAny {// If any key usage is acceptable, no need to check the chain for // key usages.return , nil } } = make([][]*Certificate, 0, len())var , intfor , := range {if !checkChainForKeyUsage(, .KeyUsages) { ++continue }if !policiesValid(, ) { ++continue } = append(, ) }iflen() == 0 {var []stringif > 0 {if == 0 {returnnil, CertificateInvalidError{, IncompatibleUsage, ""} } = append(, fmt.Sprintf("%d chains with incompatible key usage", )) }if > 0 { = append(, fmt.Sprintf("%d chains with invalid policies", )) } = CertificateInvalidError{, NoValidChains, strings.Join(, ", ")}returnnil, }return , nil}func appendToFreshChain( []*Certificate, *Certificate) []*Certificate { := make([]*Certificate, len()+1)copy(, ) [len()] = return}// 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( *Certificate, []*Certificate) bool {typeinterface { (crypto.PublicKey) bool }var *pkix.Extensionfor , := range .Extensions {if .Id.Equal(oidExtensionSubjectAltName) { = &break } }for , := range {if !bytes.Equal(.RawSubject, .RawSubject) {continue }if !.PublicKey.().(.PublicKey) {continue }var *pkix.Extensionfor , := range .Extensions {if .Id.Equal(oidExtensionSubjectAltName) { = &break } }if == nil && == nil {returntrue } elseif == nil || == nil {returnfalse }ifbytes.Equal(.Value, .Value) {returntrue } }returnfalse}// maxChainSignatureChecks is the maximum number of CheckSignatureFrom calls// that an invocation of buildChains will (transitively) make. Most chains are// 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 = 100func ( *Certificate) ( []*Certificate, *int, *VerifyOptions) ( [][]*Certificate, error) {var (error *Certificate ) := func( int, potentialParent) {if .cert.PublicKey == nil || alreadyInChain(.cert, ) {return }if == nil { = new(int) } *++if * > maxChainSignatureChecks { = errors.New("x509: signature check attempts limit reached while verifying certificate chain")return }if := .CheckSignatureFrom(.cert); != nil {if == nil { = = .cert }return } = .cert.isValid(, , )if != nil {if == nil { = = .cert }return }if .constraint != nil {if := .constraint(); != nil {if == nil { = = .cert }return } }switch {caserootCertificate: = append(, appendToFreshChain(, .cert))caseintermediateCertificate:var [][]*Certificate , = .cert.(appendToFreshChain(, .cert), , ) = append(, ...) } }for , := range .Roots.findPotentialParents() { (rootCertificate, ) }for , := range .Intermediates.findPotentialParents() { (intermediateCertificate, ) }iflen() > 0 { = nil }iflen() == 0 && == nil { = UnknownAuthorityError{, , } }return}func validHostnamePattern( string) bool { returnvalidHostname(, true) }func validHostnameInput( string) bool { returnvalidHostname(, 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( string, bool) bool {if ! { = strings.TrimSuffix(, ".") }iflen() == 0 {returnfalse }if == "*" {// Bare wildcards are not allowed, they are not valid DNS names, // nor are they allowed per RFC 6125.returnfalse }for , := rangestrings.Split(, ".") {if == "" {// Empty label.returnfalse }if && == 0 && == "*" {// 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 , := range {if'a' <= && <= 'z' {continue }if'0' <= && <= '9' {continue }if'A' <= && <= 'Z' {continue }if == '-' && != 0 {continue }if == '_' {// Not a valid character in hostnames, but commonly // found in deployments outside the WebPKI.continue }returnfalse } }returntrue}func matchExactly(, string) bool {if == "" || == "." || == "" || == "." {returnfalse }returntoLowerCaseASCII() == toLowerCaseASCII()}func matchHostnames(, string) bool { = toLowerCaseASCII() = toLowerCaseASCII(strings.TrimSuffix(, "."))iflen() == 0 || len() == 0 {returnfalse } := strings.Split(, ".") := strings.Split(, ".")iflen() != len() {returnfalse }for , := range {if == 0 && == "*" {continue }if != [] {returnfalse } }returntrue}// 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( string) string {// If the string is already lower-case then there's nothing to do. := truefor , := range {if == utf8.RuneError {// If we get a UTF-8 error then there might be // upper-case ASCII bytes in the invalid sequence. = falsebreak }if'A' <= && <= 'Z' { = falsebreak } }if {return } := []byte()for , := range {if'A' <= && <= 'Z' { [] += 'a' - 'A' } }returnstring()}// 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// fields can have a wildcard as the complete left-most label (e.g. *.example.com).//// Note that the legacy Common Name field is ignored.func ( *Certificate) ( string) error {// IP addresses may be written in [ ]. := iflen() >= 3 && [0] == '[' && [len()-1] == ']' { = [1 : len()-1] }if := net.ParseIP(); != nil {// We only match IP addresses against IP SANs. // See RFC 6125, Appendix B.2.for , := range .IPAddresses {if .Equal() {returnnil } }returnHostnameError{, } } := toLowerCaseASCII() // Save allocations inside the loop. := validHostnameInput()for , := range .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 && validHostnamePattern() {ifmatchHostnames(, ) {returnnil } } else {ifmatchExactly(, ) {returnnil } } }returnHostnameError{, }}func checkChainForKeyUsage( []*Certificate, []ExtKeyUsage) bool { := make([]ExtKeyUsage, len())copy(, )iflen() == 0 {returnfalse } := len()// 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.:for := len() - 1; >= 0; -- { := []iflen(.ExtKeyUsage) == 0 && len(.UnknownExtKeyUsage) == 0 {// The certificate doesn't have any extended key usage specified.continue }for , := range .ExtKeyUsage {if == ExtKeyUsageAny {// The certificate is explicitly good for any usage.continue } }constExtKeyUsage = -1 :for , := range {if == {continue }for , := range .ExtKeyUsage {if == {continue } } [] = --if == 0 {returnfalse } } }returntrue}func mustNewOIDFromInts( []uint64) OID { , := OIDFromInts()if != nil {panic(fmt.Sprintf("OIDFromInts(%v) unexpected error: %v", , )) }return}type policyGraphNode struct { validPolicy OID expectedPolicySet []OID// we do not implement qualifiers, so we don't track qualifier_set parents map[*policyGraphNode]bool children map[*policyGraphNode]bool}func newPolicyGraphNode( OID, []*policyGraphNode) *policyGraphNode { := &policyGraphNode{validPolicy: ,expectedPolicySet: []OID{},children: map[*policyGraphNode]bool{},parents: map[*policyGraphNode]bool{}, }for , := range { .children[] = true .parents[] = true }return}type policyGraph struct { strata []map[string]*policyGraphNode// map of OID -> nodes at strata[depth-1] with OID in their expectedPolicySet parentIndex map[string][]*policyGraphNode depth int}var anyPolicyOID = mustNewOIDFromInts([]uint64{2, 5, 29, 32, 0})func newPolicyGraph() *policyGraph { := policyGraphNode{validPolicy: anyPolicyOID,expectedPolicySet: []OID{anyPolicyOID},children: map[*policyGraphNode]bool{},parents: map[*policyGraphNode]bool{}, }return &policyGraph{depth: 0,strata: []map[string]*policyGraphNode{{string(anyPolicyOID.der): &}}, }}func ( *policyGraph) ( *policyGraphNode) { .strata[.depth][string(.validPolicy.der)] = }func ( *policyGraph) ( OID) []*policyGraphNode {if .depth == 0 {returnnil }return .parentIndex[string(.der)]}func ( *policyGraph) () *policyGraphNode {if .depth == 0 {returnnil }return .strata[.depth-1][string(anyPolicyOID.der)]}func ( *policyGraph) () iter.Seq[*policyGraphNode] {if .depth == 0 {returnnil }returnmaps.Values(.strata[.depth-1])}func ( *policyGraph) () map[string]*policyGraphNode {return .strata[.depth]}func ( *policyGraph) ( OID) *policyGraphNode {return .strata[.depth][string(.der)]}func ( *policyGraph) ( OID) { := .strata[.depth][string(.der)]if == nil {return }for := range .parents {delete(.children, ) }for := range .children {delete(.parents, ) }delete(.strata[.depth], string(.der))}func ( *policyGraph) () []*policyGraphNode {var []*policyGraphNodefor := .depth; >= 0; -- {for , := range .strata[] {if .validPolicy.Equal(anyPolicyOID) {continue }iflen(.parents) == 1 {for := range .parents {if .validPolicy.Equal(anyPolicyOID) { = append(, ) } } } } }return}func ( *policyGraph) () {for := .depth - 1; > 0; -- {for , := range .strata[] {iflen(.children) == 0 {for := range .parents {delete(.children, ) }delete(.strata[], string(.validPolicy.der)) } } }}func ( *policyGraph) () { .parentIndex = map[string][]*policyGraphNode{}for , := range .strata[.depth] {for , := range .expectedPolicySet { .parentIndex[string(.der)] = append(.parentIndex[string(.der)], ) } } .depth++ .strata = append(.strata, map[string]*policyGraphNode{})}func policiesValid( []*Certificate, VerifyOptions) bool {// The following code implements the policy verification algorithm as // specified in RFC 5280 and updated by RFC 9618. In particular the // following sections are replaced by RFC 9618: // * 6.1.2 (a) // * 6.1.3 (d) // * 6.1.3 (e) // * 6.1.3 (f) // * 6.1.4 (b) // * 6.1.5 (g)iflen() == 1 {returntrue }// n is the length of the chain minus the trust anchor := len() - 1 := newPolicyGraph()var , , intif !.inhibitAnyPolicy { = + 1 }if !.requireExplicitPolicy { = + 1 }if !.inhibitPolicyMapping { = + 1 } := map[string]bool{}for , := range .CertificatePolicies { [string(.der)] = true }// If the user does not pass any policies, we consider // that equivalent to passing anyPolicyOID.iflen() == 0 { [string(anyPolicyOID.der)] = true }for := - 1; >= 0; -- { := [] := bytes.Equal(.RawIssuer, .RawSubject)// 6.1.3 (e) -- as updated by RFC 9618iflen(.Policies) == 0 { = nil }// 6.1.3 (f) -- as updated by RFC 9618if == 0 && == nil {returnfalse }if != nil { .incrDepth() := map[string]bool{}// 6.1.3 (d) (1) -- as updated by RFC 9618for , := range .Policies { [string(.der)] = trueif .Equal(anyPolicyOID) {continue }// 6.1.3 (d) (1) (i) -- as updated by RFC 9618 := .parentsWithExpected()iflen() == 0 {// 6.1.3 (d) (1) (ii) -- as updated by RFC 9618if := .parentWithAnyPolicy(); != nil { = []*policyGraphNode{} } }iflen() > 0 { .insert(newPolicyGraphNode(, )) } }// 6.1.3 (d) (2) -- as updated by RFC 9618 // NOTE: in the check "n-i < n" our i is different from the i in the specification. // In the specification chains go from the trust anchor to the leaf, whereas our // chains go from the leaf to the trust anchor, so our i's our inverted. Our // check here matches the check "i < n" in the specification.if [string(anyPolicyOID.der)] && ( > 0 || (- < && )) { := map[string][]*policyGraphNode{} := .leaves()for := range .parents() {for , := range .expectedPolicySet {if [string(.der)] == nil { [string(.der)] = append([string(.der)], ) } } }for , := range { .insert(newPolicyGraphNode(OID{der: []byte()}, )) } }// 6.1.3 (d) (3) -- as updated by RFC 9618 .prune()if != 0 {// 6.1.4 (b) -- as updated by RFC 9618iflen(.PolicyMappings) > 0 {// collect map of issuer -> []subject := map[string][]OID{}for , := range .PolicyMappings {if > 0 {if .IssuerDomainPolicy.Equal(anyPolicyOID) || .SubjectDomainPolicy.Equal(anyPolicyOID) {// Invalid mappingreturnfalse } [string(.IssuerDomainPolicy.der)] = append([string(.IssuerDomainPolicy.der)], .SubjectDomainPolicy) } else {// 6.1.4 (b) (3) (i) -- as updated by RFC 9618 .deleteLeaf(.IssuerDomainPolicy)// 6.1.4 (b) (3) (ii) -- as updated by RFC 9618 .prune() } }for , := range {// 6.1.4 (b) (1) -- as updated by RFC 9618if := .leafWithPolicy(OID{der: []byte()}); != nil { .expectedPolicySet = } elseif := .leafWithPolicy(anyPolicyOID); != nil {// 6.1.4 (b) (2) -- as updated by RFC 9618 := newPolicyGraphNode(OID{der: []byte()}, []*policyGraphNode{}) .expectedPolicySet = .insert() } } } } }if != 0 {// 6.1.4 (h)if ! {if > 0 { -- }if > 0 { -- }if > 0 { -- } }// 6.1.4 (i)if (.RequireExplicitPolicy > 0 || .RequireExplicitPolicyZero) && .RequireExplicitPolicy < { = .RequireExplicitPolicy }if (.InhibitPolicyMapping > 0 || .InhibitPolicyMappingZero) && .InhibitPolicyMapping < { = .InhibitPolicyMapping }// 6.1.4 (j)if (.InhibitAnyPolicy > 0 || .InhibitAnyPolicyZero) && .InhibitAnyPolicy < { = .InhibitAnyPolicy } } }// 6.1.5 (a)if > 0 { -- }// 6.1.5 (b)if [0].RequireExplicitPolicyZero { = 0 }// 6.1.5 (g) (1) -- as updated by RFC 9618var []*policyGraphNode// 6.1.5 (g) (2) -- as updated by RFC 9618if != nil { = .validPolicyNodes()// 6.1.5 (g) (3) -- as updated by RFC 9618if := .leafWithPolicy(anyPolicyOID); != nil { = append(, ) } }// 6.1.5 (g) (4) -- as updated by RFC 9618 := map[string]bool{}for , := range { [string(.validPolicy.der)] = true }// 6.1.5 (g) (5) -- as updated by RFC 9618 := maps.Clone()// 6.1.5 (g) (6) -- as updated by RFC 9618iflen() != 1 || ![string(anyPolicyOID.der)] {// 6.1.5 (g) (6) (i) -- as updated by RFC 9618for := range {if ![] {delete(, ) } }// 6.1.5 (g) (6) (ii) -- as updated by RFC 9618if [string(anyPolicyOID.der)] {for := range { [] = true } } }if == 0 && len() == 0 {returnfalse }returntrue}
The pages are generated with Goldsv0.7.3-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.