Source file src/crypto/x509/hybrid_pool_test.go

     1  // Copyright 2011 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  package x509_test
     6  
     7  import (
     8  	"crypto/ecdsa"
     9  	"crypto/elliptic"
    10  	"crypto/rand"
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"crypto/x509/pkix"
    14  	"internal/testenv"
    15  	"math/big"
    16  	"runtime"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  func TestHybridPool(t *testing.T) {
    22  	t.Parallel()
    23  	if !(runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios") {
    24  		t.Skipf("platform verifier not available on %s", runtime.GOOS)
    25  	}
    26  	if !testenv.HasExternalNetwork() {
    27  		t.Skip()
    28  	}
    29  	if runtime.GOOS == "windows" {
    30  		// NOTE(#51599): on the Windows builders we sometimes see that the state
    31  		// of the root pool is not fully initialized, causing an expected
    32  		// platform verification to fail. In part this is because Windows
    33  		// dynamically populates roots into its local trust store at time of
    34  		// use. We can attempt to prime the pool by attempting TLS connections
    35  		// to google.com until it works, suggesting the pool has been properly
    36  		// updated. If after we hit the dealine, the pool has _still_ not been
    37  		// populated with the expected root, it's unlikely we are ever going to
    38  		// get into a good state, and so we just fail the test. #52108 suggests
    39  		// a better possible long term solution.
    40  
    41  		deadline := time.Now().Add(time.Second * 10)
    42  		nextSleep := 10 * time.Millisecond
    43  		for i := 0; ; i++ {
    44  			c, err := tls.Dial("tcp", "google.com:443", nil)
    45  			if err == nil {
    46  				c.Close()
    47  				break
    48  			}
    49  			nextSleep = nextSleep * time.Duration(i)
    50  			if time.Until(deadline) < nextSleep {
    51  				t.Fatal("windows root pool appears to be in an uninitialized state (missing root that chains to google.com)")
    52  			}
    53  			time.Sleep(nextSleep)
    54  		}
    55  	}
    56  
    57  	// Get the google.com chain, which should be valid on all platforms we
    58  	// are testing
    59  	c, err := tls.Dial("tcp", "google.com:443", &tls.Config{InsecureSkipVerify: true})
    60  	if err != nil {
    61  		t.Fatalf("tls connection failed: %s", err)
    62  	}
    63  	googChain := c.ConnectionState().PeerCertificates
    64  
    65  	rootTmpl := &x509.Certificate{
    66  		SerialNumber:          big.NewInt(1),
    67  		Subject:               pkix.Name{CommonName: "Go test root"},
    68  		IsCA:                  true,
    69  		BasicConstraintsValid: true,
    70  		NotBefore:             time.Now().Add(-time.Hour),
    71  		NotAfter:              time.Now().Add(time.Hour * 10),
    72  	}
    73  	k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    74  	if err != nil {
    75  		t.Fatalf("failed to generate test key: %s", err)
    76  	}
    77  	rootDER, err := x509.CreateCertificate(rand.Reader, rootTmpl, rootTmpl, k.Public(), k)
    78  	if err != nil {
    79  		t.Fatalf("failed to create test cert: %s", err)
    80  	}
    81  	root, err := x509.ParseCertificate(rootDER)
    82  	if err != nil {
    83  		t.Fatalf("failed to parse test cert: %s", err)
    84  	}
    85  
    86  	pool, err := x509.SystemCertPool()
    87  	if err != nil {
    88  		t.Fatalf("SystemCertPool failed: %s", err)
    89  	}
    90  	opts := x509.VerifyOptions{Roots: pool}
    91  
    92  	_, err = googChain[0].Verify(opts)
    93  	if err != nil {
    94  		t.Fatalf("verification failed for google.com chain (system only pool): %s", err)
    95  	}
    96  
    97  	pool.AddCert(root)
    98  
    99  	_, err = googChain[0].Verify(opts)
   100  	if err != nil {
   101  		t.Fatalf("verification failed for google.com chain (hybrid pool): %s", err)
   102  	}
   103  
   104  	certTmpl := &x509.Certificate{
   105  		SerialNumber: big.NewInt(1),
   106  		NotBefore:    time.Now().Add(-time.Hour),
   107  		NotAfter:     time.Now().Add(time.Hour * 10),
   108  		DNSNames:     []string{"example.com"},
   109  	}
   110  	certDER, err := x509.CreateCertificate(rand.Reader, certTmpl, rootTmpl, k.Public(), k)
   111  	if err != nil {
   112  		t.Fatalf("failed to create test cert: %s", err)
   113  	}
   114  	cert, err := x509.ParseCertificate(certDER)
   115  	if err != nil {
   116  		t.Fatalf("failed to parse test cert: %s", err)
   117  	}
   118  
   119  	_, err = cert.Verify(opts)
   120  	if err != nil {
   121  		t.Fatalf("verification failed for custom chain (hybrid pool): %s", err)
   122  	}
   123  }
   124  

View as plain text