| // 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 { |
| macOS.CFArrayAppendValue(certs, sc) |
| } |
| } |
| } |
| |
| policies := macOS.CFArrayCreateMutable() |
| defer macOS.ReleaseCFArray(policies) |
| sslPolicy, err := macOS.SecPolicyCreateSSL(opts.DNSName) |
| if err != nil { |
| return nil, err |
| } |
| 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, err := macOS.SecTrustGetCertificateAtIndex(trustObj, i) |
| if err != nil { |
| return nil, err |
| } |
| 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 |
| } |