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  }