// Copyright 2020 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 x509 import ( macOS "crypto/x509/internal/macos" "errors" "fmt" ) func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { certs := macOS.CFArrayCreateMutable() defer macOS.ReleaseCFArray(certs) leaf, err := macOS.SecCertificateCreateWithData(c.Raw) if err != nil { return nil, errors.New("invalid leaf certificate") } macOS.CFArrayAppendValue(certs, leaf) if opts.Intermediates != nil { for _, lc := range opts.Intermediates.lazyCerts { c, err := lc.getCert() if err != nil { return nil, err } sc, err := macOS.SecCertificateCreateWithData(c.Raw) if err != nil { return nil, err } macOS.CFArrayAppendValue(certs, sc) } } policies := macOS.CFArrayCreateMutable() defer macOS.ReleaseCFArray(policies) sslPolicy := macOS.SecPolicyCreateSSL(opts.DNSName) macOS.CFArrayAppendValue(policies, sslPolicy) trustObj, err := macOS.SecTrustCreateWithCertificates(certs, policies) if err != nil { return nil, err } defer macOS.CFRelease(trustObj) if !opts.CurrentTime.IsZero() { dateRef := macOS.TimeToCFDateRef(opts.CurrentTime) defer macOS.CFRelease(dateRef) if err := macOS.SecTrustSetVerifyDate(trustObj, dateRef); err != nil { return nil, err } } // TODO(roland): we may want to allow passing in SCTs via VerifyOptions and // set them via SecTrustSetSignedCertificateTimestamps, since Apple will // always enforce its SCT requirements, and there are still _some_ people // using TLS or OCSP for that. if ret, err := macOS.SecTrustEvaluateWithError(trustObj); err != nil { switch ret { case macOS.ErrSecCertificateExpired: return nil, CertificateInvalidError{c, Expired, err.Error()} case macOS.ErrSecHostNameMismatch: return nil, HostnameError{c, opts.DNSName} case macOS.ErrSecNotTrusted: return nil, UnknownAuthorityError{Cert: c} default: return nil, fmt.Errorf("x509: %s", err) } } chain := [][]*Certificate{{}} numCerts := macOS.SecTrustGetCertificateCount(trustObj) for i := 0; i < numCerts; i++ { certRef := macOS.SecTrustGetCertificateAtIndex(trustObj, i) cert, err := exportCertificate(certRef) if err != nil { return nil, err } chain[0] = append(chain[0], cert) } if len(chain[0]) == 0 { // This should _never_ happen, but to be safe return nil, errors.New("x509: macOS certificate verification internal error") } if opts.DNSName != "" { // If we have a DNS name, apply our own name verification if err := chain[0][0].VerifyHostname(opts.DNSName); err != nil { return nil, err } } keyUsages := opts.KeyUsages if len(keyUsages) == 0 { keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} } // If any key usage is acceptable then we're done. for _, usage := range keyUsages { if usage == ExtKeyUsageAny { return chain, nil } } if !checkChainForKeyUsage(chain[0], keyUsages) { return nil, CertificateInvalidError{c, IncompatibleUsage, ""} } return chain, nil } // exportCertificate returns a *Certificate for a SecCertificateRef. func exportCertificate(cert macOS.CFRef) (*Certificate, error) { data, err := macOS.SecCertificateCopyData(cert) if err != nil { return nil, err } return ParseCertificate(data) } func loadSystemRoots() (*CertPool, error) { return &CertPool{systemPool: true}, nil }