...

Source file src/golang.org/x/crypto/acme/autocert/renewal_test.go

Documentation: golang.org/x/crypto/acme/autocert

     1  // Copyright 2016 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 autocert
     6  
     7  import (
     8  	"context"
     9  	"crypto"
    10  	"crypto/ecdsa"
    11  	"testing"
    12  	"time"
    13  
    14  	"golang.org/x/crypto/acme"
    15  	"golang.org/x/crypto/acme/autocert/internal/acmetest"
    16  )
    17  
    18  func TestRenewalNext(t *testing.T) {
    19  	now := time.Now()
    20  	man := &Manager{
    21  		RenewBefore: 7 * 24 * time.Hour,
    22  		nowFunc:     func() time.Time { return now },
    23  	}
    24  	defer man.stopRenew()
    25  	tt := []struct {
    26  		expiry   time.Time
    27  		min, max time.Duration
    28  	}{
    29  		{now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
    30  		{now.Add(time.Hour), 0, 1},
    31  		{now, 0, 1},
    32  		{now.Add(-time.Hour), 0, 1},
    33  	}
    34  
    35  	dr := &domainRenewal{m: man}
    36  	for i, test := range tt {
    37  		next := dr.next(test.expiry)
    38  		if next < test.min || test.max < next {
    39  			t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
    40  		}
    41  	}
    42  }
    43  
    44  func TestRenewFromCache(t *testing.T) {
    45  	man := testManager(t)
    46  	man.RenewBefore = 24 * time.Hour
    47  
    48  	ca := acmetest.NewCAServer(t).Start()
    49  	ca.ResolveGetCertificate(exampleDomain, man.GetCertificate)
    50  
    51  	man.Client = &acme.Client{
    52  		DirectoryURL: ca.URL(),
    53  	}
    54  
    55  	// cache an almost expired cert
    56  	now := time.Now()
    57  	c := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
    58  	if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	// verify the renewal happened
    63  	defer func() {
    64  		// Stop the timers that read and execute testDidRenewLoop before restoring it.
    65  		// Otherwise the timer callback may race with the deferred write.
    66  		man.stopRenew()
    67  		testDidRenewLoop = func(next time.Duration, err error) {}
    68  	}()
    69  	renewed := make(chan bool, 1)
    70  	testDidRenewLoop = func(next time.Duration, err error) {
    71  		defer func() {
    72  			select {
    73  			case renewed <- true:
    74  			default:
    75  				// The renewal timer uses a random backoff. If the first renewal fails for
    76  				// some reason, we could end up with multiple calls here before the test
    77  				// stops the timer.
    78  			}
    79  		}()
    80  
    81  		if err != nil {
    82  			t.Errorf("testDidRenewLoop: %v", err)
    83  		}
    84  		// Next should be about 90 days:
    85  		// CaServer creates 90days expiry + account for man.RenewBefore.
    86  		// Previous expiration was within 1 min.
    87  		future := 88 * 24 * time.Hour
    88  		if next < future {
    89  			t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
    90  		}
    91  
    92  		// ensure the new cert is cached
    93  		after := time.Now().Add(future)
    94  		tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
    95  		if err != nil {
    96  			t.Errorf("man.cacheGet: %v", err)
    97  			return
    98  		}
    99  		if !tlscert.Leaf.NotAfter.After(after) {
   100  			t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
   101  		}
   102  
   103  		// verify the old cert is also replaced in memory
   104  		man.stateMu.Lock()
   105  		defer man.stateMu.Unlock()
   106  		s := man.state[exampleCertKey]
   107  		if s == nil {
   108  			t.Errorf("m.state[%q] is nil", exampleCertKey)
   109  			return
   110  		}
   111  		tlscert, err = s.tlscert()
   112  		if err != nil {
   113  			t.Errorf("s.tlscert: %v", err)
   114  			return
   115  		}
   116  		if !tlscert.Leaf.NotAfter.After(after) {
   117  			t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
   118  		}
   119  	}
   120  
   121  	// trigger renew
   122  	hello := clientHelloInfo(exampleDomain, algECDSA)
   123  	if _, err := man.GetCertificate(hello); err != nil {
   124  		t.Fatal(err)
   125  	}
   126  	<-renewed
   127  }
   128  
   129  func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
   130  	ca := acmetest.NewCAServer(t).Start()
   131  	man := testManager(t)
   132  	man.RenewBefore = 24 * time.Hour
   133  	man.Client = &acme.Client{
   134  		DirectoryURL: "invalid",
   135  	}
   136  
   137  	// cache a recently renewed cert with a different private key
   138  	now := time.Now()
   139  	newCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Hour*24*90))
   140  	if err := man.cachePut(context.Background(), exampleCertKey, newCert); err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	newLeaf, err := validCert(exampleCertKey, newCert.Certificate, newCert.PrivateKey.(crypto.Signer), now)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  
   148  	// set internal state to an almost expired cert
   149  	oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
   150  	if err != nil {
   151  		t.Fatal(err)
   152  	}
   153  	oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  	man.stateMu.Lock()
   158  	if man.state == nil {
   159  		man.state = make(map[certKey]*certState)
   160  	}
   161  	s := &certState{
   162  		key:  oldCert.PrivateKey.(crypto.Signer),
   163  		cert: oldCert.Certificate,
   164  		leaf: oldLeaf,
   165  	}
   166  	man.state[exampleCertKey] = s
   167  	man.stateMu.Unlock()
   168  
   169  	// verify the renewal accepted the newer cached cert
   170  	defer func() {
   171  		// Stop the timers that read and execute testDidRenewLoop before restoring it.
   172  		// Otherwise the timer callback may race with the deferred write.
   173  		man.stopRenew()
   174  		testDidRenewLoop = func(next time.Duration, err error) {}
   175  	}()
   176  	renewed := make(chan bool, 1)
   177  	testDidRenewLoop = func(next time.Duration, err error) {
   178  		defer func() {
   179  			select {
   180  			case renewed <- true:
   181  			default:
   182  				// The renewal timer uses a random backoff. If the first renewal fails for
   183  				// some reason, we could end up with multiple calls here before the test
   184  				// stops the timer.
   185  			}
   186  		}()
   187  
   188  		if err != nil {
   189  			t.Errorf("testDidRenewLoop: %v", err)
   190  		}
   191  		// Next should be about 90 days
   192  		// Previous expiration was within 1 min.
   193  		future := 88 * 24 * time.Hour
   194  		if next < future {
   195  			t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
   196  		}
   197  
   198  		// ensure the cached cert was not modified
   199  		tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
   200  		if err != nil {
   201  			t.Errorf("man.cacheGet: %v", err)
   202  			return
   203  		}
   204  		if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
   205  			t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
   206  		}
   207  
   208  		// verify the old cert is also replaced in memory
   209  		man.stateMu.Lock()
   210  		defer man.stateMu.Unlock()
   211  		s := man.state[exampleCertKey]
   212  		if s == nil {
   213  			t.Errorf("m.state[%q] is nil", exampleCertKey)
   214  			return
   215  		}
   216  		stateKey := s.key.Public().(*ecdsa.PublicKey)
   217  		if !stateKey.Equal(newLeaf.PublicKey) {
   218  			t.Error("state key was not updated from cache")
   219  			return
   220  		}
   221  		tlscert, err = s.tlscert()
   222  		if err != nil {
   223  			t.Errorf("s.tlscert: %v", err)
   224  			return
   225  		}
   226  		if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
   227  			t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
   228  		}
   229  	}
   230  
   231  	// assert the expiring cert is returned from state
   232  	hello := clientHelloInfo(exampleDomain, algECDSA)
   233  	tlscert, err := man.GetCertificate(hello)
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
   238  		t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
   239  	}
   240  
   241  	// trigger renew
   242  	man.startRenew(exampleCertKey, s.key, s.leaf.NotAfter)
   243  	<-renewed
   244  	func() {
   245  		man.renewalMu.Lock()
   246  		defer man.renewalMu.Unlock()
   247  
   248  		// verify the private key is replaced in the renewal state
   249  		r := man.renewal[exampleCertKey]
   250  		if r == nil {
   251  			t.Errorf("m.renewal[%q] is nil", exampleCertKey)
   252  			return
   253  		}
   254  		renewalKey := r.key.Public().(*ecdsa.PublicKey)
   255  		if !renewalKey.Equal(newLeaf.PublicKey) {
   256  			t.Error("renewal private key was not updated from cache")
   257  		}
   258  	}()
   259  
   260  	// assert the new cert is returned from state after renew
   261  	hello = clientHelloInfo(exampleDomain, algECDSA)
   262  	tlscert, err = man.GetCertificate(hello)
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	if !newLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
   267  		t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
   268  	}
   269  }
   270  

View as plain text