// Copyright 2018 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. // +build darwin // Command macOS-roots-test runs crypto/x509.TestSystemRoots as a // stand-alone binary for crowdsourced testing. package main import ( "crypto/x509" "fmt" "log" "os" "os/exec" "time" "unsafe" ) type CertPool struct { bySubjectKeyId map[string][]int byName map[string][]int certs []*x509.Certificate } func (s *CertPool) contains(cert *x509.Certificate) bool { if s == nil { return false } candidates := s.byName[string(cert.RawSubject)] for _, c := range candidates { if s.certs[c].Equal(cert) { return true } } return false } func main() { var failed bool t0 := time.Now() sysRootsExt, err := loadSystemRoots() // actual system roots sysRootsDuration := time.Since(t0) if err != nil { log.Fatalf("failed to read system roots (cgo): %v", err) } sysRoots := (*CertPool)(unsafe.Pointer(sysRootsExt)) t1 := time.Now() execRootsExt, err := execSecurityRoots() // non-cgo roots execSysRootsDuration := time.Since(t1) if err != nil { log.Fatalf("failed to read system roots (nocgo): %v", err) } execRoots := (*CertPool)(unsafe.Pointer(execRootsExt)) fmt.Printf(" cgo sys roots: %v\n", sysRootsDuration) fmt.Printf("non-cgo sys roots: %v\n", execSysRootsDuration) // On Mavericks, there are 212 bundled certs, at least there was at // one point in time on one machine. (Maybe it was a corp laptop // with extra certs?) Other OS X users report 135, 142, 145... // Let's try requiring at least 100, since this is just a sanity // check. if want, have := 100, len(sysRoots.certs); have < want { failed = true fmt.Printf("want at least %d system roots, have %d\n", want, have) } // Check that the two cert pools are the same. sysPool := make(map[string]*x509.Certificate, len(sysRoots.certs)) for _, c := range sysRoots.certs { sysPool[string(c.Raw)] = c } for _, c := range execRoots.certs { if _, ok := sysPool[string(c.Raw)]; ok { delete(sysPool, string(c.Raw)) } else { // verify-cert lets in certificates that are not trusted roots, but are // signed by trusted roots. This should not be a problem, so confirm that's // the case and skip them. if _, err := c.Verify(x509.VerifyOptions{ Roots: sysRootsExt, Intermediates: execRootsExt, // the intermediates for EAP certs are stored in the keychain KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, }); err != nil { failed = true fmt.Printf("certificate only present in non-cgo pool: %v (verify error: %v)\n", c.Subject, err) } else { fmt.Printf("signed certificate only present in non-cgo pool (acceptable): %v\n", c.Subject) } } } for _, c := range sysPool { failed = true fmt.Printf("certificate only present in cgo pool: %v\n", c.Subject) } if failed && debugDarwinRoots { cmd := exec.Command("security", "dump-trust-settings") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Run() cmd = exec.Command("security", "dump-trust-settings", "-d") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Run() } if failed { fmt.Printf("\n\n!!! The test failed!\n\nPlease report *the whole output* at https://github.com/golang/go/issues/24652 wrapping it in ``` a code block ```\nThank you!\n") } else { fmt.Printf("\n\nThe test passed, no need to report the output. Thank you.\n") } }