golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/cmd/macos-roots-test/main.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build darwin 6 7 // Command macOS-roots-test runs crypto/x509.TestSystemRoots as a 8 // stand-alone binary for crowdsourced testing. 9 package main 10 11 import ( 12 "crypto/x509" 13 "fmt" 14 "log" 15 "os" 16 "os/exec" 17 "time" 18 "unsafe" 19 ) 20 21 type CertPool struct { 22 bySubjectKeyId map[string][]int 23 byName map[string][]int 24 certs []*x509.Certificate 25 } 26 27 func (s *CertPool) contains(cert *x509.Certificate) bool { 28 if s == nil { 29 return false 30 } 31 32 candidates := s.byName[string(cert.RawSubject)] 33 for _, c := range candidates { 34 if s.certs[c].Equal(cert) { 35 return true 36 } 37 } 38 39 return false 40 } 41 42 func main() { 43 var failed bool 44 45 t0 := time.Now() 46 sysRootsExt, err := loadSystemRoots() // actual system roots 47 sysRootsDuration := time.Since(t0) 48 49 if err != nil { 50 log.Fatalf("failed to read system roots (cgo): %v", err) 51 } 52 sysRoots := (*CertPool)(unsafe.Pointer(sysRootsExt)) 53 54 t1 := time.Now() 55 execRootsExt, err := execSecurityRoots() // non-cgo roots 56 execSysRootsDuration := time.Since(t1) 57 58 if err != nil { 59 log.Fatalf("failed to read system roots (nocgo): %v", err) 60 } 61 execRoots := (*CertPool)(unsafe.Pointer(execRootsExt)) 62 63 fmt.Printf(" cgo sys roots: %v\n", sysRootsDuration) 64 fmt.Printf("non-cgo sys roots: %v\n", execSysRootsDuration) 65 66 // On Mavericks, there are 212 bundled certs, at least there was at 67 // one point in time on one machine. (Maybe it was a corp laptop 68 // with extra certs?) Other OS X users report 135, 142, 145... 69 // Let's try requiring at least 100, since this is just a sanity 70 // check. 71 if want, have := 100, len(sysRoots.certs); have < want { 72 failed = true 73 fmt.Printf("want at least %d system roots, have %d\n", want, have) 74 } 75 76 // Check that the two cert pools are the same. 77 sysPool := make(map[string]*x509.Certificate, len(sysRoots.certs)) 78 for _, c := range sysRoots.certs { 79 sysPool[string(c.Raw)] = c 80 } 81 for _, c := range execRoots.certs { 82 if _, ok := sysPool[string(c.Raw)]; ok { 83 delete(sysPool, string(c.Raw)) 84 } else { 85 // verify-cert lets in certificates that are not trusted roots, but are 86 // signed by trusted roots. This should not be a problem, so confirm that's 87 // the case and skip them. 88 if _, err := c.Verify(x509.VerifyOptions{ 89 Roots: sysRootsExt, 90 Intermediates: execRootsExt, // the intermediates for EAP certs are stored in the keychain 91 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, 92 }); err != nil { 93 failed = true 94 fmt.Printf("certificate only present in non-cgo pool: %v (verify error: %v)\n", c.Subject, err) 95 } else { 96 fmt.Printf("signed certificate only present in non-cgo pool (acceptable): %v\n", c.Subject) 97 } 98 } 99 } 100 for _, c := range sysPool { 101 failed = true 102 fmt.Printf("certificate only present in cgo pool: %v\n", c.Subject) 103 } 104 105 if failed && debugDarwinRoots { 106 cmd := exec.Command("security", "dump-trust-settings") 107 cmd.Stdout = os.Stdout 108 cmd.Stderr = os.Stderr 109 cmd.Run() 110 cmd = exec.Command("security", "dump-trust-settings", "-d") 111 cmd.Stdout = os.Stdout 112 cmd.Stderr = os.Stderr 113 cmd.Run() 114 } 115 116 if failed { 117 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") 118 } else { 119 fmt.Printf("\n\nThe test passed, no need to report the output. Thank you.\n") 120 } 121 }