summaryrefslogtreecommitdiffstats
path: root/src/crypto/x509/root_darwin.go
blob: 469e907a8e154ea946bbfe1c2785b03495810ea3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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, 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
}